Skip to content

Commit 21d8bdb

Browse files
author
Ruben Bridgewater
committed
Refactor multi to have a consistent error handling
Ignore *.log files
1 parent 13635c9 commit 21d8bdb

File tree

4 files changed

+80
-22
lines changed

4 files changed

+80
-22
lines changed

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ generate_commands.js
66
multi_bench.js
77
test-unref.js
88
changelog.md
9+
*.log

index.js

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,6 @@ RedisClient.prototype.ready_check = function () {
389389
RedisClient.prototype.send_offline_queue = function () {
390390
var command_obj, buffered_writes = 0;
391391

392-
// TODO: Implement queue.pop() as it should be faster than shift and evaluate petka antonovs queue
393392
while (command_obj = this.offline_queue.shift()) {
394393
debug("Sending offline command: " + command_obj.command);
395394
buffered_writes += !this.send_command(command_obj.command, command_obj.args, command_obj.callback);
@@ -995,15 +994,15 @@ Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) {
995994
return this;
996995
};
997996

998-
Multi.prototype.send_command = function (command, args, cb) {
997+
Multi.prototype.send_command = function (command, args, index, cb) {
999998
var self = this;
1000999
this._client.send_command(command, args, function (err, reply) {
10011000
if (err) {
10021001
if (cb) {
10031002
cb(err);
1004-
} else {
1005-
self.errors.push(err);
10061003
}
1004+
err.position = index - 1;
1005+
self.errors.push(err);
10071006
}
10081007
});
10091008
};
@@ -1029,7 +1028,7 @@ Multi.prototype.exec = Multi.prototype.EXEC = function (callback) {
10291028
break;
10301029
}
10311030
}
1032-
this.send_command(command, args, cb);
1031+
this.send_command(command, args, index, cb);
10331032
}
10341033

10351034
this._client.send_command('exec', [], function(err, replies) {
@@ -1042,9 +1041,10 @@ Multi.prototype.execute_callback = function (err, replies) {
10421041

10431042
if (err) {
10441043
if (err.code !== 'CONNECTION_BROKEN') {
1044+
err.code = 'EXECABORT';
1045+
err.errors = this.errors;
10451046
if (this.callback) {
1046-
this.errors.push(err);
1047-
this.callback(this.errors);
1047+
this.callback(err);
10481048
} else {
10491049
// Exclude CONNECTION_BROKEN so that error won't be emitted twice
10501050
this._client.emit('error', err);
@@ -1059,17 +1059,22 @@ Multi.prototype.execute_callback = function (err, replies) {
10591059
args = this.queue[i];
10601060

10611061
// If we asked for strings, even in detect_buffers mode, then return strings:
1062-
if (this._client.options.detect_buffers && this.wants_buffers[i] === false) {
1063-
replies[i - 1] = reply = reply_to_strings(reply);
1064-
}
1065-
1066-
// TODO - confusing and error-prone that hgetall is special cased in two places
1067-
if (reply && args[0] === "hgetall") {
1068-
replies[i - 1] = reply = reply_to_object(reply);
1062+
if (reply) {
1063+
if (this._client.options.detect_buffers && this.wants_buffers[i] === false) {
1064+
replies[i - 1] = reply = reply_to_strings(reply);
1065+
}
1066+
if (args[0] === "hgetall") {
1067+
// TODO - confusing and error-prone that hgetall is special cased in two places
1068+
replies[i - 1] = reply = reply_to_object(reply);
1069+
}
10691070
}
10701071

10711072
if (typeof args[args.length - 1] === "function") {
1072-
args[args.length - 1](null, reply);
1073+
if (reply instanceof Error) {
1074+
args[args.length - 1](reply);
1075+
} else {
1076+
args[args.length - 1](null, reply);
1077+
}
10731078
}
10741079
}
10751080
}

test/commands/multi.spec.js

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -249,18 +249,55 @@ describe("The 'multi' method", function () {
249249
});
250250
});
251251

252-
it('reports multiple exceptions when they occur', function (done) {
253-
helper.serverVersionAtLeast.call(this, client, [2, 6, 5]);
252+
it('reports EXECABORT exceptions when they occur (while queueing)', function (done) {
253+
client.multi().config("bar").set("foo").exec(function (err, reply) {
254+
assert.equal(err.code, "EXECABORT");
255+
assert.equal(reply, undefined, "The reply should have been discarded");
256+
assert(err.message.match(/^EXECABORT/), "Error message should begin with EXECABORT");
257+
assert.equal(err.errors.length, 1, "err.errors should have 1 items");
258+
assert.strictEqual(err.errors[0].command_used, 'SET');
259+
assert.strictEqual(err.errors[0].position, 1);
260+
assert(/^ERR/.test(err.errors[0].message), "Actuall error message should begin with ERR");
261+
return done();
262+
});
263+
});
254264

255-
client.multi().set("foo").exec(function (err, reply) {
256-
assert(Array.isArray(err), "err should be an array");
257-
assert.equal(2, err.length, "err should have 2 items");
258-
assert(err[0].message.match(/^ERR/), "First error message should begin with ERR");
259-
assert(err[1].message.match(/^EXECABORT/), "First error message should begin with EXECABORT");
265+
it('reports multiple exceptions when they occur (while EXEC is running)', function (done) {
266+
client.multi().config("bar").debug("foo").exec(function (err, reply) {
267+
assert.strictEqual(reply.length, 2);
268+
assert(/^ERR/.test(reply[0].message), "Error message should begin with ERR");
269+
assert(/^ERR/.test(reply[1].message), "Error message should begin with ERR");
260270
return done();
261271
});
262272
});
263273

274+
it('reports multiple exceptions when they occur (while EXEC is running) and calls cb', function (done) {
275+
var multi = client.multi();
276+
multi.config("bar", helper.isError());
277+
multi.set('foo', 'bar', helper.isString('OK'));
278+
multi.debug("foo").exec(function (err, reply) {
279+
assert.strictEqual(reply.length, 3);
280+
assert(/^ERR/.test(reply[0].message), "Error message should begin with ERR");
281+
assert(/^ERR/.test(reply[2].message), "Error message should begin with ERR");
282+
assert.strictEqual(reply[1], "OK");
283+
client.get('foo', helper.isString('bar', done));
284+
});
285+
});
286+
287+
it("emits an error if no callback has been provided and execabort error occured", function (done) {
288+
helper.serverVersionAtLeast.call(this, client, [2, 6, 5]);
289+
290+
var multi = client.multi();
291+
multi.config("bar");
292+
multi.set("foo");
293+
multi.exec();
294+
295+
client.on('error', function(err) {
296+
assert.equal(err.code, "EXECABORT");
297+
done();
298+
});
299+
});
300+
264301
});
265302
});
266303
});

test/node_redis.spec.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,21 @@ describe("The node_redis client", function () {
7777
});
7878
});
7979

80+
it("connects correctly to localhost and no ready check", function (done) {
81+
client = redis.createClient(undefined, undefined, {
82+
no_ready_check: true
83+
});
84+
client.on("error", done);
85+
86+
client.once("ready", function () {
87+
client.set('foo', 'bar');
88+
client.get('foo', function(err, res) {
89+
assert.strictEqual(res, 'bar');
90+
done(err);
91+
});
92+
});
93+
});
94+
8095
it("throws on strange connection info", function () {
8196
try {
8297
redis.createClient(true);

0 commit comments

Comments
 (0)