Skip to content

Commit 241e156

Browse files
author
Ruben Bridgewater
committed
Fix saving buffers with charsets other than utf-8 while using multi
This will also improve pipelinening for buffers and fixes the return value of Batch.exec Fixes #913
1 parent 19ce668 commit 241e156

File tree

5 files changed

+105
-30
lines changed

5 files changed

+105
-30
lines changed

Diff for: changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Changelog
55

66
Bugfixes
77

8+
- Fixed saving buffers with charsets other than utf-8 while using multi ([@BridgeAR](https://github.com/BridgeAR))
89
- Fixed js parser handling big values very slow ([@BridgeAR](https://github.com/BridgeAR))
910
- The speed is up to ~500% faster than before but still up to ~50% slower than the hiredis parser.
1011

Diff for: index.js

+33-10
Original file line numberDiff line numberDiff line change
@@ -731,12 +731,20 @@ RedisClient.prototype.send_command = function (command, args, callback) {
731731
for (i = 0; i < args.length; i += 1) {
732732
if (Buffer.isBuffer(args[i])) {
733733
buffer_args = true;
734+
if (this.pipeline !== 0) {
735+
this.pipeline += 2;
736+
this.writeDefault = this.writeBuffers;
737+
}
734738
} else if (typeof args[i] !== 'string') {
735739
args[i] = String(args[i]);
736740
// 30000 seemed to be a good value to switch to buffers after testing and checking the pros and cons
737741
} else if (args[i].length > 30000) {
738742
big_data = true;
739743
args[i] = new Buffer(args[i]);
744+
if (this.pipeline !== 0) {
745+
this.pipeline += 2;
746+
this.writeDefault = this.writeBuffers;
747+
}
740748
}
741749
}
742750
if (this.options.detect_buffers) {
@@ -764,7 +772,7 @@ RedisClient.prototype.send_command = function (command, args, callback) {
764772
this.should_buffer = true;
765773
}
766774
// Return false to signal buffering
767-
return false;
775+
return !this.should_buffer;
768776
}
769777

770778
if (command === 'subscribe' || command === 'psubscribe' || command === 'unsubscribe' || command === 'punsubscribe') {
@@ -797,7 +805,7 @@ RedisClient.prototype.send_command = function (command, args, callback) {
797805

798806
for (i = 0; i < args.length; i += 1) {
799807
arg = args[i];
800-
if (!Buffer.isBuffer(arg)) {
808+
if (typeof arg === 'string') {
801809
this.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n');
802810
} else {
803811
this.write('$' + arg.length + '\r\n');
@@ -810,6 +818,22 @@ RedisClient.prototype.send_command = function (command, args, callback) {
810818
return !this.should_buffer;
811819
};
812820

821+
RedisClient.prototype.writeDefault = RedisClient.prototype.writeStrings = function (data) {
822+
var command, str = '';
823+
while (command = this.pipeline_queue.shift()) {
824+
str += command;
825+
}
826+
this.should_buffer = !this.stream.write(str + data);
827+
};
828+
829+
RedisClient.prototype.writeBuffers = function (data) {
830+
var command;
831+
while (command = this.pipeline_queue.shift()) {
832+
this.stream.write(command);
833+
}
834+
this.should_buffer = !this.stream.write(data);
835+
};
836+
813837
RedisClient.prototype.write = function (data) {
814838
if (this.pipeline === 0) {
815839
this.should_buffer = !this.stream.write(data);
@@ -818,11 +842,7 @@ RedisClient.prototype.write = function (data) {
818842

819843
this.pipeline--;
820844
if (this.pipeline === 0) {
821-
var command, str = '';
822-
while (command = this.pipeline_queue.shift()) {
823-
str += command;
824-
}
825-
this.should_buffer = !this.stream.write(str + data);
845+
this.writeDefault(data);
826846
return;
827847
}
828848

@@ -1121,10 +1141,12 @@ Multi.prototype.exec_transaction = function (callback) {
11211141
this.send_command(command, args, index, cb);
11221142
}
11231143

1124-
this._client.uncork();
1125-
return this._client.send_command('exec', [], function(err, replies) {
1144+
this._client.send_command('exec', [], function(err, replies) {
11261145
self.execute_callback(err, replies);
11271146
});
1147+
this._client.uncork();
1148+
this._client.writeDefault = this._client.writeStrings;
1149+
return !this._client.should_buffer;
11281150
};
11291151

11301152
Multi.prototype.execute_callback = function (err, replies) {
@@ -1231,7 +1253,8 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct
12311253
index++;
12321254
}
12331255
this._client.uncork();
1234-
return this._client.should_buffer;
1256+
this._client.writeDefault = this._client.writeStrings;
1257+
return !this._client.should_buffer;
12351258
};
12361259

12371260
var createClient = function (port_arg, host_arg, options) {

Diff for: test/detect_buffers.spec.js

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ describe("detect_buffers", function () {
2727
});
2828
});
2929

30+
afterEach(function () {
31+
client.end();
32+
});
33+
3034
describe('get', function () {
3135
describe('first argument is a string', function () {
3236
it('returns a string', function (done) {

Diff for: test/helper.js

+2
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,9 @@ module.exports = {
156156
i++;
157157
if (i === max) {
158158
func();
159+
return true;
159160
}
161+
return false;
160162
};
161163
},
162164
killConnection: function (client) {

Diff for: test/commands/multi.spec.js renamed to test/multi.spec.js

+65-20
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,74 @@
11
'use strict';
22

33
var assert = require('assert');
4-
var config = require("../lib/config");
5-
var helper = require('../helper');
4+
var config = require("./lib/config");
5+
var helper = require('./helper');
66
var redis = config.redis;
7+
var zlib = require('zlib');
78
var uuid = require('uuid');
9+
var client;
810

911
describe("The 'multi' method", function () {
1012

13+
afterEach(function () {
14+
client.end();
15+
});
16+
17+
describe('regression test', function () {
18+
it('saved buffers with charsets different than utf-8 (issue #913)', function (done) {
19+
client = redis.createClient();
20+
21+
var end = helper.callFuncAfter(done, 100);
22+
23+
// Some random object created from http://beta.json-generator.com/
24+
var test_obj = {
25+
"_id": "5642c4c33d4667c4a1fefd99","index": 0, "guid": "5baf1f1c-7621-41e7-ae7a-f8c6f3199b0f", "isActive": true,
26+
"balance": "$1,028.63", "picture": "http://placehold.it/32x32", "age": 31, "eyeColor": "green", "name": {"first": "Shana", "last": "Long"},
27+
"company": "MANGLO", "email": "[email protected]", "phone": "+1 (926) 405-3105", "address": "747 Dank Court, Norfolk, Ohio, 1112",
28+
"about": "Eu pariatur in nisi occaecat enim qui consequat nostrud cupidatat id. " +
29+
"Commodo commodo dolore esse irure minim quis deserunt anim laborum aute deserunt et est. Quis nisi laborum deserunt nisi quis.",
30+
"registered": "Friday, April 18, 2014 9:56 AM", "latitude": "74.566613", "longitude": "-11.660432", "tags": [7, "excepteur"],
31+
"range": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "friends": [3, {"id": 1, "name": "Schultz Dyer"}],
32+
"greeting": "Hello, Shana! You have 5 unread messages.", "favoriteFruit": "strawberry"
33+
};
34+
35+
function run () {
36+
if (end() === true) {
37+
return;
38+
}
39+
// To demonstrate a big payload for hash set field values, let's create a big array
40+
var test_arr = [];
41+
for (var i = 0; i < 80; i++) {
42+
var new_obj = JSON.parse(JSON.stringify(test_obj));
43+
test_arr.push(new_obj);
44+
}
45+
46+
var json = JSON.stringify(test_arr);
47+
zlib.deflate(new Buffer(json), function (err, buffer) {
48+
if (err) {
49+
done(err);
50+
return;
51+
}
52+
53+
var multi = client.multi();
54+
multi.del('SOME_KEY');
55+
56+
for (i = 0; i < 100; i++) {
57+
multi.hset('SOME_KEY', 'SOME_FIELD' + i, buffer);
58+
}
59+
multi.exec(function (err, res) {
60+
if (err) {
61+
done(err);
62+
return;
63+
}
64+
run();
65+
});
66+
});
67+
}
68+
run();
69+
});
70+
});
71+
1172
helper.allTests(function(parser, ip, args) {
1273

1374
describe("using " + parser + " and " + ip, function () {
@@ -19,14 +80,13 @@ describe("The 'multi' method", function () {
1980
});
2081

2182
describe("when not connected", function () {
22-
var client;
2383

2484
beforeEach(function (done) {
2585
client = redis.createClient.apply(redis.createClient, args);
2686
client.once("ready", function () {
2787
client.quit();
2888
});
29-
client.on('end', function () {
89+
client.once('end', function () {
3090
return done();
3191
});
3292
});
@@ -37,7 +97,7 @@ describe("The 'multi' method", function () {
3797
assert(err.message.match(/The connection has already been closed/));
3898
done();
3999
});
40-
assert.strictEqual(notBuffering, false);
100+
assert.strictEqual(notBuffering, true);
41101
});
42102

43103
it("reports an error if promisified", function () {
@@ -48,17 +108,12 @@ describe("The 'multi' method", function () {
48108
});
49109

50110
describe("when connected", function () {
51-
var client;
52111

53112
beforeEach(function (done) {
54113
client = redis.createClient.apply(redis.createClient, args);
55114
client.once("connect", done);
56115
});
57116

58-
afterEach(function () {
59-
client.end();
60-
});
61-
62117
it("executes a pipelined multi properly in combination with the offline queue", function (done) {
63118
var multi1 = client.multi();
64119
multi1.set("m1", "123");
@@ -93,11 +148,6 @@ describe("The 'multi' method", function () {
93148
});
94149

95150
describe("when connection is broken", function () {
96-
var client;
97-
98-
afterEach(function () {
99-
client.end();
100-
});
101151

102152
it("return an error even if connection is in broken mode if callback is present", function (done) {
103153
client = redis.createClient({
@@ -137,7 +187,6 @@ describe("The 'multi' method", function () {
137187
});
138188

139189
describe("when ready", function () {
140-
var client;
141190

142191
beforeEach(function (done) {
143192
client = redis.createClient.apply(redis.createClient, args);
@@ -148,10 +197,6 @@ describe("The 'multi' method", function () {
148197
});
149198
});
150199

151-
afterEach(function () {
152-
client.end();
153-
});
154-
155200
it("returns an empty result array", function (done) {
156201
var multi = client.multi();
157202
var notBuffering = multi.exec(function (err, res) {

0 commit comments

Comments
 (0)