You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-04 15:02:09 +03:00
Add prefix option
Fixes #323 Add key prefix tests Add changelog entry for prefix
This commit is contained in:
@@ -214,6 +214,7 @@ limits total amount of connection tries. Setting this to 1 will prevent any reco
|
||||
* `rename_commands`: *null*; pass a object with renamed commands to use those instead of the original functions. See the [redis security topics](http://redis.io/topics/security) for more info.
|
||||
* `tls`: an object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback),
|
||||
to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel).
|
||||
* `prefix`: *null*; pass a string to prefix all used keys with that string as prefix e.g. 'namespace:test'
|
||||
|
||||
```js
|
||||
var redis = require("redis"),
|
||||
|
@@ -5,10 +5,11 @@ Changelog
|
||||
|
||||
Features
|
||||
|
||||
- Added `tls` option to iniate a connection to a redis server behind a TLS proxy. Thanks ([@paddybyers](https://github.com/paddybyers)
|
||||
- Added a *url* option to pass the connection url with the options object ([@BridgeAR](https://github.com/BridgeAR)
|
||||
- Added `client.duplicate([options])` to duplicate the current client and return a new one with the same options ([@BridgeAR](https://github.com/BridgeAR)
|
||||
- Improve performance by up to 20% on almost all use cases ([@BridgeAR](https://github.com/BridgeAR)
|
||||
- Added `tls` option to iniate a connection to a redis server behind a TLS proxy. Thanks ([@paddybyers](https://github.com/paddybyers))
|
||||
- Added `prefix` option to auto key prefix any command with the provided prefix (([@luin](https://github.com/luin) & [@BridgeAR](https://github.com/BridgeAR)))
|
||||
- Added `url` option to pass the connection url with the options object ([@BridgeAR](https://github.com/BridgeAR))
|
||||
- Added `client.duplicate([options])` to duplicate the current client and return a new one with the same options ([@BridgeAR](https://github.com/BridgeAR))
|
||||
- Improve performance by up to 20% on almost all use cases ([@BridgeAR](https://github.com/BridgeAR))
|
||||
|
||||
Bugfixes
|
||||
|
||||
|
@@ -1,41 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var http = require("http"),
|
||||
fs = require("fs");
|
||||
|
||||
function prettyCurrentTime() {
|
||||
var date = new Date();
|
||||
return date.toLocaleString();
|
||||
}
|
||||
|
||||
function write_file(commands, path) {
|
||||
var file_contents, out_commands;
|
||||
|
||||
console.log("Writing " + Object.keys(commands).length + " commands to " + path);
|
||||
|
||||
file_contents = "'use strict';\n\n// This file was generated by ./generate_commands.js on " + prettyCurrentTime() + "\n";
|
||||
|
||||
out_commands = Object.keys(commands).map(function (key) {
|
||||
return key.toLowerCase();
|
||||
});
|
||||
|
||||
file_contents += "module.exports = " + JSON.stringify(out_commands, null, " ") + ";\n";
|
||||
|
||||
fs.writeFile(path, file_contents);
|
||||
}
|
||||
|
||||
http.get({host: "redis.io", path: "/commands.json"}, function (res) {
|
||||
var body = "";
|
||||
|
||||
console.log("Response from redis.io/commands.json: " + res.statusCode);
|
||||
|
||||
res.on('data', function (chunk) {
|
||||
body += chunk;
|
||||
});
|
||||
|
||||
res.on('end', function () {
|
||||
write_file(JSON.parse(body), "lib/commands.js");
|
||||
});
|
||||
}).on('error', function (e) {
|
||||
console.log("Error fetching command list from redis.io: " + e.message);
|
||||
});
|
54
index.js
54
index.js
@@ -9,9 +9,7 @@ var Queue = require('double-ended-queue');
|
||||
var Command = require('./lib/command');
|
||||
var events = require('events');
|
||||
var parsers = [];
|
||||
// This static list of commands is updated from time to time.
|
||||
// ./lib/commands.js can be updated with generate_commands.js
|
||||
var commands = require('./lib/commands');
|
||||
var commands = require('redis-commands');
|
||||
var connection_id = 0;
|
||||
var default_port = 6379;
|
||||
var default_host = '127.0.0.1';
|
||||
@@ -22,7 +20,7 @@ function debug (msg) { if (exports.debug_mode) { console.error(msg); } }
|
||||
|
||||
exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG);
|
||||
|
||||
// hiredis might not be installed
|
||||
// Hiredis might not be installed
|
||||
try {
|
||||
parsers.push(require('./lib/parsers/hiredis'));
|
||||
} catch (err) {
|
||||
@@ -189,7 +187,7 @@ RedisClient.prototype.unref = function () {
|
||||
}
|
||||
};
|
||||
|
||||
// flush provided queues, erroring any items with a callback first
|
||||
// Flush provided queues, erroring any items with a callback first
|
||||
RedisClient.prototype.flush_and_error = function (error, queue_names) {
|
||||
var command_obj;
|
||||
queue_names = queue_names || ['offline_queue', 'command_queue'];
|
||||
@@ -457,7 +455,7 @@ RedisClient.prototype.on_info_cmd = function (err, res) {
|
||||
key = 'db' + i;
|
||||
}
|
||||
|
||||
// expose info key/vals to users
|
||||
// Expose info key/vals to users
|
||||
this.server_info = obj;
|
||||
|
||||
if (!obj.loading || obj.loading === '0') {
|
||||
@@ -684,7 +682,7 @@ RedisClient.prototype.return_reply = function (reply) {
|
||||
} else {
|
||||
this.pub_sub_mode = true;
|
||||
}
|
||||
// subscribe commands take an optional callback and also emit an event, but only the first response is included in the callback
|
||||
// Subscribe commands take an optional callback and also emit an event, but only the first response is included in the callback
|
||||
// TODO - document this or fix it so it works in a more obvious way
|
||||
if (command_obj && typeof command_obj.callback === 'function') {
|
||||
command_obj.callback(null, reply[1]);
|
||||
@@ -723,6 +721,7 @@ RedisClient.prototype.send_command = function (command, args, callback) {
|
||||
command_str = '',
|
||||
buffer_args = false,
|
||||
big_data = false,
|
||||
prefix_keys,
|
||||
buffer = this.options.return_buffers;
|
||||
|
||||
if (args === undefined) {
|
||||
@@ -810,7 +809,14 @@ RedisClient.prototype.send_command = function (command, args, callback) {
|
||||
if (typeof this.options.rename_commands !== 'undefined' && this.options.rename_commands[command]) {
|
||||
command = this.options.rename_commands[command];
|
||||
}
|
||||
|
||||
if (this.options.prefix) {
|
||||
prefix_keys = commands.getKeyIndexes(command, args);
|
||||
i = prefix_keys.pop();
|
||||
while (i !== undefined) {
|
||||
args[i] = this.options.prefix + args[i];
|
||||
i = prefix_keys.pop();
|
||||
}
|
||||
}
|
||||
// Always use 'Multi bulk commands', but if passed any Buffer args, then do multiple writes, one for each arg.
|
||||
// This means that using Buffers in commands is going to be slower, so use Strings if you don't already have a Buffer.
|
||||
command_str = '*' + (args.length + 1) + '\r\n$' + command.length + '\r\n' + command + '\r\n';
|
||||
@@ -943,23 +949,7 @@ function Multi(client, args) {
|
||||
}
|
||||
}
|
||||
|
||||
RedisClient.prototype.multi = RedisClient.prototype.MULTI = function (args) {
|
||||
var multi = new Multi(this, args);
|
||||
multi.exec = multi.EXEC = multi.exec_transaction;
|
||||
return multi;
|
||||
};
|
||||
|
||||
RedisClient.prototype.batch = RedisClient.prototype.BATCH = function (args) {
|
||||
return new Multi(this, args);
|
||||
};
|
||||
|
||||
commands.forEach(function (fullCommand) {
|
||||
var command = fullCommand.split(' ')[0];
|
||||
|
||||
// Skip all full commands that have already been added instead of overwriting them over and over again
|
||||
if (RedisClient.prototype[command]) {
|
||||
return;
|
||||
}
|
||||
commands.list.forEach(function (command) {
|
||||
|
||||
RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command] = function (key, arg, callback) {
|
||||
if (Array.isArray(key)) {
|
||||
@@ -1006,7 +996,17 @@ commands.forEach(function (fullCommand) {
|
||||
};
|
||||
});
|
||||
|
||||
// store db in this.select_db to restore it on reconnect
|
||||
RedisClient.prototype.multi = RedisClient.prototype.MULTI = function (args) {
|
||||
var multi = new Multi(this, args);
|
||||
multi.exec = multi.EXEC = multi.exec_transaction;
|
||||
return multi;
|
||||
};
|
||||
|
||||
RedisClient.prototype.batch = RedisClient.prototype.BATCH = function (args) {
|
||||
return new Multi(this, args);
|
||||
};
|
||||
|
||||
// Store db in this.select_db to restore it on reconnect
|
||||
RedisClient.prototype.select = RedisClient.prototype.SELECT = function (db, callback) {
|
||||
var self = this;
|
||||
return this.send_command('select', [db], function (err, res) {
|
||||
@@ -1138,7 +1138,7 @@ Multi.prototype.exec_transaction = function (callback) {
|
||||
this._client.cork(len + 2);
|
||||
this.wants_buffers = new Array(len);
|
||||
this.send_command('multi', []);
|
||||
// drain queue, callback will catch 'QUEUED' or error
|
||||
// Drain queue, callback will catch 'QUEUED' or error
|
||||
for (var index = 0; index < len; index++) {
|
||||
var args = this.queue.get(index).slice(0);
|
||||
var command = args.shift();
|
||||
|
197
lib/commands.js
197
lib/commands.js
@@ -1,197 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// This file was generated by ./generate_commands.js on Thu Sep 03 2015 02:40:54 GMT+0200 (CEST)
|
||||
module.exports = [
|
||||
"append",
|
||||
"auth",
|
||||
"bgrewriteaof",
|
||||
"bgsave",
|
||||
"bitcount",
|
||||
"bitop",
|
||||
"bitpos",
|
||||
"blpop",
|
||||
"brpop",
|
||||
"brpoplpush",
|
||||
"client kill",
|
||||
"client list",
|
||||
"client getname",
|
||||
"client pause",
|
||||
"client setname",
|
||||
"cluster addslots",
|
||||
"cluster count-failure-reports",
|
||||
"cluster countkeysinslot",
|
||||
"cluster delslots",
|
||||
"cluster failover",
|
||||
"cluster forget",
|
||||
"cluster getkeysinslot",
|
||||
"cluster info",
|
||||
"cluster keyslot",
|
||||
"cluster meet",
|
||||
"cluster nodes",
|
||||
"cluster replicate",
|
||||
"cluster reset",
|
||||
"cluster saveconfig",
|
||||
"cluster set-config-epoch",
|
||||
"cluster setslot",
|
||||
"cluster slaves",
|
||||
"cluster slots",
|
||||
"command",
|
||||
"command count",
|
||||
"command getkeys",
|
||||
"command info",
|
||||
"config get",
|
||||
"config rewrite",
|
||||
"config set",
|
||||
"config resetstat",
|
||||
"dbsize",
|
||||
"debug object",
|
||||
"debug segfault",
|
||||
"decr",
|
||||
"decrby",
|
||||
"del",
|
||||
"discard",
|
||||
"dump",
|
||||
"echo",
|
||||
"eval",
|
||||
"evalsha",
|
||||
"exec",
|
||||
"exists",
|
||||
"expire",
|
||||
"expireat",
|
||||
"flushall",
|
||||
"flushdb",
|
||||
"geoadd",
|
||||
"geohash",
|
||||
"geopos",
|
||||
"geodist",
|
||||
"georadius",
|
||||
"georadiusbymember",
|
||||
"get",
|
||||
"getbit",
|
||||
"getrange",
|
||||
"getset",
|
||||
"hdel",
|
||||
"hexists",
|
||||
"hget",
|
||||
"hgetall",
|
||||
"hincrby",
|
||||
"hincrbyfloat",
|
||||
"hkeys",
|
||||
"hlen",
|
||||
"hmget",
|
||||
"hmset",
|
||||
"hset",
|
||||
"hsetnx",
|
||||
"hstrlen",
|
||||
"hvals",
|
||||
"incr",
|
||||
"incrby",
|
||||
"incrbyfloat",
|
||||
"info",
|
||||
"keys",
|
||||
"lastsave",
|
||||
"lindex",
|
||||
"linsert",
|
||||
"llen",
|
||||
"lpop",
|
||||
"lpush",
|
||||
"lpushx",
|
||||
"lrange",
|
||||
"lrem",
|
||||
"lset",
|
||||
"ltrim",
|
||||
"mget",
|
||||
"migrate",
|
||||
"monitor",
|
||||
"move",
|
||||
"mset",
|
||||
"msetnx",
|
||||
"multi",
|
||||
"object",
|
||||
"persist",
|
||||
"pexpire",
|
||||
"pexpireat",
|
||||
"pfadd",
|
||||
"pfcount",
|
||||
"pfmerge",
|
||||
"ping",
|
||||
"psetex",
|
||||
"psubscribe",
|
||||
"pubsub",
|
||||
"pttl",
|
||||
"publish",
|
||||
"punsubscribe",
|
||||
"quit",
|
||||
"randomkey",
|
||||
"rename",
|
||||
"renamenx",
|
||||
"restore",
|
||||
"role",
|
||||
"rpop",
|
||||
"rpoplpush",
|
||||
"rpush",
|
||||
"rpushx",
|
||||
"sadd",
|
||||
"save",
|
||||
"scard",
|
||||
"script exists",
|
||||
"script flush",
|
||||
"script kill",
|
||||
"script load",
|
||||
"sdiff",
|
||||
"sdiffstore",
|
||||
"select",
|
||||
"set",
|
||||
"setbit",
|
||||
"setex",
|
||||
"setnx",
|
||||
"setrange",
|
||||
"shutdown",
|
||||
"sinter",
|
||||
"sinterstore",
|
||||
"sismember",
|
||||
"slaveof",
|
||||
"slowlog",
|
||||
"smembers",
|
||||
"smove",
|
||||
"sort",
|
||||
"spop",
|
||||
"srandmember",
|
||||
"srem",
|
||||
"strlen",
|
||||
"subscribe",
|
||||
"sunion",
|
||||
"sunionstore",
|
||||
"sync",
|
||||
"time",
|
||||
"ttl",
|
||||
"type",
|
||||
"unsubscribe",
|
||||
"unwatch",
|
||||
"wait",
|
||||
"watch",
|
||||
"zadd",
|
||||
"zcard",
|
||||
"zcount",
|
||||
"zincrby",
|
||||
"zinterstore",
|
||||
"zlexcount",
|
||||
"zrange",
|
||||
"zrangebylex",
|
||||
"zrevrangebylex",
|
||||
"zrangebyscore",
|
||||
"zrank",
|
||||
"zrem",
|
||||
"zremrangebylex",
|
||||
"zremrangebyrank",
|
||||
"zremrangebyscore",
|
||||
"zrevrange",
|
||||
"zrevrangebyscore",
|
||||
"zrevrank",
|
||||
"zscore",
|
||||
"zunionstore",
|
||||
"scan",
|
||||
"sscan",
|
||||
"hscan",
|
||||
"zscan"
|
||||
];
|
@@ -20,7 +20,8 @@
|
||||
"posttest": "jshint ."
|
||||
},
|
||||
"dependencies": {
|
||||
"double-ended-queue": "^2.1.0-0"
|
||||
"double-ended-queue": "^2.1.0-0",
|
||||
"redis-commands": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
|
119
test/prefix.spec.js
Normal file
119
test/prefix.spec.js
Normal file
@@ -0,0 +1,119 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require("assert");
|
||||
var config = require("./lib/config");
|
||||
var helper = require('./helper');
|
||||
var redis = config.redis;
|
||||
|
||||
describe("prefix key names", function () {
|
||||
|
||||
helper.allTests(function(parser, ip, args) {
|
||||
|
||||
describe("using " + parser + " and " + ip, function () {
|
||||
var client = null;
|
||||
|
||||
beforeEach(function(done) {
|
||||
client = redis.createClient({
|
||||
parser: parser,
|
||||
prefix: 'test:prefix:'
|
||||
});
|
||||
client.on('ready', function () {
|
||||
client.flushdb(function (err) {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
client.end(true);
|
||||
});
|
||||
|
||||
it("auto prefix set / get", function (done) {
|
||||
client.set('key', 'value', function(err, reply) {
|
||||
assert.strictEqual(reply, 'OK');
|
||||
});
|
||||
client.get('key', function(err, reply) {
|
||||
assert.strictEqual(reply, 'value');
|
||||
});
|
||||
client.getrange('key', 1, -1, function(err, reply) {
|
||||
assert.strictEqual(reply, 'alue');
|
||||
assert.strictEqual(err, null);
|
||||
});
|
||||
client.exists('key', function (err, res) {
|
||||
assert.strictEqual(res, 1);
|
||||
});
|
||||
client.exists('test:prefix:key', function (err, res) {
|
||||
// The key will be prefixed itself
|
||||
assert.strictEqual(res, 0);
|
||||
});
|
||||
client.mset('key2', 'value2', 'key3', 'value3');
|
||||
client.keys('*', function (err, res) {
|
||||
assert.strictEqual(res.length, 3);
|
||||
assert(res.indexOf('test:prefix:key') !== -1);
|
||||
assert(res.indexOf('test:prefix:key2') !== -1);
|
||||
assert(res.indexOf('test:prefix:key3') !== -1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("auto prefix set / get with .batch", function (done) {
|
||||
var batch = client.batch();
|
||||
batch.set('key', 'value', function(err, reply) {
|
||||
assert.strictEqual(reply, 'OK');
|
||||
});
|
||||
batch.get('key', function(err, reply) {
|
||||
assert.strictEqual(reply, 'value');
|
||||
});
|
||||
batch.getrange('key', 1, -1, function(err, reply) {
|
||||
assert.strictEqual(reply, 'alue');
|
||||
assert.strictEqual(err, null);
|
||||
});
|
||||
batch.exists('key', function (err, res) {
|
||||
assert.strictEqual(res, 1);
|
||||
});
|
||||
batch.exists('test:prefix:key', function (err, res) {
|
||||
// The key will be prefixed itself
|
||||
assert.strictEqual(res, 0);
|
||||
});
|
||||
batch.mset('key2', 'value2', 'key3', 'value3');
|
||||
batch.keys('*', function (err, res) {
|
||||
assert.strictEqual(res.length, 3);
|
||||
assert(res.indexOf('test:prefix:key') !== -1);
|
||||
assert(res.indexOf('test:prefix:key2') !== -1);
|
||||
assert(res.indexOf('test:prefix:key3') !== -1);
|
||||
});
|
||||
batch.exec(done);
|
||||
});
|
||||
|
||||
it("auto prefix set / get with .multi", function (done) {
|
||||
var multi = client.multi();
|
||||
multi.set('key', 'value', function(err, reply) {
|
||||
assert.strictEqual(reply, 'OK');
|
||||
});
|
||||
multi.get('key', function(err, reply) {
|
||||
assert.strictEqual(reply, 'value');
|
||||
});
|
||||
multi.getrange('key', 1, -1, function(err, reply) {
|
||||
assert.strictEqual(reply, 'alue');
|
||||
assert.strictEqual(err, null);
|
||||
});
|
||||
multi.exists('key', function (err, res) {
|
||||
assert.strictEqual(res, 1);
|
||||
});
|
||||
multi.exists('test:prefix:key', function (err, res) {
|
||||
// The key will be prefixed itself
|
||||
assert.strictEqual(res, 0);
|
||||
});
|
||||
multi.mset('key2', 'value2', 'key3', 'value3');
|
||||
multi.keys('*', function (err, res) {
|
||||
assert.strictEqual(res.length, 3);
|
||||
assert(res.indexOf('test:prefix:key') !== -1);
|
||||
assert(res.indexOf('test:prefix:key2') !== -1);
|
||||
assert(res.indexOf('test:prefix:key3') !== -1);
|
||||
});
|
||||
multi.exec(done);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user