Skip to content

Commit de87d03

Browse files
author
Nick Santos
committed
upgrade snappy. The new version of snappy has a lot of API changes and is async-only.
1 parent e40a7d2 commit de87d03

6 files changed

+99
-90
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
zcache
22
===
33

4-
[![Build Status](https://travis-ci.org/Medium/zcache.svg?branch=xiao-travis)](https://travis-ci.org/Medium/zcache)
4+
[![Build Status](https://travis-ci.org/Medium/zcache.svg)](https://travis-ci.org/Medium/zcache)

externs/snappy.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@
22

33
/**
44
* @param {string} value
5+
* @param {function(?, ?)} callback
56
* @return {Buffer}
67
*/
7-
function compressSync(value) {}
8+
function compress(value, callback) {}
89

910
/**
1011
* @param {Buffer} value
1112
* @param {function(Buffer): string} parser
13+
* @param {function(?, ?)} callback
1214
* @return {string}
1315
*/
14-
function decompressSync(value, parser) {}
16+
function decompress(value, parser, callback) {}
1517

1618
/**
1719
* @param {Buffer} value
@@ -27,7 +29,6 @@ var parsers = {
2729

2830
module.exports = {
2931
parsers: parsers,
30-
compressSync: compressSync,
31-
decompressSync: decompressSync
32+
compress: compress,
33+
decompress: decompress
3234
}
33-

lib/RedisConnection.js

+61-51
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ function RedisConnection(host, port, options) {
2828
this._bound_onConnect = this._onConnect.bind(this)
2929
this._bound_onError = this._onError.bind(this)
3030
this._bound_onEnd = this._onEnd.bind(this)
31-
32-
// Controls if we turn on compression or not.
31+
32+
// Controls if we turn on compression or not.
3333
// All cache values which are longer than the pivot are eligible for compression
3434
// Pivot and encoding prefix are hardcoded for now. Will revisit after
3535
// we know we are using snappy for sure
@@ -47,42 +47,52 @@ RedisConnection.prototype.isAvailable = function () {
4747

4848
/** @override */
4949
RedisConnection.prototype.set = function (key, val, maxAgeMs, setWhenNotExist) {
50-
var deferred = Q.defer()
51-
var params = [key, this._compress(val), 'PX', maxAgeMs]
52-
if (setWhenNotExist) params.push('NX')
53-
this._client.set(params, this._makeNodeResolverWithTimeout(deferred, 'set', 'Redis [set] key: ' + key))
54-
return deferred.promise
50+
return this._compress(val)
51+
.thenBound(function (compressedVal) {
52+
var params = [key, compressedVal, 'PX', maxAgeMs]
53+
if (setWhenNotExist) params.push('NX')
54+
55+
var deferred = Q.defer()
56+
this._client.set(params, this._makeNodeResolverWithTimeout(deferred, 'set', 'Redis [set] key: ' + key))
57+
return deferred.promise
58+
}, this)
5559
}
5660

5761
/** @override */
5862
RedisConnection.prototype.mset = function (items, maxAgeMs, setWhenNotExist) {
5963
if (!items || !items.length) return Q.resolve(undefined)
6064

61-
var deferred = Q.defer()
62-
var commands = []
63-
var i, l
64-
if (setWhenNotExist) {
65-
// Use "SET" to set each key with a "NX" flag.
66-
for (i = 0, l = items.length; i < l; i++) {
67-
commands.push(['set', items[i].key, this._compress(items[i].value), 'PX', maxAgeMs, 'NX'])
68-
}
69-
} else {
70-
// Use "MSET" to set all the keys and "EXPIRE" to set TTL for each key
71-
var msetCommand = ['MSET']
72-
commands.push(msetCommand)
73-
for (i = 0, l = items.length; i < l; i++) {
74-
var key = items[i].key
75-
// Append key value arguments to the set command.
76-
msetCommand.push(key, this._compress(items[i].value))
77-
// Append an expire command.
78-
commands.push(['EXPIRE', key, Math.floor(maxAgeMs / 1000)])
65+
var compressedPromises = items.map(function (item) {
66+
return this._compress(item.value)
67+
}, this)
68+
return Q.all(compressedPromises)
69+
.thenBound(function (compressedValues) {
70+
var deferred = Q.defer()
71+
var commands = []
72+
73+
var i, l
74+
if (setWhenNotExist) {
75+
// Use "SET" to set each key with a "NX" flag.
76+
for (i = 0, l = items.length; i < l; i++) {
77+
commands.push(['set', items[i].key, compressedValues[i], 'PX', maxAgeMs, 'NX'])
78+
}
79+
} else {
80+
// Use "MSET" to set all the keys and "EXPIRE" to set TTL for each key
81+
var msetCommand = ['MSET']
82+
commands.push(msetCommand)
83+
for (i = 0, l = items.length; i < l; i++) {
84+
var key = items[i].key
85+
// Append key value arguments to the set command.
86+
msetCommand.push(key, compressedValues[i])
87+
// Append an expire command.
88+
commands.push(['EXPIRE', key, Math.floor(maxAgeMs / 1000)])
89+
}
7990
}
80-
}
81-
this._client.multi(commands).exec(
82-
this._makeNodeResolverWithTimeout(deferred, 'mset',
83-
'Redis [mset] key.0: ' + items[0].key + ' key.length: ' + items.length))
84-
85-
return deferred.promise
91+
this._client.multi(commands).exec(
92+
this._makeNodeResolverWithTimeout(deferred, 'mset',
93+
'Redis [mset] key.0: ' + items[0].key + ' key.length: ' + items.length))
94+
return deferred.promise
95+
}, this)
8696
}
8797

8898
/** @override */
@@ -109,7 +119,7 @@ RedisConnection.prototype.mget = function (keys) {
109119
this._makeNodeResolverWithTimeout(deferred, 'mget',
110120
opDesc))
111121
return deferred.promise
112-
.then(function (vals) {
122+
.thenBound(function (vals) {
113123
// This function post-processes values from Redis client to
114124
// make cache miss result consistent with the API.
115125
//
@@ -119,12 +129,12 @@ RedisConnection.prototype.mget = function (keys) {
119129
if (null === vals[i]) {
120130
vals[i] = undefined
121131
} else {
122-
//for real values determine if you need to decompress
123-
vals[i] = self._decompress(vals[i])
132+
//for real values determine if you need to uncompress
133+
vals[i] = this._uncompress(vals[i])
124134
}
125135
}
126-
return vals
127-
})
136+
return Q.all(vals)
137+
}, this)
128138
.then(this.getCountUpdater())
129139
}
130140

@@ -259,50 +269,50 @@ RedisConnection.prototype._makeNodeResolverWithTimeout = function (deferred, opN
259269
* Private method controls how all cache values are encoded.
260270
*
261271
* @param {string|undefined|null} value Original cache value
262-
* @return {string|undefined|null} Value encoded appropriately for the cache
272+
* @return {Q.Promise.<string|undefined|null>} Value encoded appropriately for the cache
263273
*/
264274
RedisConnection.prototype._compress = function (value) {
265275
if (!value || !this._compressionEnabled) {
266-
return value
276+
return Q.resolve(value)
267277
}
268278

269279
if (value.length > this._snappyPivot) {
270280
try {
271-
var compressed = snappy.compressSync(value)
272-
return this._compressedPrefix + compressed.toString('base64')
281+
return Q.nfcall(snappy.compress, value).thenBound(function (compressed) {
282+
return this._compressedPrefix + compressed.toString('base64')
283+
}, this)
273284
} catch (e) {
274285
console.warn("Compression failed: " + e.message)
275-
return this._uncompressedPrefix + value
286+
return Q.resolve(this._uncompressedPrefix + value)
276287
}
277288
} else {
278-
return this._uncompressedPrefix + value
289+
return Q.resolve(this._uncompressedPrefix + value)
279290
}
280291
}
281292

282293
/**
283294
* Private Method that knows how to parsed encoded cache value and decode.
284295
*
285296
* @param {string|undefined|null} value Possibly encoded value retrieved from the cache.
286-
* @return {string|undefined|null} The original input value
297+
* @return {Q.Promise.<string|undefined|null>} The original input value
287298
*/
288-
RedisConnection.prototype._decompress = function (value) {
289-
if (!value) return value
299+
RedisConnection.prototype._uncompress = function (value) {
300+
if (!value) return Q.resolve(value)
290301

291302
// Note: always check prefixes even if compression is disabled, as there might
292303
// be entries from prior to disabling compression
293304
if (value.indexOf(this._compressedPrefix) === 0) {
294305
try {
295306
var compressedBuf = new Buffer(value.substring(this._compressedPrefix.length), 'base64')
296-
var orig = snappy.decompressSync(compressedBuf, snappy.parsers.string)
297-
return orig
307+
return Q.nfcall(snappy.uncompress, compressedBuf, {asBuffer: false})
298308
} catch (e) {
299309
console.warn("Decompression failed: " + e.message)
300-
return undefined
301-
}
310+
return Q.resolve(undefined)
311+
}
302312
} else if (value.indexOf(this._uncompressedPrefix) === 0) {
303-
return value.substring(this._uncompressedPrefix.length)
313+
return Q.resolve(value.substring(this._uncompressedPrefix.length))
304314
} else {
305-
return value
315+
return Q.resolve(value)
306316
}
307317
}
308318

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@
2222
"hiredis": "0.1.16",
2323
"metrics": "0.1.6",
2424
"hashring": "1.0.3",
25-
"snappy": "2.1.2"
25+
"snappy": "3.0.7"
2626
},
2727
"devDependencies": {
28-
"nodeunit": "0.7.4",
29-
"nodeunitq": "0.0.3",
28+
"nodeunit": "0.9.0",
29+
"nodeunitq": "0.1.1",
3030
"logg": "0.2.2",
3131
"closure-npc": "0.1.3",
3232
"sinon": "git://github.com/Medium/Sinon.JS.git#xiao-fix-clearTimeout-for-nodejs"

test/test_CacheCluster.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ builder.add(function testLatencyMeasurement(test) {
341341
.then(function() {
342342
test.equal(20, cluster.getStats('set').count())
343343
test.ok(cluster.getStats('set').mean() > 28)
344-
test.ok(cluster.getStats('set').mean() < 35)
344+
test.ok(cluster.getStats('set').mean() < 38)
345345

346346
var getPromises = []
347347
for (var i = 0; i < 20; i++) {
@@ -352,7 +352,7 @@ builder.add(function testLatencyMeasurement(test) {
352352
.then(function() {
353353
test.equal(20, cluster.getStats('get').count())
354354
test.ok(cluster.getStats('get').mean() > 28)
355-
test.ok(cluster.getStats('get').mean() < 35)
355+
test.ok(cluster.getStats('get').mean() < 38)
356356

357357
var items = []
358358
for (var i = 0; i < 20; i++) {
@@ -366,7 +366,7 @@ builder.add(function testLatencyMeasurement(test) {
366366
.then(function() {
367367
test.equal(1, cluster.getStats('mset').count())
368368
test.ok(cluster.getStats('mset').mean() > 28)
369-
test.ok(cluster.getStats('mset').mean() < 35)
369+
test.ok(cluster.getStats('mset').mean() < 38)
370370

371371
var keys = []
372372
for (var i = 0; i < 20; i++) {
@@ -377,7 +377,7 @@ builder.add(function testLatencyMeasurement(test) {
377377
.then(function() {
378378
test.equal(1, cluster.getStats('mget').count())
379379
test.ok(cluster.getStats('mget').mean() > 28)
380-
test.ok(cluster.getStats('mget').mean() < 35)
380+
test.ok(cluster.getStats('mget').mean() < 38)
381381
})
382382

383383
})

0 commit comments

Comments
 (0)