From c3d091614e18ee01de1637476f155f4e7b98129f Mon Sep 17 00:00:00 2001 From: JT Date: Wed, 21 Mar 2018 11:52:20 +0000 Subject: [PATCH] Add a `keyless` flag to designate commands that don't take keys ... rather than checking interval===0 which isn't true for commands with movable keys (e.g. zunionstore). For keyless commands we just address a random node, which was always the intention, but this actually makes that work. Fixes #25 For those movable-keys commands we just go by the first key given. And if redis complains then it'll fail with a redis error. Fixes #26. This also addresses #21 inasmuch as the command shouldn't error any more. However, MONITOR should perhaps get special treatment rather than just addressing a random node --- config/commands.js | 811 +++++++++++++++++++++++++++------------------ src/RedisClustr.js | 4 +- tools/commands.js | 6 + 3 files changed, 495 insertions(+), 326 deletions(-) diff --git a/config/commands.js b/config/commands.js index b948618..76ba611 100644 --- a/config/commands.js +++ b/config/commands.js @@ -1,835 +1,998 @@ -// Generated using tools/commands.js and config/commandsConfig.js on Sun, 22 Nov 2015 23:03:06 GMT +// Generated using tools/commands.js and config/commandsConfig.js on Wed, 21 Mar 2018 11:49:17 GMT module.exports = { - lindex: { + append: { multiKey: false, interval: 1, + keyless: false, + readOnly: false + }, + asking: { + multiKey: false, + interval: 0, + keyless: true, readOnly: true }, - zrank: { + auth: { multiKey: false, - interval: 1, + interval: 0, + keyless: true, readOnly: true }, - zinterstore: { + bgrewriteaof: { multiKey: false, interval: 0, - readOnly: false + keyless: true, + readOnly: true }, - persist: { + bgsave: { + multiKey: false, + interval: 0, + keyless: true, + readOnly: true + }, + bitcount: { multiKey: false, interval: 1, + keyless: false, + readOnly: true + }, + bitop: { + multiKey: true, + interval: 1, + keyless: false, readOnly: false }, - getrange: { + bitpos: { multiKey: false, interval: 1, + keyless: false, readOnly: true }, - brpoplpush: { + blpop: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - zscore: { + brpop: { multiKey: false, interval: 1, - readOnly: true + keyless: false, + readOnly: false }, - zrevrangebylex: { + brpoplpush: { multiKey: false, interval: 1, + keyless: false, + readOnly: false + }, + client: { + multiKey: false, + interval: 0, + keyless: true, readOnly: true }, - replconf: { + cluster: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - sadd: { + command: { multiKey: false, - interval: 1, - readOnly: false + interval: 0, + keyless: true, + readOnly: true }, - pfadd: { + config: { multiKey: false, - interval: 1, - readOnly: false + interval: 0, + keyless: true, + readOnly: true }, - getbit: { + dbsize: { multiKey: false, - interval: 1, + interval: 0, + keyless: true, readOnly: true }, - zincrby: { + debug: { multiKey: false, - interval: 1, + interval: 0, + keyless: true, readOnly: false }, - hgetall: { + decr: { multiKey: false, interval: 1, - readOnly: true + keyless: false, + readOnly: false }, - zremrangebyscore: { + decrby: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - type: { - multiKey: false, + del: { + group: function(resp) { + var total = 0; + for (var i = 0; i < resp.length; i++) { + total += (resp[i] || 0); + } + return total; + }, + multiKey: true, interval: 1, - readOnly: true + keyless: false, + readOnly: false }, - cluster: { + discard: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - zrange: { + dump: { multiKey: false, interval: 1, + keyless: false, readOnly: true }, - debug: { + echo: { + multiKey: false, + interval: 0, + keyless: true, + readOnly: true + }, + eval: { multiKey: false, interval: 0, + keyless: false, readOnly: false }, - bitcount: { + evalsha: { multiKey: false, - interval: 1, - readOnly: true + interval: 0, + keyless: false, + readOnly: false }, - flushdb: { + exec: { multiKey: false, interval: 0, + keyless: true, readOnly: false }, - sunionstore: { + exists: { multiKey: true, interval: 1, - readOnly: false + keyless: false, + readOnly: true }, - smove: { + expire: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - rpushx: { + expireat: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - zrangebylex: { + flushall: { multiKey: false, - interval: 1, - readOnly: true + interval: 0, + keyless: true, + readOnly: false }, - multi: { + flushdb: { multiKey: false, interval: 0, - readOnly: true - }, - sdiff: { - multiKey: true, - interval: 1, - readOnly: true + keyless: true, + readOnly: false }, - hscan: { + get: { multiKey: false, interval: 1, + keyless: false, readOnly: true }, - zrevrank: { + getbit: { multiKey: false, interval: 1, + keyless: false, readOnly: true }, - punsubscribe: { + getrange: { multiKey: false, - interval: 0, + interval: 1, + keyless: false, readOnly: true }, - lset: { + getset: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - psetex: { + hdel: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - hvals: { + hexists: { multiKey: false, interval: 1, + keyless: false, readOnly: true }, - zrem: { + hget: { multiKey: false, interval: 1, - readOnly: false + keyless: false, + readOnly: true }, - smembers: { + hgetall: { multiKey: false, interval: 1, + keyless: false, readOnly: true }, - zscan: { + hincrby: { multiKey: false, interval: 1, - readOnly: true + keyless: false, + readOnly: false }, - dbsize: { + hincrbyfloat: { multiKey: false, - interval: 0, + interval: 1, + keyless: false, + readOnly: false + }, + hkeys: { + multiKey: false, + interval: 1, + keyless: false, readOnly: true }, - sinterstore: { - multiKey: true, + hlen: { + multiKey: false, interval: 1, - readOnly: false + keyless: false, + readOnly: true }, - lastsave: { + hmget: { multiKey: false, - interval: 0, + interval: 1, + keyless: false, readOnly: true }, - evalsha: { + hmset: { multiKey: false, - interval: 0, + interval: 1, + keyless: false, readOnly: false }, - scan: { + hscan: { multiKey: false, - interval: 0, + interval: 1, + keyless: false, readOnly: true }, - unsubscribe: { + hset: { multiKey: false, - interval: 0, - readOnly: true + interval: 1, + keyless: false, + readOnly: false }, - setex: { + hsetnx: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - scard: { + hvals: { multiKey: false, interval: 1, + keyless: false, readOnly: true }, - ping: { + incr: { multiKey: false, - interval: 0, - readOnly: true + interval: 1, + keyless: false, + readOnly: false }, - bitpos: { + incrby: { multiKey: false, interval: 1, - readOnly: true + keyless: false, + readOnly: false }, - role: { + incrbyfloat: { multiKey: false, - interval: 0, + interval: 1, + keyless: false, readOnly: false }, - psubscribe: { + info: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - wait: { + keys: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - config: { + lastsave: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - publish: { + latency: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - sdiffstore: { - multiKey: true, - interval: 1, - readOnly: false - }, - lrange: { + lindex: { multiKey: false, interval: 1, + keyless: false, readOnly: true }, - hsetnx: { + linsert: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - asking: { + llen: { multiKey: false, - interval: 0, + interval: 1, + keyless: false, readOnly: true }, - decr: { + lpop: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - client: { - multiKey: false, - interval: 0, - readOnly: true - }, - linsert: { + lpush: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - spop: { + lpushx: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - subscribe: { + lrange: { multiKey: false, - interval: 0, + interval: 1, + keyless: false, readOnly: true }, - lpushx: { + lrem: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - ltrim: { + lset: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - migrate: { - multiKey: false, - interval: 0, - readOnly: false - }, - llen: { + ltrim: { multiKey: false, interval: 1, - readOnly: true + keyless: false, + readOnly: false }, - zlexcount: { - multiKey: false, + mget: { + group: function(resp) { + return resp.map(function(r) { + if (!r) return r; + return r[0]; + }); + }, + multiKey: true, interval: 1, + keyless: false, readOnly: true }, - psync: { + migrate: { multiKey: false, interval: 0, - readOnly: true - }, - 'restore-asking': { - multiKey: false, - interval: 1, + keyless: false, readOnly: false }, - save: { - multiKey: false, - interval: 0, - readOnly: true - }, - latency: { + monitor: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - setnx: { + move: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - auth: { - multiKey: false, - interval: 0, - readOnly: true - }, - hmget: { - multiKey: false, - interval: 1, - readOnly: true - }, - sinter: { + mset: { + group: function() { + return 'OK'; + }, multiKey: true, - interval: 1, - readOnly: true + interval: 2, + keyless: false, + readOnly: false }, - watch: { + msetnx: { multiKey: true, - interval: 1, - readOnly: true + interval: 2, + keyless: false, + readOnly: false }, - strlen: { + multi: { multiKey: false, - interval: 1, + interval: 0, + keyless: true, readOnly: true }, - sync: { + object: { multiKey: false, - interval: 0, + interval: 2, + keyless: false, readOnly: true }, - zrangebyscore: { + persist: { multiKey: false, interval: 1, - readOnly: true + keyless: false, + readOnly: false }, - bitop: { - multiKey: true, + pexpire: { + multiKey: false, interval: 1, + keyless: false, readOnly: false }, - msetnx: { - multiKey: true, - interval: 2, + pexpireat: { + multiKey: false, + interval: 1, + keyless: false, readOnly: false }, - hmset: { + pfadd: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - pfmerge: { + pfcount: { multiKey: true, interval: 1, - readOnly: false + keyless: false, + readOnly: true }, - readwrite: { + pfdebug: { multiKey: false, interval: 0, - readOnly: true + keyless: true, + readOnly: false }, - sort: { - multiKey: false, + pfmerge: { + multiKey: true, interval: 1, + keyless: false, readOnly: false }, - monitor: { + pfselftest: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - randomkey: { + ping: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - incr: { + psetex: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - mget: { - group: function(resp) { - return resp.map(function(r) { - if (!r) return r; - return r[0]; - }); - }, - multiKey: true, - interval: 1, + psubscribe: { + multiKey: false, + interval: 0, + keyless: true, readOnly: true }, - hincrby: { + psync: { multiKey: false, - interval: 1, - readOnly: false + interval: 0, + keyless: true, + readOnly: true }, - srandmember: { + pttl: { multiKey: false, interval: 1, + keyless: false, readOnly: true }, - zremrangebyrank: { + publish: { multiKey: false, - interval: 1, - readOnly: false + interval: 0, + keyless: true, + readOnly: true }, - script: { + pubsub: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - srem: { + punsubscribe: { multiKey: false, - interval: 1, - readOnly: false + interval: 0, + keyless: true, + readOnly: true }, - setrange: { + randomkey: { multiKey: false, - interval: 1, - readOnly: false - }, - mset: { - group: function() { - return 'OK'; - }, - multiKey: true, - interval: 2, - readOnly: false + interval: 0, + keyless: true, + readOnly: true }, - flushall: { + readonly: { multiKey: false, interval: 0, - readOnly: false + keyless: true, + readOnly: true }, - blpop: { + readwrite: { + multiKey: false, + interval: 0, + keyless: true, + readOnly: true + }, + rename: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, renamenx: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - select: { + replconf: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - bgrewriteaof: { + restore: { multiKey: false, - interval: 0, - readOnly: true + interval: 1, + keyless: false, + readOnly: false }, - zcount: { + 'restore-asking': { multiKey: false, interval: 1, - readOnly: true + keyless: false, + readOnly: false }, - substr: { + role: { multiKey: false, - interval: 1, - readOnly: true + interval: 0, + keyless: true, + readOnly: false }, - sismember: { + rpop: { multiKey: false, interval: 1, - readOnly: true + keyless: false, + readOnly: false }, - incrby: { + rpoplpush: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - hget: { + rpush: { multiKey: false, interval: 1, - readOnly: true + keyless: false, + readOnly: false }, - zrevrangebyscore: { + rpushx: { multiKey: false, interval: 1, - readOnly: true + keyless: false, + readOnly: false }, - setbit: { + sadd: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - time: { + save: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - slaveof: { + scan: { multiKey: false, interval: 0, - readOnly: false + keyless: true, + readOnly: true }, - hset: { + scard: { multiKey: false, interval: 1, - readOnly: false + keyless: false, + readOnly: true }, - dump: { + script: { multiKey: false, + interval: 0, + keyless: true, + readOnly: true + }, + sdiff: { + multiKey: true, interval: 1, + keyless: false, readOnly: true }, - move: { - multiKey: false, + sdiffstore: { + multiKey: true, interval: 1, + keyless: false, readOnly: false }, - sscan: { + select: { multiKey: false, - interval: 1, + interval: 0, + keyless: true, readOnly: true }, - append: { + set: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - discard: { + setbit: { multiKey: false, - interval: 0, - readOnly: true + interval: 1, + keyless: false, + readOnly: false }, - lpop: { + setex: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - pexpire: { + setnx: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - pfdebug: { + setrange: { multiKey: false, - interval: 0, + interval: 1, + keyless: false, readOnly: false }, - readonly: { + shutdown: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - get: { - multiKey: false, + sinter: { + multiKey: true, interval: 1, + keyless: false, readOnly: true }, - zadd: { - multiKey: false, + sinterstore: { + multiKey: true, interval: 1, + keyless: false, readOnly: false }, - hkeys: { + sismember: { multiKey: false, interval: 1, + keyless: false, readOnly: true }, - restore: { + slaveof: { multiKey: false, - interval: 1, + interval: 0, + keyless: true, readOnly: false }, - exec: { + slowlog: { multiKey: false, interval: 0, - readOnly: false + keyless: true, + readOnly: true }, - eval: { + smembers: { multiKey: false, - interval: 0, - readOnly: false + interval: 1, + keyless: false, + readOnly: true }, - set: { + smove: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - expire: { + sort: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - zremrangebylex: { + spop: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - pubsub: { + srandmember: { multiKey: false, - interval: 0, + interval: 1, + keyless: false, readOnly: true }, - zcard: { + srem: { multiKey: false, interval: 1, - readOnly: true + keyless: false, + readOnly: false }, - object: { + sscan: { multiKey: false, - interval: 2, + interval: 1, + keyless: false, readOnly: true }, - unwatch: { + strlen: { multiKey: false, - interval: 0, + interval: 1, + keyless: false, readOnly: true }, - keys: { + subscribe: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - hdel: { + substr: { multiKey: false, interval: 1, + keyless: false, + readOnly: true + }, + sunion: { + multiKey: true, + interval: 1, + keyless: false, + readOnly: true + }, + sunionstore: { + multiKey: true, + interval: 1, + keyless: false, readOnly: false }, - echo: { + sync: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - pfselftest: { + time: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - brpop: { + ttl: { multiKey: false, interval: 1, - readOnly: false + keyless: false, + readOnly: true }, - pttl: { + type: { multiKey: false, interval: 1, + keyless: false, readOnly: true }, - hincrbyfloat: { + unsubscribe: { multiKey: false, - interval: 1, - readOnly: false + interval: 0, + keyless: true, + readOnly: true }, - hlen: { + unwatch: { multiKey: false, - interval: 1, + interval: 0, + keyless: true, readOnly: true }, - slowlog: { + wait: { multiKey: false, interval: 0, + keyless: true, readOnly: true }, - hexists: { - multiKey: false, + watch: { + multiKey: true, interval: 1, + keyless: false, readOnly: true }, - getset: { + zadd: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - lpush: { + zcard: { multiKey: false, interval: 1, - readOnly: false + keyless: false, + readOnly: true }, - info: { + zcount: { multiKey: false, - interval: 0, + interval: 1, + keyless: false, readOnly: true }, - rpoplpush: { + zincrby: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - zunionstore: { + zinterstore: { multiKey: false, interval: 0, + keyless: false, readOnly: false }, - lrem: { + zlexcount: { multiKey: false, interval: 1, - readOnly: false + keyless: false, + readOnly: true }, - rpush: { + zrange: { multiKey: false, interval: 1, - readOnly: false + keyless: false, + readOnly: true }, - pexpireat: { + zrangebylex: { multiKey: false, interval: 1, - readOnly: false + keyless: false, + readOnly: true }, - zrevrange: { + zrangebyscore: { multiKey: false, interval: 1, + keyless: false, readOnly: true }, - ttl: { + zrank: { multiKey: false, interval: 1, + keyless: false, readOnly: true }, - del: { - group: function(resp) { - var total = 0; - for (var i = 0; i < resp.length; i++) { - total += (resp[i] || 0); - } - return total; - }, - multiKey: true, + zrem: { + multiKey: false, interval: 1, + keyless: false, readOnly: false }, - rename: { + zremrangebylex: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - bgsave: { + zremrangebyrank: { multiKey: false, - interval: 0, - readOnly: true + interval: 1, + keyless: false, + readOnly: false }, - decrby: { + zremrangebyscore: { multiKey: false, interval: 1, + keyless: false, readOnly: false }, - sunion: { - multiKey: true, + zrevrange: { + multiKey: false, interval: 1, + keyless: false, readOnly: true }, - shutdown: { + zrevrangebylex: { multiKey: false, - interval: 0, + interval: 1, + keyless: false, readOnly: true }, - incrbyfloat: { + zrevrangebyscore: { multiKey: false, interval: 1, - readOnly: false - }, - pfcount: { - multiKey: true, - interval: 1, + keyless: false, readOnly: true }, - command: { + zrevrank: { multiKey: false, - interval: 0, + interval: 1, + keyless: false, readOnly: true }, - exists: { - multiKey: true, + zscan: { + multiKey: false, interval: 1, + keyless: false, readOnly: true }, - rpop: { + zscore: { multiKey: false, interval: 1, - readOnly: false + keyless: false, + readOnly: true }, - expireat: { + zunionstore: { multiKey: false, - interval: 1, + interval: 0, + keyless: false, readOnly: false } }; diff --git a/src/RedisClustr.js b/src/RedisClustr.js index cc5154b..662a1ff 100644 --- a/src/RedisClustr.js +++ b/src/RedisClustr.js @@ -270,7 +270,7 @@ RedisClustr.prototype.selectClient = function(key, conf) { // this command doesnt have keys, return any connection // NOTE: this means slaves may be used for no key commands regardless of slave config - if (conf.interval === 0) return self.getRandomConnection(); + if (conf.keyless) return self.getRandomConnection(); if (Array.isArray(key)) key = key[0]; if (Buffer.isBuffer(key)) key = key.toString(); @@ -359,7 +359,7 @@ RedisClustr.prototype.doCommand = function(cmd, conf, args) { self.parseArgs(args, function(_, args, cb) { var key = args[0]; - if (!key) return cb(new Error('no key for command: ' + cmd)); + if (!key && !conf.keyless) return cb(new Error('no key for command: ' + cmd)); var r = self.selectClient(key, conf); if (!r) return cb(new Error('couldn\'t get client')); diff --git a/tools/commands.js b/tools/commands.js index d24ff9f..e7ccedc 100644 --- a/tools/commands.js +++ b/tools/commands.js @@ -9,11 +9,17 @@ redis.command(function(err, res) { redis.quit(); if (err) return console.error(err); + // Ensure stable sorting of commands to avoid git diff churn every time we regenerate + res.sort(function(a, b) { + return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0; + }); + for (var i = 0; i < res.length; i++) { var c = res[i]; var cnf = extraConfig[c[0]] || {}; cnf.multiKey = c[4] === -1; cnf.interval = c[5]; + cnf.keyless = c[5] === 0 && c[2].indexOf('movablekeys') === -1; cnf.readOnly = c[2].indexOf('readonly') !== -1; commands[c[0]] = cnf; }