You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-06 02:15:48 +03:00
Refactor command parsing
This commit is contained in:
52
index.js
52
index.js
@@ -6,8 +6,6 @@ var util = require('util');
|
||||
var utils = require('./lib/utils');
|
||||
var Queue = require('double-ended-queue');
|
||||
var errorClasses = require('./lib/customErrors');
|
||||
var Command = require('./lib/command').Command;
|
||||
var OfflineCommand = require('./lib/command').OfflineCommand;
|
||||
var EventEmitter = require('events');
|
||||
var Parser = require('redis-parser');
|
||||
var commands = require('redis-commands');
|
||||
@@ -156,6 +154,7 @@ function RedisClient (options, stream) {
|
||||
this.times_connected = 0;
|
||||
this.buffers = options.return_buffers || options.detect_buffers;
|
||||
this.options = options;
|
||||
this.old_state = {};
|
||||
this.reply = 'ON'; // Returning replies is the default
|
||||
// Init parser
|
||||
this.reply_parser = create_parser(this);
|
||||
@@ -443,14 +442,10 @@ RedisClient.prototype.on_ready = function () {
|
||||
|
||||
// Restore modal commands from previous connection. The order of the commands is important
|
||||
if (this.selected_db !== undefined) {
|
||||
this.internal_send_command('select', [this.selected_db]);
|
||||
this.select(this.selected_db);
|
||||
}
|
||||
if (this.old_state !== null) {
|
||||
this.monitoring = this.old_state.monitoring;
|
||||
this.pub_sub_mode = this.old_state.pub_sub_mode;
|
||||
}
|
||||
if (this.monitoring) { // Monitor has to be fired before pub sub commands
|
||||
this.internal_send_command('monitor', []); // The state is still set
|
||||
if (this.old_state.monitoring) { // Monitor has to be fired before pub sub commands
|
||||
this.monitor();
|
||||
}
|
||||
var callback_count = Object.keys(this.subscription_set).length;
|
||||
if (!this.options.disable_resubscribing && callback_count) {
|
||||
@@ -466,8 +461,8 @@ RedisClient.prototype.on_ready = function () {
|
||||
debug('Sending pub/sub on_ready commands');
|
||||
for (var key in this.subscription_set) {
|
||||
var command = key.slice(0, key.indexOf('_'));
|
||||
var args = self.subscription_set[key];
|
||||
self.internal_send_command(command, [args], callback);
|
||||
var args = this.subscription_set[key];
|
||||
this[command]([args], callback);
|
||||
}
|
||||
this.send_offline_queue();
|
||||
return;
|
||||
@@ -530,7 +525,7 @@ RedisClient.prototype.ready_check = function () {
|
||||
RedisClient.prototype.send_offline_queue = function () {
|
||||
for (var command_obj = this.offline_queue.shift(); command_obj; command_obj = this.offline_queue.shift()) {
|
||||
debug('Sending offline command: ' + command_obj.command);
|
||||
this.internal_send_command(command_obj.command, command_obj.args, command_obj.callback, command_obj.call_on_write);
|
||||
this.internal_send_command(command_obj);
|
||||
}
|
||||
this.drain();
|
||||
};
|
||||
@@ -575,8 +570,7 @@ RedisClient.prototype.connection_gone = function (why, error) {
|
||||
this.pipeline = false;
|
||||
|
||||
var state = {
|
||||
monitoring: this.monitoring,
|
||||
pub_sub_mode: this.pub_sub_mode
|
||||
monitoring: this.monitoring
|
||||
};
|
||||
this.old_state = state;
|
||||
this.monitoring = false;
|
||||
@@ -834,7 +828,6 @@ RedisClient.prototype.return_reply = function (reply) {
|
||||
|
||||
function handle_offline_command (self, command_obj) {
|
||||
var command = command_obj.command;
|
||||
var callback = command_obj.callback;
|
||||
var err, msg;
|
||||
if (self.closing || !self.enable_offline_queue) {
|
||||
command = command.toUpperCase();
|
||||
@@ -852,10 +845,10 @@ function handle_offline_command (self, command_obj) {
|
||||
code: 'NR_CLOSED',
|
||||
command: command
|
||||
});
|
||||
if (command_obj.args && command_obj.args.length) {
|
||||
if (command_obj.args.length) {
|
||||
err.args = command_obj.args;
|
||||
}
|
||||
utils.reply_in_order(self, callback, err);
|
||||
utils.reply_in_order(self, command_obj.callback, err);
|
||||
} else {
|
||||
debug('Queueing ' + command + ' for next server connection.');
|
||||
self.offline_queue.push(command_obj);
|
||||
@@ -865,22 +858,23 @@ function handle_offline_command (self, command_obj) {
|
||||
|
||||
// Do not call internal_send_command directly, if you are not absolutly certain it handles everything properly
|
||||
// e.g. monitor / info does not work with internal_send_command only
|
||||
RedisClient.prototype.internal_send_command = function (command, args, callback, call_on_write) {
|
||||
var arg, prefix_keys, command_obj;
|
||||
RedisClient.prototype.internal_send_command = function (command_obj) {
|
||||
var arg, prefix_keys;
|
||||
var i = 0;
|
||||
var command_str = '';
|
||||
var args = command_obj.args;
|
||||
var command = command_obj.command;
|
||||
var len = args.length;
|
||||
var big_data = false;
|
||||
var buffer_args = false;
|
||||
var args_copy = new Array(len);
|
||||
|
||||
if (process.domain && callback) {
|
||||
callback = process.domain.bind(callback);
|
||||
if (process.domain && command_obj.callback) {
|
||||
command_obj.callback = process.domain.bind(command_obj.callback);
|
||||
}
|
||||
|
||||
if (this.ready === false || this.stream.writable === false) {
|
||||
// Handle offline commands right away
|
||||
handle_offline_command(this, new OfflineCommand(command, args, callback, call_on_write));
|
||||
handle_offline_command(this, command_obj);
|
||||
return false; // Indicate buffering
|
||||
}
|
||||
|
||||
@@ -905,7 +899,7 @@ RedisClient.prototype.internal_send_command = function (command, args, callback,
|
||||
args_copy[i] = 'null'; // Backwards compatible :/
|
||||
} else if (Buffer.isBuffer(args[i])) {
|
||||
args_copy[i] = args[i];
|
||||
buffer_args = true;
|
||||
command_obj.buffer_args = true;
|
||||
big_data = true;
|
||||
} else {
|
||||
this.warn(
|
||||
@@ -927,8 +921,6 @@ RedisClient.prototype.internal_send_command = function (command, args, callback,
|
||||
args_copy[i] = '' + args[i];
|
||||
}
|
||||
}
|
||||
// Pass the original args to make sure in error cases the original arguments are returned
|
||||
command_obj = new Command(command, args, buffer_args, callback);
|
||||
|
||||
if (this.options.prefix) {
|
||||
prefix_keys = commands.getKeyIndexes(command, args_copy);
|
||||
@@ -967,8 +959,8 @@ RedisClient.prototype.internal_send_command = function (command, args, callback,
|
||||
debug('send_command: buffer send ' + arg.length + ' bytes');
|
||||
}
|
||||
}
|
||||
if (call_on_write) {
|
||||
call_on_write();
|
||||
if (command_obj.call_on_write) {
|
||||
command_obj.call_on_write();
|
||||
}
|
||||
// Handle `CLIENT REPLY ON|OFF|SKIP`
|
||||
// This has to be checked after call_on_write
|
||||
@@ -978,8 +970,8 @@ RedisClient.prototype.internal_send_command = function (command, args, callback,
|
||||
} else {
|
||||
// Do not expect a reply
|
||||
// Does this work in combination with the pub sub mode?
|
||||
if (callback) {
|
||||
utils.reply_in_order(this, callback, null, undefined, this.command_queue);
|
||||
if (command_obj.callback) {
|
||||
utils.reply_in_order(this, command_obj.callback, null, undefined, this.command_queue);
|
||||
}
|
||||
if (this.reply === 'SKIP') {
|
||||
this.reply = 'SKIP_ONE_MORE';
|
||||
|
@@ -1,22 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
// This Command constructor is ever so slightly faster than using an object literal, but more importantly, using
|
||||
// a named constructor helps it show up meaningfully in the V8 CPU profiler and in heap snapshots.
|
||||
function Command (command, args, buffer_args, callback) {
|
||||
this.command = command;
|
||||
this.args = args;
|
||||
this.buffer_args = buffer_args;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
function OfflineCommand (command, args, callback, call_on_write) {
|
||||
function Command (command, args, callback, call_on_write) {
|
||||
this.command = command;
|
||||
this.args = args;
|
||||
this.buffer_args = false;
|
||||
this.callback = callback;
|
||||
this.call_on_write = call_on_write;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Command: Command,
|
||||
OfflineCommand: OfflineCommand
|
||||
};
|
||||
module.exports = Command;
|
||||
|
@@ -3,6 +3,7 @@
|
||||
var commands = require('redis-commands');
|
||||
var Multi = require('./multi');
|
||||
var RedisClient = require('../').RedisClient;
|
||||
var Command = require('./command');
|
||||
|
||||
// TODO: Rewrite this including the invidual commands into a Commands class
|
||||
// that provided a functionality to add new commands to the client
|
||||
@@ -42,7 +43,7 @@ commands.list.forEach(function (command) {
|
||||
arr[i] = arguments[i];
|
||||
}
|
||||
}
|
||||
return this.internal_send_command(command, arr, callback);
|
||||
return this.internal_send_command(new Command(command, arr, callback));
|
||||
};
|
||||
Object.defineProperty(RedisClient.prototype[command], 'name', {
|
||||
value: command
|
||||
@@ -82,7 +83,7 @@ commands.list.forEach(function (command) {
|
||||
arr[i] = arguments[i];
|
||||
}
|
||||
}
|
||||
this.queue.push([command, arr, callback]);
|
||||
this.queue.push(new Command(command, arr, callback));
|
||||
return this;
|
||||
};
|
||||
Object.defineProperty(Multi.prototype[command], 'name', {
|
||||
|
@@ -3,6 +3,7 @@
|
||||
var utils = require('./utils');
|
||||
var debug = require('./debug');
|
||||
var RedisClient = require('../').RedisClient;
|
||||
var Command = require('./command');
|
||||
var noop = function () {};
|
||||
|
||||
/**********************************************
|
||||
@@ -36,7 +37,7 @@ RedisClient.prototype.send_command = RedisClient.prototype.sendCommand = functio
|
||||
// but this might change from time to time and at the moment there's no good way to distinguishe them
|
||||
// from each other, so let's just do it do it this way for the time being
|
||||
if (command === 'multi' || typeof this[command] !== 'function') {
|
||||
return this.internal_send_command(command, args, callback);
|
||||
return this.internal_send_command(new Command(command, args, callback));
|
||||
}
|
||||
if (typeof callback === 'function') {
|
||||
args = args.concat([callback]); // Prevent manipulating the input array
|
||||
|
@@ -3,6 +3,7 @@
|
||||
var utils = require('./utils');
|
||||
var debug = require('./debug');
|
||||
var Multi = require('./multi');
|
||||
var Command = require('./command');
|
||||
var no_password_is_set = /no password is set/;
|
||||
var loading = /LOADING/;
|
||||
var RedisClient = require('../').RedisClient;
|
||||
@@ -42,33 +43,34 @@ function select_callback (self, db, callback) {
|
||||
}
|
||||
|
||||
RedisClient.prototype.select = RedisClient.prototype.SELECT = function select (db, callback) {
|
||||
return this.internal_send_command('select', [db], select_callback(this, db, callback));
|
||||
return this.internal_send_command(new Command('select', [db], select_callback(this, db, callback)));
|
||||
};
|
||||
|
||||
Multi.prototype.select = Multi.prototype.SELECT = function select (db, callback) {
|
||||
this.queue.push(['select', [db], select_callback(this._client, db, callback)]);
|
||||
this.queue.push(new Command('select', [db], select_callback(this._client, db, callback)));
|
||||
return this;
|
||||
};
|
||||
|
||||
function monitor_callback (self, callback) {
|
||||
return function (err, res) {
|
||||
if (err === null) {
|
||||
self.monitoring = true;
|
||||
}
|
||||
utils.callback_or_emit(self, callback, err, res);
|
||||
};
|
||||
}
|
||||
|
||||
RedisClient.prototype.monitor = RedisClient.prototype.MONITOR = function monitor (callback) {
|
||||
// Use a individual command, as this is a special case that does not has to be checked for any other command
|
||||
return this.internal_send_command('monitor', [], monitor_callback(this, callback));
|
||||
var self = this;
|
||||
var call_on_write = function () {
|
||||
// Activating monitor mode has to happen before Redis returned the callback,
|
||||
// as the client could receive monitoring commands before the callback returned through a race condition
|
||||
self.monitoring = true;
|
||||
};
|
||||
return this.internal_send_command(new Command('monitor', [], callback, call_on_write));
|
||||
};
|
||||
|
||||
// Only works with batch, not in a transaction
|
||||
Multi.prototype.monitor = Multi.prototype.MONITOR = function monitor (callback) {
|
||||
// Use a individual command, as this is a special case that does not has to be checked for any other command
|
||||
if (this.exec !== this.exec_transaction) {
|
||||
this.queue.push(['monitor', [], monitor_callback(this._client, callback)]);
|
||||
var self = this;
|
||||
var call_on_write = function () {
|
||||
self._client.monitoring = true;
|
||||
};
|
||||
this.queue.push(new Command('monitor', [], callback, call_on_write));
|
||||
return this;
|
||||
}
|
||||
// Set multi monitoring to indicate the exec that it should abort
|
||||
@@ -100,7 +102,7 @@ RedisClient.prototype.QUIT = RedisClient.prototype.quit = function quit (callbac
|
||||
// TODO: Consider this for v.3
|
||||
// Allow the quit command to be fired as soon as possible to prevent it landing in the offline queue.
|
||||
// this.ready = this.offline_queue.length === 0;
|
||||
var backpressure_indicator = this.internal_send_command('quit', [], quit_callback(this, callback));
|
||||
var backpressure_indicator = this.internal_send_command(new Command('quit', [], quit_callback(this, callback)));
|
||||
// Calling quit should always end the connection, no matter if there's a connection or not
|
||||
this.closing = true;
|
||||
this.ready = false;
|
||||
@@ -115,7 +117,7 @@ Multi.prototype.QUIT = Multi.prototype.quit = function quit (callback) {
|
||||
self.closing = true;
|
||||
self.ready = false;
|
||||
};
|
||||
this.queue.push(['quit', [], quit_callback(self, callback), call_on_write]);
|
||||
this.queue.push(new Command('quit', [], quit_callback(self, callback), call_on_write));
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -164,7 +166,7 @@ RedisClient.prototype.info = RedisClient.prototype.INFO = function info (section
|
||||
} else if (section !== undefined) {
|
||||
args = Array.isArray(section) ? section : [section];
|
||||
}
|
||||
return this.internal_send_command('info', args, info_callback(this, callback));
|
||||
return this.internal_send_command(new Command('info', args, info_callback(this, callback)));
|
||||
};
|
||||
|
||||
Multi.prototype.info = Multi.prototype.INFO = function info (section, callback) {
|
||||
@@ -174,7 +176,7 @@ Multi.prototype.info = Multi.prototype.INFO = function info (section, callback)
|
||||
} else if (section !== undefined) {
|
||||
args = Array.isArray(section) ? section : [section];
|
||||
}
|
||||
this.queue.push(['info', args, info_callback(this._client, callback)]);
|
||||
this.queue.push(new Command('info', args, info_callback(this._client, callback)));
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -205,7 +207,7 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, c
|
||||
this.auth_pass = pass;
|
||||
var ready = this.ready;
|
||||
this.ready = ready || this.offline_queue.length === 0;
|
||||
var tmp = this.internal_send_command('auth', [pass], auth_callback(this, pass, callback));
|
||||
var tmp = this.internal_send_command(new Command('auth', [pass], auth_callback(this, pass, callback)));
|
||||
this.ready = ready;
|
||||
return tmp;
|
||||
};
|
||||
@@ -216,7 +218,7 @@ Multi.prototype.auth = Multi.prototype.AUTH = function auth (pass, callback) {
|
||||
|
||||
// Stash auth for connect and reconnect.
|
||||
this.auth_pass = pass;
|
||||
this.queue.push(['auth', [pass], auth_callback(this._client, callback)]);
|
||||
this.queue.push(new Command('auth', [pass], auth_callback(this._client, callback)));
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -262,7 +264,7 @@ RedisClient.prototype.client = RedisClient.prototype.CLIENT = function client ()
|
||||
};
|
||||
}
|
||||
}
|
||||
return this.internal_send_command('client', arr, callback, call_on_write);
|
||||
return this.internal_send_command(new Command('client', arr, callback, call_on_write));
|
||||
};
|
||||
|
||||
Multi.prototype.client = Multi.prototype.CLIENT = function client () {
|
||||
@@ -307,7 +309,7 @@ Multi.prototype.client = Multi.prototype.CLIENT = function client () {
|
||||
};
|
||||
}
|
||||
}
|
||||
this.queue.push(['client', arr, callback, call_on_write]);
|
||||
this.queue.push(new Command('client', arr, callback, call_on_write));
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -347,7 +349,7 @@ RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function hmset () {
|
||||
arr[i] = arguments[i];
|
||||
}
|
||||
}
|
||||
return this.internal_send_command('hmset', arr, callback);
|
||||
return this.internal_send_command(new Command('hmset', arr, callback));
|
||||
};
|
||||
|
||||
Multi.prototype.hmset = Multi.prototype.HMSET = function hmset () {
|
||||
@@ -386,7 +388,7 @@ Multi.prototype.hmset = Multi.prototype.HMSET = function hmset () {
|
||||
arr[i] = arguments[i];
|
||||
}
|
||||
}
|
||||
this.queue.push(['hmset', arr, callback]);
|
||||
this.queue.push(new Command('hmset', arr, callback));
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -414,7 +416,7 @@ RedisClient.prototype.subscribe = RedisClient.prototype.SUBSCRIBE = function sub
|
||||
var call_on_write = function () {
|
||||
self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1;
|
||||
};
|
||||
return this.internal_send_command('subscribe', arr, callback, call_on_write);
|
||||
return this.internal_send_command(new Command('subscribe', arr, callback, call_on_write));
|
||||
};
|
||||
|
||||
Multi.prototype.subscribe = Multi.prototype.SUBSCRIBE = function subscribe () {
|
||||
@@ -441,7 +443,7 @@ Multi.prototype.subscribe = Multi.prototype.SUBSCRIBE = function subscribe () {
|
||||
var call_on_write = function () {
|
||||
self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1;
|
||||
};
|
||||
this.queue.push(['subscribe', arr, callback, call_on_write]);
|
||||
this.queue.push(new Command('subscribe', arr, callback, call_on_write));
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -470,7 +472,7 @@ RedisClient.prototype.unsubscribe = RedisClient.prototype.UNSUBSCRIBE = function
|
||||
// Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback
|
||||
self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1;
|
||||
};
|
||||
return this.internal_send_command('unsubscribe', arr, callback, call_on_write);
|
||||
return this.internal_send_command(new Command('unsubscribe', arr, callback, call_on_write));
|
||||
};
|
||||
|
||||
Multi.prototype.unsubscribe = Multi.prototype.UNSUBSCRIBE = function unsubscribe () {
|
||||
@@ -498,7 +500,7 @@ Multi.prototype.unsubscribe = Multi.prototype.UNSUBSCRIBE = function unsubscribe
|
||||
// Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback
|
||||
self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1;
|
||||
};
|
||||
this.queue.push(['unsubscribe', arr, callback, call_on_write]);
|
||||
this.queue.push(new Command('unsubscribe', arr, callback, call_on_write));
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -526,7 +528,7 @@ RedisClient.prototype.psubscribe = RedisClient.prototype.PSUBSCRIBE = function p
|
||||
var call_on_write = function () {
|
||||
self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1;
|
||||
};
|
||||
return this.internal_send_command('psubscribe', arr, callback, call_on_write);
|
||||
return this.internal_send_command(new Command('psubscribe', arr, callback, call_on_write));
|
||||
};
|
||||
|
||||
Multi.prototype.psubscribe = Multi.prototype.PSUBSCRIBE = function psubscribe () {
|
||||
@@ -553,7 +555,7 @@ Multi.prototype.psubscribe = Multi.prototype.PSUBSCRIBE = function psubscribe ()
|
||||
var call_on_write = function () {
|
||||
self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1;
|
||||
};
|
||||
this.queue.push(['psubscribe', arr, callback, call_on_write]);
|
||||
this.queue.push(new Command('psubscribe', arr, callback, call_on_write));
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -582,7 +584,7 @@ RedisClient.prototype.punsubscribe = RedisClient.prototype.PUNSUBSCRIBE = functi
|
||||
// Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback
|
||||
self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1;
|
||||
};
|
||||
return this.internal_send_command('punsubscribe', arr, callback, call_on_write);
|
||||
return this.internal_send_command(new Command('punsubscribe', arr, callback, call_on_write));
|
||||
};
|
||||
|
||||
Multi.prototype.punsubscribe = Multi.prototype.PUNSUBSCRIBE = function punsubscribe () {
|
||||
@@ -610,6 +612,6 @@ Multi.prototype.punsubscribe = Multi.prototype.PUNSUBSCRIBE = function punsubscr
|
||||
// Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback
|
||||
self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1;
|
||||
};
|
||||
this.queue.push(['punsubscribe', arr, callback, call_on_write]);
|
||||
this.queue.push(new Command('punsubscribe', arr, callback, call_on_write));
|
||||
return this;
|
||||
};
|
||||
|
76
lib/multi.js
76
lib/multi.js
@@ -2,6 +2,7 @@
|
||||
|
||||
var Queue = require('double-ended-queue');
|
||||
var utils = require('./utils');
|
||||
var Command = require('./command');
|
||||
|
||||
function Multi (client, args) {
|
||||
this._client = client;
|
||||
@@ -20,18 +21,23 @@ function Multi (client, args) {
|
||||
}
|
||||
}
|
||||
|
||||
function pipeline_transaction_command (self, command, args, index, cb, call_on_write) {
|
||||
function pipeline_transaction_command (self, command_obj, index) {
|
||||
// Queueing is done first, then the commands are executed
|
||||
self._client.send_command(command, args, function (err, reply) {
|
||||
var tmp = command_obj.callback;
|
||||
command_obj.callback = function (err, reply) {
|
||||
// Ignore the multi command. This is applied by node_redis and the user does not benefit by it
|
||||
if (err && index !== -1) {
|
||||
if (cb) {
|
||||
cb(err);
|
||||
if (tmp) {
|
||||
tmp(err);
|
||||
}
|
||||
err.position = index;
|
||||
self.errors.push(err);
|
||||
}
|
||||
});
|
||||
// Keep track of who wants buffer responses:
|
||||
// By the time the callback is called the command_obj got the buffer_args attribute attached
|
||||
self.wants_buffers[index] = command_obj.buffer_args;
|
||||
};
|
||||
self._client.internal_send_command(command_obj);
|
||||
}
|
||||
|
||||
Multi.prototype.exec_atomic = Multi.prototype.EXEC_ATOMIC = Multi.prototype.execAtomic = function exec_atomic (callback) {
|
||||
@@ -42,7 +48,7 @@ Multi.prototype.exec_atomic = Multi.prototype.EXEC_ATOMIC = Multi.prototype.exec
|
||||
};
|
||||
|
||||
function multi_callback (self, err, replies) {
|
||||
var i = 0, args;
|
||||
var i = 0, command_obj;
|
||||
|
||||
if (err) {
|
||||
err.errors = self.errors;
|
||||
@@ -56,22 +62,22 @@ function multi_callback (self, err, replies) {
|
||||
}
|
||||
|
||||
if (replies) {
|
||||
while (args = self.queue.shift()) {
|
||||
while (command_obj = self.queue.shift()) {
|
||||
if (replies[i] instanceof Error) {
|
||||
var match = replies[i].message.match(utils.err_code);
|
||||
// LUA script could return user errors that don't behave like all other errors!
|
||||
if (match) {
|
||||
replies[i].code = match[1];
|
||||
}
|
||||
replies[i].command = args[0].toUpperCase();
|
||||
if (typeof args[2] === 'function') {
|
||||
args[2](replies[i]);
|
||||
replies[i].command = command_obj.command.toUpperCase();
|
||||
if (typeof command_obj.callback === 'function') {
|
||||
command_obj.callback(replies[i]);
|
||||
}
|
||||
} else {
|
||||
// If we asked for strings, even in detect_buffers mode, then return strings:
|
||||
replies[i] = self._client.handle_reply(replies[i], args[0], self.wants_buffers[i]);
|
||||
if (typeof args[2] === 'function') {
|
||||
args[2](null, replies[i]);
|
||||
replies[i] = self._client.handle_reply(replies[i], command_obj.command, self.wants_buffers[i]);
|
||||
if (typeof command_obj.callback === 'function') {
|
||||
command_obj.callback(null, replies[i]);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
@@ -98,30 +104,16 @@ Multi.prototype.exec_transaction = function exec_transaction (callback) {
|
||||
self.callback = callback;
|
||||
self._client.cork();
|
||||
self.wants_buffers = new Array(len);
|
||||
pipeline_transaction_command(self, 'multi', [], -1);
|
||||
pipeline_transaction_command(self, new Command('multi', []), -1);
|
||||
// Drain queue, callback will catch 'QUEUED' or error
|
||||
for (var index = 0; index < len; index++) {
|
||||
// The commands may not be shifted off, since they are needed in the result handler
|
||||
var command_obj = self.queue.get(index);
|
||||
var command = command_obj[0];
|
||||
var cb = command_obj[2];
|
||||
var call_on_write = command_obj.length === 4 ? command_obj[3] : undefined;
|
||||
// Keep track of who wants buffer responses:
|
||||
if (self._client.options.detect_buffers) {
|
||||
self.wants_buffers[index] = false;
|
||||
for (var i = 0; i < command_obj[1].length; i += 1) {
|
||||
if (command_obj[1][i] instanceof Buffer) {
|
||||
self.wants_buffers[index] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pipeline_transaction_command(self, command, command_obj[1], index, cb, call_on_write);
|
||||
pipeline_transaction_command(self, self.queue.get(index), index);
|
||||
}
|
||||
|
||||
self._client.internal_send_command('exec', [], function (err, replies) {
|
||||
self._client.internal_send_command(new Command('exec', [], function (err, replies) {
|
||||
multi_callback(self, err, replies);
|
||||
});
|
||||
}));
|
||||
self._client.uncork();
|
||||
return !self._client.should_buffer;
|
||||
};
|
||||
@@ -144,16 +136,17 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct
|
||||
var len = self.queue.length;
|
||||
var index = 0;
|
||||
var command_obj;
|
||||
if (len === 0) {
|
||||
utils.reply_in_order(self._client, callback, null, []);
|
||||
return !self._client.should_buffer;
|
||||
}
|
||||
self._client.cork();
|
||||
if (!callback) {
|
||||
while (command_obj = self.queue.shift()) {
|
||||
self._client.internal_send_command(command_obj[0], command_obj[1], command_obj[2], (command_obj.length === 4 ? command_obj[3] : undefined));
|
||||
self._client.internal_send_command(command_obj);
|
||||
}
|
||||
self._client.uncork();
|
||||
return !self._client.should_buffer;
|
||||
} else if (len === 0) {
|
||||
utils.reply_in_order(self._client, callback, null, []);
|
||||
return !self._client.should_buffer;
|
||||
}
|
||||
var callback_without_own_cb = function (err, res) {
|
||||
if (err) {
|
||||
@@ -175,18 +168,15 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct
|
||||
};
|
||||
self.results = [];
|
||||
while (command_obj = self.queue.shift()) {
|
||||
var command = command_obj[0];
|
||||
var call_on_write = command_obj.length === 4 ? command_obj[3] : undefined;
|
||||
var cb;
|
||||
if (typeof command_obj[2] === 'function') {
|
||||
cb = batch_callback(self, command_obj[2], index);
|
||||
if (typeof command_obj.callback === 'function') {
|
||||
command_obj.callback = batch_callback(self, command_obj.callback, index);
|
||||
} else {
|
||||
cb = callback_without_own_cb;
|
||||
command_obj.callback = callback_without_own_cb;
|
||||
}
|
||||
if (typeof callback === 'function' && index === len - 1) {
|
||||
cb = last_callback(cb);
|
||||
command_obj.callback = last_callback(command_obj.callback);
|
||||
}
|
||||
this._client.internal_send_command(command, command_obj[1], cb, call_on_write);
|
||||
this._client.internal_send_command(command_obj);
|
||||
index++;
|
||||
}
|
||||
self._client.uncork();
|
||||
|
@@ -56,9 +56,9 @@ describe("The 'info' method", function () {
|
||||
|
||||
it('check redis v.2.4 support', function (done) {
|
||||
var end = helper.callFuncAfter(done, 2);
|
||||
client.internal_send_command = function (command, args, callback) {
|
||||
assert.strictEqual(args.length, 0);
|
||||
assert.strictEqual(command, 'info');
|
||||
client.internal_send_command = function (command_obj) {
|
||||
assert.strictEqual(command_obj.args.length, 0);
|
||||
assert.strictEqual(command_obj.command, 'info');
|
||||
end();
|
||||
};
|
||||
client.info();
|
||||
|
@@ -692,6 +692,11 @@ describe("The 'multi' method", function () {
|
||||
// subscribe => enters subscribe mode and this does not work in combination with exec (the same for psubscribe, unsubscribe...)
|
||||
//
|
||||
|
||||
// Make sure send_command is not called
|
||||
client.send_command = function () {
|
||||
throw new Error('failed');
|
||||
};
|
||||
|
||||
assert.strictEqual(client.selected_db, undefined);
|
||||
var multi = client.multi();
|
||||
multi.select(5, function (err, res) {
|
||||
|
@@ -325,6 +325,7 @@ describe('The node_redis client', function () {
|
||||
bclient.blpop('blocking list 2', 5, function (err, value) {
|
||||
assert.strictEqual(value[0], 'blocking list 2');
|
||||
assert.strictEqual(value[1], 'initial value');
|
||||
bclient.end(true);
|
||||
done(err);
|
||||
});
|
||||
bclient.once('ready', function () {
|
||||
|
@@ -215,6 +215,7 @@ describe('publish/subscribe', function () {
|
||||
sub.stream.end();
|
||||
});
|
||||
|
||||
sub.select(3);
|
||||
sub.subscribe(channels);
|
||||
|
||||
sub.on('ready', function (err, results) {
|
||||
|
Reference in New Issue
Block a user