Skip to content

Commit

Permalink
perf: improve the performance of checking flags (#312)
Browse files Browse the repository at this point in the history
  • Loading branch information
luin committed May 29, 2016
1 parent 5c2e119 commit 236da27
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 13 deletions.
4 changes: 2 additions & 2 deletions lib/cluster/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,8 +423,8 @@ Cluster.prototype.sendCommand = function (command, stream, node) {
if (_this.status === 'ready' || (command.name === 'cluster')) {
if (node && node.redis) {
redis = node.redis;
} else if (_.includes(Command.FLAGS.ENTER_SUBSCRIBER_MODE, command.name) ||
_.includes(Command.FLAGS.EXIT_SUBSCRIBER_MODE, command.name)) {
} else if (Command.checkFlag('ENTER_SUBSCRIBER_MODE', command.name) ||
Command.checkFlag('EXIT_SUBSCRIBER_MODE', command.name)) {
redis = _this.subscriber;
} else {
if (!random) {
Expand Down
22 changes: 19 additions & 3 deletions lib/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,6 @@ Command.prototype.transformReply = function (result) {
};

Command.FLAGS = {
// Commands that can be processed when Redis is loading data from disk
VALID_WHEN_LOADING: ['info', 'auth', 'select', 'subscribe', 'unsubscribe', 'psubscribe',
'pubsubscribe', 'publish', 'shutdown', 'replconf', 'role', 'pubsub', 'command', 'latency'],
// Commands that can be processed when client is in the subscriber mode
VALID_IN_SUBSCRIBER_MODE: ['subscribe', 'psubscribe', 'unsubscribe', 'punsubscribe', 'ping', 'quit'],
// Commands that are valid in monitor mode
Expand All @@ -243,6 +240,25 @@ Command.FLAGS = {
WILL_DISCONNECT: ['quit']
};

var flagMap = Object.keys(Command.FLAGS).reduce(function (map, flagName) {
map[flagName] = {};
Command.FLAGS[flagName].forEach(function (commandName) {
map[flagName][commandName] = true;
});
return map;
}, {});

/**
* Check whether the command has the flag
*
* @param {string} flagName
* @param {string} commandName
* @return {boolean}
*/
Command.checkFlag = function (flagName, commandName) {
return !!flagMap[flagName][commandName];
};

Command._transformer = {
argument: {},
reply: {}
Expand Down
7 changes: 4 additions & 3 deletions lib/redis.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var debug = require('debug')('ioredis:redis');
var Connector = require('./connectors/connector');
var SentinelConnector = require('./connectors/sentinel_connector');
var ScanStream = require('./scan_stream');
var commands = require('redis-commands');

/**
* Creates a Redis instance
Expand Down Expand Up @@ -530,13 +531,13 @@ Redis.prototype.sendCommand = function (command, stream) {
command.reject(new Error('Connection is closed.'));
return command.promise;
}
if (this.condition.subscriber && !_.includes(Command.FLAGS.VALID_IN_SUBSCRIBER_MODE, command.name)) {
if (this.condition.subscriber && !Command.checkFlag('VALID_IN_SUBSCRIBER_MODE', command.name)) {
command.reject(new Error('Connection in subscriber mode, only subscriber commands may be used'));
return command.promise;
}

var writable = (this.status === 'ready') ||
((this.status === 'connect') && _.includes(Command.FLAGS.VALID_WHEN_LOADING, command.name));
((this.status === 'connect') && commands.hasFlag(command.name, 'loading'));
if (!this.stream) {
writable = false;
} else if (!this.stream.writable) {
Expand All @@ -561,7 +562,7 @@ Redis.prototype.sendCommand = function (command, stream) {
select: this.condition.select
});

if (_.includes(Command.FLAGS.WILL_DISCONNECT, command.name)) {
if (Command.checkFlag('WILL_DISCONNECT', command.name)) {
this.manuallyClosing = true;
}
} else if (this.options.enableOfflineQueue) {
Expand Down
3 changes: 1 addition & 2 deletions lib/redis/event_handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

var debug = require('debug')('ioredis:connection');
var Command = require('../command');
var _ = require('lodash');

exports.connectHandler = function (self) {
return function () {
Expand Down Expand Up @@ -123,7 +122,7 @@ exports.readyHandler = function (self) {
self.call('monitor');
var sendCommand = self.sendCommand;
self.sendCommand = function (command) {
if (_.includes(Command.FLAGS.VALID_IN_MONITOR_MODE, command.name)) {
if (Command.checkFlag('VALID_IN_MONITOR_MODE', command.name)) {
return sendCommand.call(self, command);
}
command.reject(new Error('Connection is in monitoring mode, can\'t process commands.'));
Expand Down
7 changes: 4 additions & 3 deletions lib/redis/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,16 +158,17 @@ exports.returnReply = function (reply) {
item = this.commandQueue.shift();
if (!item) {
return this.emit('error',
new Error('Command queue state error. If you can reproduce this, please report it. Last reply: ' + reply.toString()));
new Error('Command queue state error. If you can reproduce this, please report it. Last reply: ' +
reply.toString()));
}
if (_.includes(Command.FLAGS.ENTER_SUBSCRIBER_MODE, item.command.name)) {
if (Command.checkFlag('ENTER_SUBSCRIBER_MODE', item.command.name)) {
this.condition.subscriber = new SubscriptionSet();
this.condition.subscriber.add(item.command.name, reply[1].toString());

if (!fillSubCommand(item.command, reply[2])) {
this.commandQueue.unshift(item);
}
} else if (_.includes(Command.FLAGS.EXIT_SUBSCRIBER_MODE, item.command.name)) {
} else if (Command.checkFlag('EXIT_SUBSCRIBER_MODE', item.command.name)) {
if (!fillUnsubCommand(item.command, reply[2])) {
this.commandQueue.unshift(item);
}
Expand Down
20 changes: 20 additions & 0 deletions test/functional/send_command.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,24 @@ describe('send command', function () {
});
});
});

it('should allow sending the loading valid commands in connect event', function (done) {
var redis = new Redis({ enableOfflineQueue: false });
redis.on('connect', function () {
redis.select(2, function (err, res) {
expect(res).to.eql('OK');
done();
});
});
});

it('should reject loading invalid commands in connect event', function (done) {
var redis = new Redis({ enableOfflineQueue: false });
redis.on('connect', function () {
redis.get('foo', function (err) {
expect(err.message).to.eql('Stream isn\'t writeable and enableOfflineQueue options is false');
done();
});
});
});
});
8 changes: 8 additions & 0 deletions test/unit/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,12 @@ describe('Command', function () {
}
});
});

describe('.checkFlag()', function () {
it('should return correct result', function () {
expect(Command.checkFlag('VALID_IN_SUBSCRIBER_MODE', 'ping')).to.eql(true);
expect(Command.checkFlag('VALID_IN_SUBSCRIBER_MODE', 'get')).to.eql(false);
expect(Command.checkFlag('WILL_DISCONNECT', 'quit')).to.eql(true);
});
});
});

0 comments on commit 236da27

Please sign in to comment.