You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-06 02:15:48 +03:00
Merge branch 'master' of github.com:mranney/node_redis
This commit is contained in:
44
README.md
44
README.md
@@ -53,7 +53,7 @@ The performance of `node_redis` improves dramatically with pipelining.
|
||||
|
||||
## Usage
|
||||
|
||||
Simple example, included as `example.js`:
|
||||
Simple example, included as `examples/simple.js`:
|
||||
|
||||
var redis = require("redis"),
|
||||
client = redis.createClient();
|
||||
@@ -168,9 +168,9 @@ to `127.0.0.1`. If you have `redis-server` running on the same computer as node
|
||||
port and host are probably fine. `options` in an object with the following possible properties:
|
||||
|
||||
* `parser`: which Redis protocol reply parser to use. Defaults to `hiredis` if that module is installed.
|
||||
This may also be set to `javascript`.
|
||||
This may also be set to `javascript`.
|
||||
* `return_buffers`: defaults to false. If set to `true`, then bulk data replies will be returned as node Buffer
|
||||
objects instead of JavaScript Strings.
|
||||
objects instead of JavaScript Strings.
|
||||
|
||||
`createClient()` returns a `RedisClient` object that is named `client` in all of the examples here.
|
||||
|
||||
@@ -195,6 +195,44 @@ want to do this:
|
||||
`client.end()` is useful for timeout cases where something is stuck or taking too long and you want
|
||||
to start over.
|
||||
|
||||
## Friendlier hash commands
|
||||
|
||||
Most Redis commands take a single String or an Array of Strings as arguments, and replies are sent back as a single String or an Array of Strings. When dealing with hash values, there are a couple of useful exceptions to this.
|
||||
|
||||
### client.hgetall(hash)
|
||||
|
||||
The reply from an HGETALL command will be converted into a JavaScript Object by `node_redis`. That way you can interact
|
||||
with the responses using JavaScript syntax.
|
||||
|
||||
Example:
|
||||
|
||||
client.hmset("hosts", "mjr", "1", "another", "23", "home", "1234");
|
||||
client.hgetall("hosts", function (err, obj) {
|
||||
console.dir(obj);
|
||||
});
|
||||
|
||||
Output:
|
||||
|
||||
{ mjr: '1', another: '23', home: '1234' }
|
||||
|
||||
### client.hmset(hash, obj, [callback])
|
||||
|
||||
Multiple values in a hash can be set by supplying an object:
|
||||
|
||||
client.HMSET(key2, {
|
||||
"0123456789": "abcdefghij",
|
||||
"some manner of key": "a type of value"
|
||||
});
|
||||
|
||||
The properties and values of this Object will be set as keys and values in the Redis hash.
|
||||
|
||||
### client.hmset(hash, key1, val1, ... keyn, valn, [callback])
|
||||
|
||||
Multiple values may also be set by supplying a list:
|
||||
|
||||
client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of value");
|
||||
|
||||
|
||||
## Publish / Subscribe
|
||||
|
||||
Here is a simple example of the API for publish / subscribe. This program opens two
|
||||
|
12
changelog.md
12
changelog.md
@@ -1,6 +1,18 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
## v0.5.0 - December 29, 2010
|
||||
|
||||
Some bug fixes:
|
||||
|
||||
* An important bug fix in reconnection logic. Previously, reply callbacks would be invoked twice after
|
||||
a reconnect.
|
||||
* Changed error callback argument to be an actual Error object.
|
||||
|
||||
New feature:
|
||||
|
||||
* Add friendly syntax for HMSET using an object.
|
||||
|
||||
## v0.4.1 - December 8, 2010
|
||||
|
||||
Remove warning about missing hiredis. You probably do want it though.
|
||||
|
85
index.js
85
index.js
@@ -75,6 +75,18 @@ function RedisClient(stream, options) {
|
||||
return_buffers: self.options.return_buffers || false
|
||||
});
|
||||
|
||||
// "reply error" is an error sent back by redis
|
||||
self.reply_parser.on("reply error", function (reply) {
|
||||
self.return_error(new Error(reply));
|
||||
});
|
||||
self.reply_parser.on("reply", function (reply) {
|
||||
self.return_reply(reply);
|
||||
});
|
||||
// "error" is bad. Somehow the parser got confused. It'll try to reset and continue.
|
||||
self.reply_parser.on("error", function (err) {
|
||||
self.emit("error", new Error("Redis reply parser error: " + err.stack));
|
||||
});
|
||||
|
||||
this.stream.on("connect", function () {
|
||||
if (exports.debug_mode) {
|
||||
console.log("Stream connected");
|
||||
@@ -84,18 +96,6 @@ function RedisClient(stream, options) {
|
||||
self.command_queue = new Queue();
|
||||
self.emitted_end = false;
|
||||
|
||||
// "reply error" is an error sent back by redis
|
||||
self.reply_parser.on("reply error", function (reply) {
|
||||
self.return_error(reply);
|
||||
});
|
||||
self.reply_parser.on("reply", function (reply) {
|
||||
self.return_reply(reply);
|
||||
});
|
||||
// "error" is bad. Somehow the parser got confused. It'll try to reset and continue.
|
||||
self.reply_parser.on("error", function (err) {
|
||||
self.emit("error", new Error("Redis reply parser error: " + err.stack));
|
||||
});
|
||||
|
||||
self.retry_timer = null;
|
||||
self.retry_delay = 250;
|
||||
self.stream.setNoDelay();
|
||||
@@ -194,7 +194,10 @@ RedisClient.prototype.connection_gone = function (why) {
|
||||
console.log("Retry conneciton in " + self.retry_delay + " ms");
|
||||
}
|
||||
self.attempts += 1;
|
||||
self.emit("reconnecting", "delay " + self.retry_delay + ", attempt " + self.attempts);
|
||||
self.emit("reconnecting", {
|
||||
delay: self.retry_delay,
|
||||
attempt: self.attempts
|
||||
});
|
||||
self.retry_timer = setTimeout(function () {
|
||||
if (exports.debug_mode) {
|
||||
console.log("Retrying connection...");
|
||||
@@ -231,16 +234,16 @@ RedisClient.prototype.return_error = function (err) {
|
||||
if (command_obj && typeof command_obj.callback === "function") {
|
||||
try {
|
||||
command_obj.callback(err);
|
||||
} catch (err) {
|
||||
} catch (callback_err) {
|
||||
// if a callback throws an exception, re-throw it on a new stack so the parser can keep going
|
||||
process.nextTick(function () {
|
||||
throw err;
|
||||
throw callback_err;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log("no callback to send error: " + util.inspect(err));
|
||||
console.log("node_redis: no callback to send error: " + util.inspect(err));
|
||||
// this will probably not make it anywhere useful, but we might as well throw
|
||||
throw new Error(err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -437,9 +440,8 @@ function Multi(client, args) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Official source is: http://code.google.com/p/redis/wiki/CommandReference
|
||||
// This list is taken from src/redis.c
|
||||
// Official source is: http://redis.io/commands.json
|
||||
// This list needs to be updated, and perhaps auto-updated somehow.
|
||||
[
|
||||
// string commands
|
||||
"get", "set", "setnx", "setex", "append", "substr", "strlen", "del", "exists", "incr", "decr", "mget",
|
||||
@@ -451,7 +453,7 @@ function Multi(client, args) {
|
||||
"zadd", "zincrby", "zrem", "zremrangebyscore", "zremrangebyrank", "zunionstore", "zinterstore", "zrange", "zrangebyscore", "zrevrangebyscore",
|
||||
"zcount", "zrevrange", "zcard", "zscore", "zrank", "zrevrank",
|
||||
// hash commands
|
||||
"hset", "hsetnx", "hget", "hmset", "hmget", "hincrby", "hdel", "hlen", "hkeys", "hgetall", "hexists", "incrby", "decrby",
|
||||
"hset", "hsetnx", "hget", "hmget", "hincrby", "hdel", "hlen", "hkeys", "hgetall", "hexists", "incrby", "decrby",
|
||||
// misc
|
||||
"getset", "mset", "msetnx", "randomkey", "select", "move", "rename", "renamenx", "expire", "expireat", "keys", "dbsize", "auth", "ping", "echo",
|
||||
"save", "bgsave", "bgwriteaof", "shutdown", "lastsave", "type", "sync", "flushdb", "flushall", "sort", "info",
|
||||
@@ -474,6 +476,47 @@ function Multi(client, args) {
|
||||
Multi.prototype[command.toUpperCase()] = Multi.prototype[command];
|
||||
});
|
||||
|
||||
RedisClient.prototype.hmset = function () {
|
||||
var args = to_array(arguments), tmp_args;
|
||||
if (args.length >= 2 && typeof args[0] === "string" && typeof args[1] === "object") {
|
||||
tmp_args = [ "hmset", args[0] ];
|
||||
Object.keys(args[1]).map(function (key) {
|
||||
tmp_args.push(key);
|
||||
tmp_args.push(args[1][key]);
|
||||
});
|
||||
if (args[2]) {
|
||||
tmp_args.push(args[2]);
|
||||
}
|
||||
args = tmp_args;
|
||||
} else {
|
||||
args.unshift("hmset");
|
||||
}
|
||||
|
||||
this.send_command.apply(this, args);
|
||||
};
|
||||
RedisClient.prototype.HMSET = RedisClient.prototype.hmset;
|
||||
|
||||
Multi.prototype.hmset = function () {
|
||||
var args = to_array(arguments), tmp_args;
|
||||
if (args.length >= 2 && typeof args[0] === "string" && typeof args[1] === "object") {
|
||||
tmp_args = [ "hmset", args[0] ];
|
||||
Object.keys(args[1]).map(function (key) {
|
||||
tmp_args.push(key);
|
||||
tmp_args.push(args[1][key]);
|
||||
});
|
||||
if (args[2]) {
|
||||
tmp_args.push(args[2]);
|
||||
}
|
||||
args = tmp_args;
|
||||
} else {
|
||||
args.unshift("hmset");
|
||||
}
|
||||
|
||||
this.queue.push(args);
|
||||
return this;
|
||||
};
|
||||
Multi.prototype.HMSET = Multi.prototype.hmset;
|
||||
|
||||
Multi.prototype.exec = function (callback) {
|
||||
var self = this;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{ "name" : "redis",
|
||||
"version" : "0.4.2",
|
||||
"version" : "0.5.0",
|
||||
"description" : "Redis client library",
|
||||
"author": "Matt Ranney <mjr@ranney.com>",
|
||||
"contributors": [
|
||||
|
66
test.js
66
test.js
@@ -211,13 +211,19 @@ tests.MULTI_6 = function () {
|
||||
|
||||
client.multi()
|
||||
.hmset("multihash", "a", "foo", "b", 1)
|
||||
.hmset("multihash", {
|
||||
extra: "fancy",
|
||||
things: "here"
|
||||
})
|
||||
.hgetall("multihash")
|
||||
.exec(function (err, replies) {
|
||||
assert.strictEqual(null, err);
|
||||
assert.equal("OK", replies[0]);
|
||||
assert.equal(Object.keys(replies[1]).length, 2);
|
||||
assert.equal("foo", replies[1].a.toString());
|
||||
assert.equal("1", replies[1].b.toString());
|
||||
assert.equal(Object.keys(replies[2]).length, 4);
|
||||
assert.equal("foo", replies[2].a);
|
||||
assert.equal("1", replies[2].b);
|
||||
assert.equal("fancy", replies[2].extra);
|
||||
assert.equal("here", replies[2].things);
|
||||
next(name);
|
||||
});
|
||||
};
|
||||
@@ -237,6 +243,30 @@ tests.WATCH_MULTI = function () {
|
||||
}
|
||||
};
|
||||
|
||||
tests.reconnect = function () {
|
||||
var name = "reconnect";
|
||||
|
||||
client.set("recon 1", "one");
|
||||
client.set("recon 2", "two", function (err, res) {
|
||||
// Do not do this in normal programs. This is to simulate the server closing on us.
|
||||
// For orderly shutdown in normal programs, do client.quit()
|
||||
client.stream.destroy();
|
||||
});
|
||||
|
||||
client.on("reconnecting", function on_recon(params) {
|
||||
client.on("connect", function on_connect() {
|
||||
client.select(test_db_num, require_string("OK", name));
|
||||
client.get("recon 1", require_string("one", name));
|
||||
client.get("recon 1", require_string("one", name));
|
||||
client.get("recon 2", require_string("two", name));
|
||||
client.get("recon 2", require_string("two", name));
|
||||
client.removeListener("connect", on_connect);
|
||||
client.removeListener("reconnecting", on_recon);
|
||||
next(name);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
tests.HSET = function () {
|
||||
var key = "test hash",
|
||||
field1 = new Buffer("0123456789"),
|
||||
@@ -257,17 +287,30 @@ tests.HSET = function () {
|
||||
client.HSET(key, field2, value2, last(name, require_number(0, name)));
|
||||
};
|
||||
|
||||
|
||||
tests.HMGET = function () {
|
||||
var key = "test hash", name = "HMGET";
|
||||
var key1 = "test hash 1", key2 = "test hash 2", name = "HMGET";
|
||||
|
||||
client.HMSET(key, "0123456789", "abcdefghij", "some manner of key", "a type of value", require_string("OK", name));
|
||||
// redis-like hmset syntax
|
||||
client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of value", require_string("OK", name));
|
||||
|
||||
client.HMGET(key, "0123456789", "some manner of key", function (err, reply) {
|
||||
// fancy hmset syntax
|
||||
client.HMSET(key2, {
|
||||
"0123456789": "abcdefghij",
|
||||
"some manner of key": "a type of value"
|
||||
}, require_string("OK", name));
|
||||
|
||||
client.HMGET(key1, "0123456789", "some manner of key", function (err, reply) {
|
||||
assert.strictEqual("abcdefghij", reply[0].toString(), name);
|
||||
assert.strictEqual("a type of value", reply[1].toString(), name);
|
||||
});
|
||||
|
||||
client.HMGET(key, "missing thing", "another missing thing", function (err, reply) {
|
||||
client.HMGET(key2, "0123456789", "some manner of key", function (err, reply) {
|
||||
assert.strictEqual("abcdefghij", reply[0].toString(), name);
|
||||
assert.strictEqual("a type of value", reply[1].toString(), name);
|
||||
});
|
||||
|
||||
client.HMGET(key1, "missing thing", "another missing thing", function (err, reply) {
|
||||
assert.strictEqual(null, reply[0], name);
|
||||
assert.strictEqual(null, reply[1], name);
|
||||
next(name);
|
||||
@@ -1014,7 +1057,10 @@ function run_next_test() {
|
||||
|
||||
console.log("Using reply parser " + client.reply_parser.name);
|
||||
|
||||
client.on("connect", function () {
|
||||
client.on("connect", function start_tests() {
|
||||
// remove listener so we don't restart all tests on reconnect
|
||||
client.removeListener("connect", start_tests);
|
||||
|
||||
// Fetch and stash info results in case anybody needs info on the server we are using.
|
||||
client.info(function (err, reply) {
|
||||
var obj = {};
|
||||
@@ -1055,8 +1101,8 @@ client3.on("error", function (err) {
|
||||
process.exit();
|
||||
});
|
||||
|
||||
client.on("reconnecting", function (msg) {
|
||||
console.log("reconnecting: " + msg);
|
||||
client.on("reconnecting", function (params) {
|
||||
console.log("reconnecting: " + util.inspect(params));
|
||||
});
|
||||
|
||||
process.on('uncaughtException', function (err) {
|
||||
|
Reference in New Issue
Block a user