Skip to content

Commit 236da27

Browse files
committed
perf: improve the performance of checking flags (#312)
1 parent 5c2e119 commit 236da27

File tree

7 files changed

+58
-13
lines changed

7 files changed

+58
-13
lines changed

lib/cluster/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,8 @@ Cluster.prototype.sendCommand = function (command, stream, node) {
423423
if (_this.status === 'ready' || (command.name === 'cluster')) {
424424
if (node && node.redis) {
425425
redis = node.redis;
426-
} else if (_.includes(Command.FLAGS.ENTER_SUBSCRIBER_MODE, command.name) ||
427-
_.includes(Command.FLAGS.EXIT_SUBSCRIBER_MODE, command.name)) {
426+
} else if (Command.checkFlag('ENTER_SUBSCRIBER_MODE', command.name) ||
427+
Command.checkFlag('EXIT_SUBSCRIBER_MODE', command.name)) {
428428
redis = _this.subscriber;
429429
} else {
430430
if (!random) {

lib/command.js

+19-3
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,6 @@ Command.prototype.transformReply = function (result) {
228228
};
229229

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

243+
var flagMap = Object.keys(Command.FLAGS).reduce(function (map, flagName) {
244+
map[flagName] = {};
245+
Command.FLAGS[flagName].forEach(function (commandName) {
246+
map[flagName][commandName] = true;
247+
});
248+
return map;
249+
}, {});
250+
251+
/**
252+
* Check whether the command has the flag
253+
*
254+
* @param {string} flagName
255+
* @param {string} commandName
256+
* @return {boolean}
257+
*/
258+
Command.checkFlag = function (flagName, commandName) {
259+
return !!flagMap[flagName][commandName];
260+
};
261+
246262
Command._transformer = {
247263
argument: {},
248264
reply: {}

lib/redis.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ var debug = require('debug')('ioredis:redis');
1313
var Connector = require('./connectors/connector');
1414
var SentinelConnector = require('./connectors/sentinel_connector');
1515
var ScanStream = require('./scan_stream');
16+
var commands = require('redis-commands');
1617

1718
/**
1819
* Creates a Redis instance
@@ -530,13 +531,13 @@ Redis.prototype.sendCommand = function (command, stream) {
530531
command.reject(new Error('Connection is closed.'));
531532
return command.promise;
532533
}
533-
if (this.condition.subscriber && !_.includes(Command.FLAGS.VALID_IN_SUBSCRIBER_MODE, command.name)) {
534+
if (this.condition.subscriber && !Command.checkFlag('VALID_IN_SUBSCRIBER_MODE', command.name)) {
534535
command.reject(new Error('Connection in subscriber mode, only subscriber commands may be used'));
535536
return command.promise;
536537
}
537538

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

564-
if (_.includes(Command.FLAGS.WILL_DISCONNECT, command.name)) {
565+
if (Command.checkFlag('WILL_DISCONNECT', command.name)) {
565566
this.manuallyClosing = true;
566567
}
567568
} else if (this.options.enableOfflineQueue) {

lib/redis/event_handler.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
var debug = require('debug')('ioredis:connection');
44
var Command = require('../command');
5-
var _ = require('lodash');
65

76
exports.connectHandler = function (self) {
87
return function () {
@@ -123,7 +122,7 @@ exports.readyHandler = function (self) {
123122
self.call('monitor');
124123
var sendCommand = self.sendCommand;
125124
self.sendCommand = function (command) {
126-
if (_.includes(Command.FLAGS.VALID_IN_MONITOR_MODE, command.name)) {
125+
if (Command.checkFlag('VALID_IN_MONITOR_MODE', command.name)) {
127126
return sendCommand.call(self, command);
128127
}
129128
command.reject(new Error('Connection is in monitoring mode, can\'t process commands.'));

lib/redis/parser.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -158,16 +158,17 @@ exports.returnReply = function (reply) {
158158
item = this.commandQueue.shift();
159159
if (!item) {
160160
return this.emit('error',
161-
new Error('Command queue state error. If you can reproduce this, please report it. Last reply: ' + reply.toString()));
161+
new Error('Command queue state error. If you can reproduce this, please report it. Last reply: ' +
162+
reply.toString()));
162163
}
163-
if (_.includes(Command.FLAGS.ENTER_SUBSCRIBER_MODE, item.command.name)) {
164+
if (Command.checkFlag('ENTER_SUBSCRIBER_MODE', item.command.name)) {
164165
this.condition.subscriber = new SubscriptionSet();
165166
this.condition.subscriber.add(item.command.name, reply[1].toString());
166167

167168
if (!fillSubCommand(item.command, reply[2])) {
168169
this.commandQueue.unshift(item);
169170
}
170-
} else if (_.includes(Command.FLAGS.EXIT_SUBSCRIBER_MODE, item.command.name)) {
171+
} else if (Command.checkFlag('EXIT_SUBSCRIBER_MODE', item.command.name)) {
171172
if (!fillUnsubCommand(item.command, reply[2])) {
172173
this.commandQueue.unshift(item);
173174
}

test/functional/send_command.js

+20
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,24 @@ describe('send command', function () {
187187
});
188188
});
189189
});
190+
191+
it('should allow sending the loading valid commands in connect event', function (done) {
192+
var redis = new Redis({ enableOfflineQueue: false });
193+
redis.on('connect', function () {
194+
redis.select(2, function (err, res) {
195+
expect(res).to.eql('OK');
196+
done();
197+
});
198+
});
199+
});
200+
201+
it('should reject loading invalid commands in connect event', function (done) {
202+
var redis = new Redis({ enableOfflineQueue: false });
203+
redis.on('connect', function () {
204+
redis.get('foo', function (err) {
205+
expect(err.message).to.eql('Stream isn\'t writeable and enableOfflineQueue options is false');
206+
done();
207+
});
208+
});
209+
});
190210
});

test/unit/command.js

+8
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,12 @@ describe('Command', function () {
7373
}
7474
});
7575
});
76+
77+
describe('.checkFlag()', function () {
78+
it('should return correct result', function () {
79+
expect(Command.checkFlag('VALID_IN_SUBSCRIBER_MODE', 'ping')).to.eql(true);
80+
expect(Command.checkFlag('VALID_IN_SUBSCRIBER_MODE', 'get')).to.eql(false);
81+
expect(Command.checkFlag('WILL_DISCONNECT', 'quit')).to.eql(true);
82+
});
83+
});
7684
});

0 commit comments

Comments
 (0)