forked from mirror/ledisdb
Merge branch 'develop'
This commit is contained in:
commit
89c2a1678c
15
README.md
15
README.md
|
@ -1,18 +1,19 @@
|
||||||
# LedisDB
|
# LedisDB
|
||||||
|
|
||||||
Ledisdb is a high performance NoSQL like Redis written by go. It supports some advanced data structure like kv, list, hash and zset, and may be alternative for Redis.
|
Ledisdb is a high performance NoSQL like Redis written by go. It supports some advanced data structure like kv, list, hash, zset, bitmap, and may be alternative for Redis.
|
||||||
|
|
||||||
LedisDB now supports multi database as backend to store data, you can test and choose the proper one for you.
|
LedisDB now supports multi database as backend to store data, you can test and choose the proper one for you.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
+ Rich advanced data structure: KV, List, Hash, ZSet, Bit.
|
+ Rich advanced data structure: KV, List, Hash, ZSet, Bitmap.
|
||||||
+ Stores lots of data, over the memory limit.
|
+ Stores lots of data, over the memory limit.
|
||||||
+ Various backend database to use: LevelDB, goleveldb, LMDB, RocksDB.
|
+ Various backend database to use: LevelDB, goleveldb, LMDB, RocksDB, BoltDB.
|
||||||
+ Supports expiration and ttl.
|
+ Supports expiration and ttl.
|
||||||
+ Redis clients, like redis-cli, are supported directly.
|
+ Redis clients, like redis-cli, are supported directly.
|
||||||
+ Multi client API supports, including Golang, Python, Lua(Openresty).
|
+ Multi client API supports, including Golang, Python, Lua(Openresty).
|
||||||
+ Easy to embed in Golang application.
|
+ Easy to embed in Golang application.
|
||||||
|
+ Restful API support, json/bson/msgpack output.
|
||||||
+ Replication to guarantee data safe.
|
+ Replication to guarantee data safe.
|
||||||
+ Supplies tools to load, dump, repair database.
|
+ Supplies tools to load, dump, repair database.
|
||||||
|
|
||||||
|
@ -94,6 +95,14 @@ You must known that changing store database runtime is very dangerous, LedisDB w
|
||||||
ledis 127.0.0.1:6380> get a
|
ledis 127.0.0.1:6380> get a
|
||||||
"1"
|
"1"
|
||||||
|
|
||||||
|
//use curl
|
||||||
|
curl http://127.0.0.1:11181/SET/hello/world
|
||||||
|
→ {"SET":[true,"OK"]}
|
||||||
|
|
||||||
|
curl http://127.0.0.1:11181/0/GET/hello?type=json
|
||||||
|
→ {"GET":"world"}
|
||||||
|
|
||||||
|
|
||||||
## Package Example
|
## Package Example
|
||||||
|
|
||||||
import "github.com/siddontang/ledisdb/ledis"
|
import "github.com/siddontang/ledisdb/ledis"
|
||||||
|
|
|
@ -8,4 +8,10 @@ go get github.com/siddontang/copier
|
||||||
|
|
||||||
go get github.com/siddontang/goleveldb/leveldb
|
go get github.com/siddontang/goleveldb/leveldb
|
||||||
|
|
||||||
go get -d github.com/siddontang/gomdb
|
go get github.com/szferi/gomdb
|
||||||
|
|
||||||
|
go get github.com/boltdb/bolt
|
||||||
|
|
||||||
|
go get gopkg.in/mgo.v2/bson
|
||||||
|
go get github.com/ugorji/go/codec
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2010 Matthew Ranney, http://ranney.com/
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||||
|
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
|
||||||
|
to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions
|
||||||
|
of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
###Ledis - a node.js LedisDB client
|
||||||
|
This is a modification of [simplegeo/nodejs-redis](https://github.com/simplegeo/nodejs-redis) , aiming to be compatible with LedisDB.
|
||||||
|
|
||||||
|
###Setup
|
||||||
|
Just copy (or move) the ledis directory into your project's **node_modules** directory.
|
||||||
|
|
||||||
|
cp -r /path/to/ledisdb/client/nodejs/ledis /path/to/your/node_modules/
|
||||||
|
|
||||||
|
###Example
|
||||||
|
Below is the total content of example.js, including the ledisDB's special commands.
|
||||||
|
|
||||||
|
|
||||||
|
var ledis = require("ledis"),
|
||||||
|
client = ledis.createClient();
|
||||||
|
|
||||||
|
client.on("error", function (err) {
|
||||||
|
console.log("Error " + err);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.set("string key", "string val", ledis.print);
|
||||||
|
client.get("string key", ledis.print);
|
||||||
|
client.hset("hash key", "hashtest 1", "some value", ledis.print);
|
||||||
|
client.hset(["hash key", "hashtest 2", "some other value"], ledis.print);
|
||||||
|
client.hkeys("hash key", function (err, replies) {
|
||||||
|
console.log(replies.length + " replies:");
|
||||||
|
replies.forEach(function (reply, i) {
|
||||||
|
console.log(" " + i + ": " + reply);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//ledis special commands
|
||||||
|
client.lpush("list key", "1", "2", "3", ledis.print);
|
||||||
|
client.lrange("list key", "0", "2", ledis.print);
|
||||||
|
client.lclear("list key", ledis.print);
|
||||||
|
client.lrange("list key", "0", "2", ledis.print);
|
||||||
|
|
||||||
|
client.zadd("zset key", 100, "m", ledis.print);
|
||||||
|
client.zexpire("zset key", 40, ledis.print);
|
||||||
|
client.zttl("zset key", ledis.print);
|
||||||
|
|
||||||
|
client.bsetbit("bit key 1", 1, 1, ledis.print);
|
||||||
|
client.bsetbit("bit key 2", 1, 1, ledis.print);
|
||||||
|
client.bopt("and", "bit key 3", "bit key 1", "bit key 2", ledis.print);
|
||||||
|
client.bget("bit key 3", function(err, result){
|
||||||
|
if (result=="\x02"){
|
||||||
|
console.log("Reply: \\x02")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.quit();
|
||||||
|
|
||||||
|
Run the example in your project directory, and will display
|
||||||
|
|
||||||
|
wyk:~/my/project/dir/$ node example.js
|
||||||
|
|
||||||
|
Reply: OK
|
||||||
|
Reply: string val
|
||||||
|
Reply: 0
|
||||||
|
Reply: 0
|
||||||
|
2 replies:
|
||||||
|
0: hashtest 1
|
||||||
|
1: hashtest 2
|
||||||
|
Reply: 3
|
||||||
|
Reply: 3,2,1
|
||||||
|
Reply: 3
|
||||||
|
Reply:
|
||||||
|
Reply: 1
|
||||||
|
Reply: 1
|
||||||
|
Reply: 40
|
||||||
|
Reply: 1
|
||||||
|
Reply: 1
|
||||||
|
Reply: 2
|
||||||
|
Reply: \x02
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
var ledis = require("ledis"),
|
||||||
|
client = ledis.createClient();
|
||||||
|
|
||||||
|
client.on("error", function (err) {
|
||||||
|
console.log("Error " + err);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.set("string key", "string val", ledis.print);
|
||||||
|
client.get("string key", ledis.print);
|
||||||
|
client.hset("hash key", "hashtest 1", "some value", ledis.print);
|
||||||
|
client.hset(["hash key", "hashtest 2", "some other value"], ledis.print);
|
||||||
|
client.hkeys("hash key", function (err, replies) {
|
||||||
|
console.log(replies.length + " replies:");
|
||||||
|
replies.forEach(function (reply, i) {
|
||||||
|
console.log(" " + i + ": " + reply);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//ledis special commands
|
||||||
|
client.lpush("list key", "1", "2", "3", ledis.print);
|
||||||
|
client.lrange("list key", "0", "2", ledis.print);
|
||||||
|
client.lclear("list key", ledis.print);
|
||||||
|
client.lrange("list key", "0", "2", ledis.print);
|
||||||
|
|
||||||
|
client.zadd("zset key", 100, "m", ledis.print);
|
||||||
|
client.zexpire("zset key", 40, ledis.print);
|
||||||
|
client.zttl("zset key", ledis.print);
|
||||||
|
|
||||||
|
client.bsetbit("bit key 1", 1, 1, ledis.print);
|
||||||
|
client.bsetbit("bit key 2", 1, 1, ledis.print);
|
||||||
|
client.bopt("and", "bit key 3", "bit key 1", "bit key 2", ledis.print);
|
||||||
|
client.bget("bit key 3", function(err, result){
|
||||||
|
if (result=="\x02"){
|
||||||
|
console.log("Reply: \\x02")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.quit();
|
|
@ -0,0 +1,8 @@
|
||||||
|
examples/
|
||||||
|
benches/
|
||||||
|
test.js
|
||||||
|
diff_multi_bench_output.js
|
||||||
|
generate_commands.js
|
||||||
|
multi_bench.js
|
||||||
|
test-unref.js
|
||||||
|
changelog.md
|
|
@ -0,0 +1,80 @@
|
||||||
|
var net = require('net');
|
||||||
|
|
||||||
|
var proxyPort = 6379;
|
||||||
|
var counter = 0;
|
||||||
|
|
||||||
|
function breaker(conn) {
|
||||||
|
conn.end();
|
||||||
|
conn.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
var server = net.createServer(function(conn) {
|
||||||
|
counter++;
|
||||||
|
var proxyConn = net.createConnection({
|
||||||
|
port: proxyPort
|
||||||
|
});
|
||||||
|
conn.pipe(proxyConn);
|
||||||
|
proxyConn.pipe(conn);
|
||||||
|
proxyConn.on('end', function() {
|
||||||
|
conn.end();
|
||||||
|
});
|
||||||
|
conn.on('end', function() {
|
||||||
|
proxyConn.end();
|
||||||
|
});
|
||||||
|
conn.on('close', function() {
|
||||||
|
proxyConn.end();
|
||||||
|
});
|
||||||
|
proxyConn.on('close', function() {
|
||||||
|
conn.end();
|
||||||
|
});
|
||||||
|
proxyConn.on('error', function() {
|
||||||
|
conn.end();
|
||||||
|
});
|
||||||
|
conn.on('error', function() {
|
||||||
|
proxyConn.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(breaker.bind(null, conn), Math.floor(Math.random() * 2000));
|
||||||
|
});
|
||||||
|
server.listen(6479);
|
||||||
|
|
||||||
|
var redis = require('./');
|
||||||
|
|
||||||
|
var port = 6479;
|
||||||
|
|
||||||
|
var client = redis.createClient(6479, 'localhost');
|
||||||
|
|
||||||
|
function iter() {
|
||||||
|
var k = "k" + Math.floor(Math.random() * 10);
|
||||||
|
var coinflip = Math.random() > 0.5;
|
||||||
|
if (coinflip) {
|
||||||
|
client.set(k, k, function(err, resp) {
|
||||||
|
if (!err && resp !== "OK") {
|
||||||
|
console.log("Unexpected set response " + resp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
client.get(k, function(err, resp) {
|
||||||
|
if (!err) {
|
||||||
|
if (k !== resp) {
|
||||||
|
console.log("Key response mismatch: " + k + " " + resp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function iters() {
|
||||||
|
for (var i = 0; i < 100; ++i) {
|
||||||
|
iter();
|
||||||
|
}
|
||||||
|
setTimeout(iters, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
client.on("connect", function () {
|
||||||
|
iters();
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("error", function (err) {
|
||||||
|
console.log("Client error " + err);
|
||||||
|
});
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,94 @@
|
||||||
|
// This file was generated by ./generate_commands.js on Wed Apr 23 2014 14:51:21 GMT-0700 (PDT)
|
||||||
|
module.exports = [
|
||||||
|
"quit",
|
||||||
|
|
||||||
|
"bget",
|
||||||
|
"bdelete",
|
||||||
|
"bsetbit",
|
||||||
|
"bgetbit",
|
||||||
|
"bmsetbit",
|
||||||
|
"bcount",
|
||||||
|
"bopt",
|
||||||
|
"bexpire",
|
||||||
|
"bexpireat",
|
||||||
|
"bttl",
|
||||||
|
"bpersist",
|
||||||
|
|
||||||
|
|
||||||
|
"hdel",
|
||||||
|
"hexists",
|
||||||
|
"hget",
|
||||||
|
"hgetall",
|
||||||
|
"hincrby",
|
||||||
|
"hkeys",
|
||||||
|
"hlen",
|
||||||
|
"hmget",
|
||||||
|
"hmset",
|
||||||
|
"hset",
|
||||||
|
"hvals",
|
||||||
|
|
||||||
|
|
||||||
|
"hclear",
|
||||||
|
"hmclear",
|
||||||
|
"hexpire",
|
||||||
|
"hexpireat",
|
||||||
|
"httl",
|
||||||
|
"hpersist",
|
||||||
|
|
||||||
|
|
||||||
|
"decr",
|
||||||
|
"decrby",
|
||||||
|
"del",
|
||||||
|
"exists",
|
||||||
|
"get",
|
||||||
|
"getset",
|
||||||
|
"incr",
|
||||||
|
"incrby",
|
||||||
|
"mget",
|
||||||
|
"mset",
|
||||||
|
"set",
|
||||||
|
"setnx",
|
||||||
|
"expire",
|
||||||
|
"expireat",
|
||||||
|
"ttl",
|
||||||
|
"persist",
|
||||||
|
|
||||||
|
"lindex",
|
||||||
|
"llen",
|
||||||
|
"lpop",
|
||||||
|
"lrange",
|
||||||
|
"lpush",
|
||||||
|
"rpop",
|
||||||
|
"rpush",
|
||||||
|
|
||||||
|
|
||||||
|
"lclear",
|
||||||
|
"lmclear",
|
||||||
|
"lexpire",
|
||||||
|
"lexpireat",
|
||||||
|
"lttl",
|
||||||
|
"lpersist",
|
||||||
|
|
||||||
|
"zadd",
|
||||||
|
"zcard",
|
||||||
|
"zcount",
|
||||||
|
"zincrby",
|
||||||
|
"zrange",
|
||||||
|
"zrangebyscore",
|
||||||
|
"zrank",
|
||||||
|
"zrem",
|
||||||
|
"zremrangebyrank",
|
||||||
|
"zremrangebyscore",
|
||||||
|
"zrevrange",
|
||||||
|
"zrevrank",
|
||||||
|
"zrevrangebyscore",
|
||||||
|
"zscore",
|
||||||
|
|
||||||
|
|
||||||
|
"zclear",
|
||||||
|
"zmclear",
|
||||||
|
"zexpire",
|
||||||
|
"zexpireat",
|
||||||
|
"zttl",
|
||||||
|
"zpersist",
|
||||||
|
];
|
|
@ -0,0 +1,46 @@
|
||||||
|
var events = require("events"),
|
||||||
|
util = require("../util"),
|
||||||
|
hiredis = require("hiredis");
|
||||||
|
|
||||||
|
exports.debug_mode = false;
|
||||||
|
exports.name = "hiredis";
|
||||||
|
|
||||||
|
function HiredisReplyParser(options) {
|
||||||
|
this.name = exports.name;
|
||||||
|
this.options = options || {};
|
||||||
|
this.reset();
|
||||||
|
events.EventEmitter.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(HiredisReplyParser, events.EventEmitter);
|
||||||
|
|
||||||
|
exports.Parser = HiredisReplyParser;
|
||||||
|
|
||||||
|
HiredisReplyParser.prototype.reset = function () {
|
||||||
|
this.reader = new hiredis.Reader({
|
||||||
|
return_buffers: this.options.return_buffers || false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
HiredisReplyParser.prototype.execute = function (data) {
|
||||||
|
var reply;
|
||||||
|
this.reader.feed(data);
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
reply = this.reader.get();
|
||||||
|
} catch (err) {
|
||||||
|
this.emit("error", err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reply === undefined) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reply && reply.constructor === Error) {
|
||||||
|
this.emit("reply error", reply);
|
||||||
|
} else {
|
||||||
|
this.emit("reply", reply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,301 @@
|
||||||
|
var events = require("events"),
|
||||||
|
util = require("../util");
|
||||||
|
|
||||||
|
function Packet(type, size) {
|
||||||
|
this.type = type;
|
||||||
|
this.size = +size;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.name = "javascript";
|
||||||
|
exports.debug_mode = false;
|
||||||
|
|
||||||
|
function ReplyParser(options) {
|
||||||
|
this.name = exports.name;
|
||||||
|
this.options = options || { };
|
||||||
|
|
||||||
|
this._buffer = null;
|
||||||
|
this._offset = 0;
|
||||||
|
this._encoding = "utf-8";
|
||||||
|
this._debug_mode = options.debug_mode;
|
||||||
|
this._reply_type = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(ReplyParser, events.EventEmitter);
|
||||||
|
|
||||||
|
exports.Parser = ReplyParser;
|
||||||
|
|
||||||
|
function IncompleteReadBuffer(message) {
|
||||||
|
this.name = "IncompleteReadBuffer";
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
util.inherits(IncompleteReadBuffer, Error);
|
||||||
|
|
||||||
|
// Buffer.toString() is quite slow for small strings
|
||||||
|
function small_toString(buf, start, end) {
|
||||||
|
var tmp = "", i;
|
||||||
|
|
||||||
|
for (i = start; i < end; i++) {
|
||||||
|
tmp += String.fromCharCode(buf[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReplyParser.prototype._parseResult = function (type) {
|
||||||
|
var start, end, offset, packetHeader;
|
||||||
|
|
||||||
|
if (type === 43 || type === 45) { // + or -
|
||||||
|
// up to the delimiter
|
||||||
|
end = this._packetEndOffset() - 1;
|
||||||
|
start = this._offset;
|
||||||
|
|
||||||
|
// include the delimiter
|
||||||
|
this._offset = end + 2;
|
||||||
|
|
||||||
|
if (end > this._buffer.length) {
|
||||||
|
this._offset = start;
|
||||||
|
throw new IncompleteReadBuffer("Wait for more data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.return_buffers) {
|
||||||
|
return this._buffer.slice(start, end);
|
||||||
|
} else {
|
||||||
|
if (end - start < 65536) { // completely arbitrary
|
||||||
|
return small_toString(this._buffer, start, end);
|
||||||
|
} else {
|
||||||
|
return this._buffer.toString(this._encoding, start, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type === 58) { // :
|
||||||
|
// up to the delimiter
|
||||||
|
end = this._packetEndOffset() - 1;
|
||||||
|
start = this._offset;
|
||||||
|
|
||||||
|
// include the delimiter
|
||||||
|
this._offset = end + 2;
|
||||||
|
|
||||||
|
if (end > this._buffer.length) {
|
||||||
|
this._offset = start;
|
||||||
|
throw new IncompleteReadBuffer("Wait for more data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.return_buffers) {
|
||||||
|
return this._buffer.slice(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the coerced numeric value
|
||||||
|
return +small_toString(this._buffer, start, end);
|
||||||
|
} else if (type === 36) { // $
|
||||||
|
// set a rewind point, as the packet could be larger than the
|
||||||
|
// buffer in memory
|
||||||
|
offset = this._offset - 1;
|
||||||
|
|
||||||
|
packetHeader = new Packet(type, this.parseHeader());
|
||||||
|
|
||||||
|
// packets with a size of -1 are considered null
|
||||||
|
if (packetHeader.size === -1) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
end = this._offset + packetHeader.size;
|
||||||
|
start = this._offset;
|
||||||
|
|
||||||
|
// set the offset to after the delimiter
|
||||||
|
this._offset = end + 2;
|
||||||
|
|
||||||
|
if (end > this._buffer.length) {
|
||||||
|
this._offset = offset;
|
||||||
|
throw new IncompleteReadBuffer("Wait for more data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.return_buffers) {
|
||||||
|
return this._buffer.slice(start, end);
|
||||||
|
} else {
|
||||||
|
return this._buffer.toString(this._encoding, start, end);
|
||||||
|
}
|
||||||
|
} else if (type === 42) { // *
|
||||||
|
offset = this._offset;
|
||||||
|
packetHeader = new Packet(type, this.parseHeader());
|
||||||
|
|
||||||
|
if (packetHeader.size < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packetHeader.size > this._bytesRemaining()) {
|
||||||
|
this._offset = offset - 1;
|
||||||
|
throw new IncompleteReadBuffer("Wait for more data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var reply = [ ];
|
||||||
|
var ntype, i, res;
|
||||||
|
|
||||||
|
offset = this._offset - 1;
|
||||||
|
|
||||||
|
for (i = 0; i < packetHeader.size; i++) {
|
||||||
|
ntype = this._buffer[this._offset++];
|
||||||
|
|
||||||
|
if (this._offset > this._buffer.length) {
|
||||||
|
throw new IncompleteReadBuffer("Wait for more data.");
|
||||||
|
}
|
||||||
|
res = this._parseResult(ntype);
|
||||||
|
if (res === undefined) {
|
||||||
|
res = null;
|
||||||
|
}
|
||||||
|
reply.push(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ReplyParser.prototype.execute = function (buffer) {
|
||||||
|
this.append(buffer);
|
||||||
|
|
||||||
|
var type, ret, offset;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
offset = this._offset;
|
||||||
|
try {
|
||||||
|
// at least 4 bytes: :1\r\n
|
||||||
|
if (this._bytesRemaining() < 4) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = this._buffer[this._offset++];
|
||||||
|
|
||||||
|
if (type === 43) { // +
|
||||||
|
ret = this._parseResult(type);
|
||||||
|
|
||||||
|
if (ret === null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.send_reply(ret);
|
||||||
|
} else if (type === 45) { // -
|
||||||
|
ret = this._parseResult(type);
|
||||||
|
|
||||||
|
if (ret === null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.send_error(ret);
|
||||||
|
} else if (type === 58) { // :
|
||||||
|
ret = this._parseResult(type);
|
||||||
|
|
||||||
|
if (ret === null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.send_reply(ret);
|
||||||
|
} else if (type === 36) { // $
|
||||||
|
ret = this._parseResult(type);
|
||||||
|
|
||||||
|
if (ret === null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the state for what is the result of
|
||||||
|
// a -1, set it back up for a null reply
|
||||||
|
if (ret === undefined) {
|
||||||
|
ret = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.send_reply(ret);
|
||||||
|
} else if (type === 42) { // *
|
||||||
|
// set a rewind point. if a failure occurs,
|
||||||
|
// wait for the next execute()/append() and try again
|
||||||
|
offset = this._offset - 1;
|
||||||
|
|
||||||
|
ret = this._parseResult(type);
|
||||||
|
|
||||||
|
this.send_reply(ret);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// catch the error (not enough data), rewind, and wait
|
||||||
|
// for the next packet to appear
|
||||||
|
if (! (err instanceof IncompleteReadBuffer)) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
this._offset = offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ReplyParser.prototype.append = function (newBuffer) {
|
||||||
|
if (!newBuffer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first run
|
||||||
|
if (this._buffer === null) {
|
||||||
|
this._buffer = newBuffer;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// out of data
|
||||||
|
if (this._offset >= this._buffer.length) {
|
||||||
|
this._buffer = newBuffer;
|
||||||
|
this._offset = 0;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// very large packet
|
||||||
|
// check for concat, if we have it, use it
|
||||||
|
if (Buffer.concat !== undefined) {
|
||||||
|
this._buffer = Buffer.concat([this._buffer.slice(this._offset), newBuffer]);
|
||||||
|
} else {
|
||||||
|
var remaining = this._bytesRemaining(),
|
||||||
|
newLength = remaining + newBuffer.length,
|
||||||
|
tmpBuffer = new Buffer(newLength);
|
||||||
|
|
||||||
|
this._buffer.copy(tmpBuffer, 0, this._offset);
|
||||||
|
newBuffer.copy(tmpBuffer, remaining, 0);
|
||||||
|
|
||||||
|
this._buffer = tmpBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._offset = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
ReplyParser.prototype.parseHeader = function () {
|
||||||
|
var end = this._packetEndOffset(),
|
||||||
|
value = small_toString(this._buffer, this._offset, end - 1);
|
||||||
|
|
||||||
|
this._offset = end + 1;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
ReplyParser.prototype._packetEndOffset = function () {
|
||||||
|
var offset = this._offset;
|
||||||
|
|
||||||
|
while (this._buffer[offset] !== 0x0d && this._buffer[offset + 1] !== 0x0a) {
|
||||||
|
offset++;
|
||||||
|
|
||||||
|
if (offset >= this._buffer.length) {
|
||||||
|
throw new IncompleteReadBuffer("didn't see LF after NL reading multi bulk count (" + offset + " => " + this._buffer.length + ", " + this._offset + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset++;
|
||||||
|
return offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
ReplyParser.prototype._bytesRemaining = function () {
|
||||||
|
return (this._buffer.length - this._offset) < 0 ? 0 : (this._buffer.length - this._offset);
|
||||||
|
};
|
||||||
|
|
||||||
|
ReplyParser.prototype.parser_error = function (message) {
|
||||||
|
this.emit("error", message);
|
||||||
|
};
|
||||||
|
|
||||||
|
ReplyParser.prototype.send_error = function (reply) {
|
||||||
|
this.emit("reply error", reply);
|
||||||
|
};
|
||||||
|
|
||||||
|
ReplyParser.prototype.send_reply = function (reply) {
|
||||||
|
this.emit("reply", reply);
|
||||||
|
};
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Queue class adapted from Tim Caswell's pattern library
|
||||||
|
// http://github.com/creationix/pattern/blob/master/lib/pattern/queue.js
|
||||||
|
|
||||||
|
function Queue() {
|
||||||
|
this.tail = [];
|
||||||
|
this.head = [];
|
||||||
|
this.offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Queue.prototype.shift = function () {
|
||||||
|
if (this.offset === this.head.length) {
|
||||||
|
var tmp = this.head;
|
||||||
|
tmp.length = 0;
|
||||||
|
this.head = this.tail;
|
||||||
|
this.tail = tmp;
|
||||||
|
this.offset = 0;
|
||||||
|
if (this.head.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.head[this.offset++]; // sorry, JSLint
|
||||||
|
};
|
||||||
|
|
||||||
|
Queue.prototype.push = function (item) {
|
||||||
|
return this.tail.push(item);
|
||||||
|
};
|
||||||
|
|
||||||
|
Queue.prototype.forEach = function (fn, thisv) {
|
||||||
|
var array = this.head.slice(this.offset), i, il;
|
||||||
|
|
||||||
|
array.push.apply(array, this.tail);
|
||||||
|
|
||||||
|
if (thisv) {
|
||||||
|
for (i = 0, il = array.length; i < il; i += 1) {
|
||||||
|
fn.call(thisv, array[i], i, array);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i = 0, il = array.length; i < il; i += 1) {
|
||||||
|
fn(array[i], i, array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
};
|
||||||
|
|
||||||
|
Queue.prototype.getLength = function () {
|
||||||
|
return this.head.length - this.offset + this.tail.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.defineProperty(Queue.prototype, "length", {
|
||||||
|
get: function () {
|
||||||
|
return this.getLength();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (typeof module !== "undefined" && module.exports) {
|
||||||
|
module.exports = Queue;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
function to_array(args) {
|
||||||
|
var len = args.length,
|
||||||
|
arr = new Array(len), i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i += 1) {
|
||||||
|
arr[i] = args[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = to_array;
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Support for very old versions of node where the module was called "sys". At some point, we should abandon this.
|
||||||
|
|
||||||
|
var util;
|
||||||
|
|
||||||
|
try {
|
||||||
|
util = require("util");
|
||||||
|
} catch (err) {
|
||||||
|
util = require("sys");
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = util;
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"name": "ledis",
|
||||||
|
"version": "0.1",
|
||||||
|
"description": "Ledis client library",
|
||||||
|
"keywords": [
|
||||||
|
"ledis",
|
||||||
|
"nosql"
|
||||||
|
],
|
||||||
|
"main": "./index.js",
|
||||||
|
"devDependencies": {
|
||||||
|
"metrics": ">=0.1.5",
|
||||||
|
"colors": "~0.6.0-1",
|
||||||
|
"underscore": "~1.4.4"
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,9 @@ package main
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/siddontang/ledisdb/server"
|
"github.com/siddontang/ledisdb/server"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
_ "net/http/pprof"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -52,5 +55,9 @@ func main() {
|
||||||
app.Close()
|
app.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log.Println(http.ListenAndServe("localhost:6060", nil))
|
||||||
|
}()
|
||||||
|
|
||||||
app.Run()
|
app.Run()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"addr": "127.0.0.1:6380",
|
"addr": "127.0.0.1:6380",
|
||||||
|
"http_addr": "127.0.0.1:11181",
|
||||||
"data_dir": "/tmp/ledis_server",
|
"data_dir": "/tmp/ledis_server",
|
||||||
|
|
||||||
"db": {
|
"db": {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/siddontang/ledisdb/ledis"
|
"github.com/siddontang/ledisdb/ledis"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -11,7 +12,8 @@ import (
|
||||||
type App struct {
|
type App struct {
|
||||||
cfg *Config
|
cfg *Config
|
||||||
|
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
|
httpListener net.Listener
|
||||||
|
|
||||||
ldb *ledis.Ledis
|
ldb *ledis.Ledis
|
||||||
|
|
||||||
|
@ -25,6 +27,14 @@ type App struct {
|
||||||
m *master
|
m *master
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func netType(s string) string {
|
||||||
|
if strings.Contains(s, "/") {
|
||||||
|
return "unix"
|
||||||
|
} else {
|
||||||
|
return "tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func NewApp(cfg *Config) (*App, error) {
|
func NewApp(cfg *Config) (*App, error) {
|
||||||
if len(cfg.DataDir) == 0 {
|
if len(cfg.DataDir) == 0 {
|
||||||
return nil, fmt.Errorf("must set data_dir first")
|
return nil, fmt.Errorf("must set data_dir first")
|
||||||
|
@ -40,14 +50,14 @@ func NewApp(cfg *Config) (*App, error) {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if strings.Contains(cfg.Addr, "/") {
|
if app.listener, err = net.Listen(netType(cfg.Addr), cfg.Addr); err != nil {
|
||||||
app.listener, err = net.Listen("unix", cfg.Addr)
|
return nil, err
|
||||||
} else {
|
|
||||||
app.listener, err = net.Listen("tcp", cfg.Addr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if len(cfg.HttpAddr) > 0 {
|
||||||
return nil, err
|
if app.httpListener, err = net.Listen(netType(cfg.HttpAddr), cfg.HttpAddr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cfg.AccessLog) > 0 {
|
if len(cfg.AccessLog) > 0 {
|
||||||
|
@ -82,6 +92,10 @@ func (app *App) Close() {
|
||||||
|
|
||||||
app.listener.Close()
|
app.listener.Close()
|
||||||
|
|
||||||
|
if app.httpListener != nil {
|
||||||
|
app.httpListener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
app.m.Close()
|
app.m.Close()
|
||||||
|
|
||||||
if app.access != nil {
|
if app.access != nil {
|
||||||
|
@ -96,16 +110,33 @@ func (app *App) Run() {
|
||||||
app.slaveof(app.cfg.SlaveOf)
|
app.slaveof(app.cfg.SlaveOf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
go app.httpServe()
|
||||||
|
|
||||||
for !app.closed {
|
for !app.closed {
|
||||||
conn, err := app.listener.Accept()
|
conn, err := app.listener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
newClient(conn, app)
|
newClientRESP(conn, app)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *App) httpServe() {
|
||||||
|
if app.httpListener == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
newClientHTTP(app, w, r)
|
||||||
|
})
|
||||||
|
|
||||||
|
svr := http.Server{Handler: mux}
|
||||||
|
svr.Serve(app.httpListener)
|
||||||
|
}
|
||||||
|
|
||||||
func (app *App) Ledis() *ledis.Ledis {
|
func (app *App) Ledis() *ledis.Ledis {
|
||||||
return app.ldb
|
return app.ldb
|
||||||
}
|
}
|
||||||
|
|
315
server/client.go
315
server/client.go
|
@ -1,315 +0,0 @@
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"github.com/siddontang/go-log/log"
|
|
||||||
"github.com/siddontang/ledisdb/ledis"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var errReadRequest = errors.New("invalid request protocol")
|
|
||||||
|
|
||||||
type client struct {
|
|
||||||
app *App
|
|
||||||
ldb *ledis.Ledis
|
|
||||||
|
|
||||||
db *ledis.DB
|
|
||||||
c net.Conn
|
|
||||||
|
|
||||||
rb *bufio.Reader
|
|
||||||
wb *bufio.Writer
|
|
||||||
|
|
||||||
cmd string
|
|
||||||
args [][]byte
|
|
||||||
|
|
||||||
reqC chan error
|
|
||||||
|
|
||||||
syncBuf bytes.Buffer
|
|
||||||
|
|
||||||
compressBuf []byte
|
|
||||||
|
|
||||||
logBuf bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func newClient(c net.Conn, app *App) {
|
|
||||||
co := new(client)
|
|
||||||
|
|
||||||
co.app = app
|
|
||||||
co.ldb = app.ldb
|
|
||||||
//use default db
|
|
||||||
co.db, _ = app.ldb.Select(0)
|
|
||||||
co.c = c
|
|
||||||
|
|
||||||
co.rb = bufio.NewReaderSize(c, 256)
|
|
||||||
co.wb = bufio.NewWriterSize(c, 256)
|
|
||||||
|
|
||||||
co.reqC = make(chan error, 1)
|
|
||||||
|
|
||||||
co.compressBuf = make([]byte, 256)
|
|
||||||
|
|
||||||
go co.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) run() {
|
|
||||||
defer func() {
|
|
||||||
if e := recover(); e != nil {
|
|
||||||
buf := make([]byte, 4096)
|
|
||||||
n := runtime.Stack(buf, false)
|
|
||||||
buf = buf[0:n]
|
|
||||||
|
|
||||||
log.Fatal("client run panic %s:%v", buf, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.c.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
for {
|
|
||||||
req, err := c.readRequest()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.handleRequest(req)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) readLine() ([]byte, error) {
|
|
||||||
return ReadLine(c.rb)
|
|
||||||
}
|
|
||||||
|
|
||||||
//A client sends to the Redis server a RESP Array consisting of just Bulk Strings.
|
|
||||||
func (c *client) readRequest() ([][]byte, error) {
|
|
||||||
l, err := c.readLine()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if len(l) == 0 || l[0] != '*' {
|
|
||||||
return nil, errReadRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
var nparams int
|
|
||||||
if nparams, err = strconv.Atoi(ledis.String(l[1:])); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if nparams <= 0 {
|
|
||||||
return nil, errReadRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
req := make([][]byte, 0, nparams)
|
|
||||||
var n int
|
|
||||||
for i := 0; i < nparams; i++ {
|
|
||||||
if l, err = c.readLine(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(l) == 0 {
|
|
||||||
return nil, errReadRequest
|
|
||||||
} else if l[0] == '$' {
|
|
||||||
//handle resp string
|
|
||||||
if n, err = strconv.Atoi(ledis.String(l[1:])); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if n == -1 {
|
|
||||||
req = append(req, nil)
|
|
||||||
} else {
|
|
||||||
buf := make([]byte, n)
|
|
||||||
if _, err = io.ReadFull(c.rb, buf); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if l, err = c.readLine(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if len(l) != 0 {
|
|
||||||
return nil, errors.New("bad bulk string format")
|
|
||||||
}
|
|
||||||
|
|
||||||
req = append(req, buf)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return nil, errReadRequest
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return req, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) handleRequest(req [][]byte) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
if len(req) == 0 {
|
|
||||||
err = ErrEmptyCommand
|
|
||||||
} else {
|
|
||||||
c.cmd = strings.ToLower(ledis.String(req[0]))
|
|
||||||
c.args = req[1:]
|
|
||||||
|
|
||||||
f, ok := regCmds[c.cmd]
|
|
||||||
if !ok {
|
|
||||||
err = ErrNotFound
|
|
||||||
} else {
|
|
||||||
go func() {
|
|
||||||
c.reqC <- f(c)
|
|
||||||
}()
|
|
||||||
err = <-c.reqC
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
duration := time.Since(start)
|
|
||||||
|
|
||||||
if c.app.access != nil {
|
|
||||||
c.logBuf.Reset()
|
|
||||||
for i, r := range req {
|
|
||||||
left := 256 - c.logBuf.Len()
|
|
||||||
if left <= 0 {
|
|
||||||
break
|
|
||||||
} else if len(r) <= left {
|
|
||||||
c.logBuf.Write(r)
|
|
||||||
if i != len(req)-1 {
|
|
||||||
c.logBuf.WriteByte(' ')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
c.logBuf.Write(r[0:left])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.app.access.Log(c.c.RemoteAddr().String(), duration.Nanoseconds()/1000000, c.logBuf.Bytes(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
c.writeError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.wb.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) writeError(err error) {
|
|
||||||
c.wb.Write(ledis.Slice("-ERR"))
|
|
||||||
if err != nil {
|
|
||||||
c.wb.WriteByte(' ')
|
|
||||||
c.wb.Write(ledis.Slice(err.Error()))
|
|
||||||
}
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) writeStatus(status string) {
|
|
||||||
c.wb.WriteByte('+')
|
|
||||||
c.wb.Write(ledis.Slice(status))
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) writeInteger(n int64) {
|
|
||||||
c.wb.WriteByte(':')
|
|
||||||
c.wb.Write(ledis.StrPutInt64(n))
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) writeBulk(b []byte) {
|
|
||||||
c.wb.WriteByte('$')
|
|
||||||
if b == nil {
|
|
||||||
c.wb.Write(NullBulk)
|
|
||||||
} else {
|
|
||||||
c.wb.Write(ledis.Slice(strconv.Itoa(len(b))))
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
c.wb.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) writeArray(ay []interface{}) {
|
|
||||||
c.wb.WriteByte('*')
|
|
||||||
if ay == nil {
|
|
||||||
c.wb.Write(NullArray)
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
} else {
|
|
||||||
c.wb.Write(ledis.Slice(strconv.Itoa(len(ay))))
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
|
|
||||||
for i := 0; i < len(ay); i++ {
|
|
||||||
switch v := ay[i].(type) {
|
|
||||||
case []interface{}:
|
|
||||||
c.writeArray(v)
|
|
||||||
case []byte:
|
|
||||||
c.writeBulk(v)
|
|
||||||
case nil:
|
|
||||||
c.writeBulk(nil)
|
|
||||||
case int64:
|
|
||||||
c.writeInteger(v)
|
|
||||||
default:
|
|
||||||
panic("invalid array type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) writeSliceArray(ay [][]byte) {
|
|
||||||
c.wb.WriteByte('*')
|
|
||||||
if ay == nil {
|
|
||||||
c.wb.Write(NullArray)
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
} else {
|
|
||||||
c.wb.Write(ledis.Slice(strconv.Itoa(len(ay))))
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
|
|
||||||
for i := 0; i < len(ay); i++ {
|
|
||||||
c.writeBulk(ay[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) writeFVPairArray(ay []ledis.FVPair) {
|
|
||||||
c.wb.WriteByte('*')
|
|
||||||
if ay == nil {
|
|
||||||
c.wb.Write(NullArray)
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
} else {
|
|
||||||
c.wb.Write(ledis.Slice(strconv.Itoa(len(ay) * 2)))
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
|
|
||||||
for i := 0; i < len(ay); i++ {
|
|
||||||
c.writeBulk(ay[i].Field)
|
|
||||||
c.writeBulk(ay[i].Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) writeScorePairArray(ay []ledis.ScorePair, withScores bool) {
|
|
||||||
c.wb.WriteByte('*')
|
|
||||||
if ay == nil {
|
|
||||||
c.wb.Write(NullArray)
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
} else {
|
|
||||||
if withScores {
|
|
||||||
c.wb.Write(ledis.Slice(strconv.Itoa(len(ay) * 2)))
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
} else {
|
|
||||||
c.wb.Write(ledis.Slice(strconv.Itoa(len(ay))))
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(ay); i++ {
|
|
||||||
c.writeBulk(ay[i].Member)
|
|
||||||
|
|
||||||
if withScores {
|
|
||||||
c.writeBulk(ledis.StrPutInt64(ay[i].Score))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) writeBulkFrom(n int64, rb io.Reader) {
|
|
||||||
c.wb.WriteByte('$')
|
|
||||||
c.wb.Write(ledis.Slice(strconv.FormatInt(n, 10)))
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
|
|
||||||
io.Copy(c.wb, rb)
|
|
||||||
c.wb.Write(Delims)
|
|
||||||
}
|
|
|
@ -0,0 +1,260 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/siddontang/go-log/log"
|
||||||
|
"github.com/siddontang/ledisdb/ledis"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
|
"gopkg.in/mgo.v2/bson"
|
||||||
|
)
|
||||||
|
|
||||||
|
var allowedContentTypes = map[string]struct{}{
|
||||||
|
"json": struct{}{},
|
||||||
|
"bson": struct{}{},
|
||||||
|
"msgpack": struct{}{},
|
||||||
|
}
|
||||||
|
var unsopportedCommands = map[string]struct{}{
|
||||||
|
"slaveof": struct{}{},
|
||||||
|
"fullsync": struct{}{},
|
||||||
|
"sync": struct{}{},
|
||||||
|
"quit": struct{}{},
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpClient struct {
|
||||||
|
app *App
|
||||||
|
db *ledis.DB
|
||||||
|
ldb *ledis.Ledis
|
||||||
|
|
||||||
|
resp responseWriter
|
||||||
|
req *requestContext
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpWriter struct {
|
||||||
|
contentType string
|
||||||
|
cmd string
|
||||||
|
w http.ResponseWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func newClientHTTP(app *App, w http.ResponseWriter, r *http.Request) {
|
||||||
|
var err error
|
||||||
|
c := new(httpClient)
|
||||||
|
c.app = app
|
||||||
|
c.ldb = app.ldb
|
||||||
|
c.db, err = c.ldb.Select(0)
|
||||||
|
if err != nil {
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.req, err = c.makeRequest(app, r, w)
|
||||||
|
if err != nil {
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.req.perform()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpClient) addr(r *http.Request) string {
|
||||||
|
return r.RemoteAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpClient) makeRequest(app *App, r *http.Request, w http.ResponseWriter) (*requestContext, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
db, cmd, argsStr, contentType := c.parseReqPath(r)
|
||||||
|
|
||||||
|
c.db, err = app.ldb.Select(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
contentType = strings.ToLower(contentType)
|
||||||
|
|
||||||
|
if _, ok := allowedContentTypes[contentType]; !ok {
|
||||||
|
return nil, fmt.Errorf("unsupported content type: '%s', only json, bson, msgpack are supported", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := newRequestContext(app)
|
||||||
|
args := make([][]byte, len(argsStr))
|
||||||
|
for i, arg := range argsStr {
|
||||||
|
args[i] = []byte(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.cmd = strings.ToLower(cmd)
|
||||||
|
if _, ok := unsopportedCommands[req.cmd]; ok {
|
||||||
|
return nil, fmt.Errorf("unsupported command: '%s'", cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.args = args
|
||||||
|
|
||||||
|
req.remoteAddr = c.addr(r)
|
||||||
|
req.resp = &httpWriter{contentType, cmd, w}
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpClient) parseReqPath(r *http.Request) (db int, cmd string, args []string, contentType string) {
|
||||||
|
|
||||||
|
contentType = r.FormValue("type")
|
||||||
|
if contentType == "" {
|
||||||
|
contentType = "json"
|
||||||
|
}
|
||||||
|
|
||||||
|
substrings := strings.Split(strings.TrimLeft(r.URL.Path, "/"), "/")
|
||||||
|
if len(substrings) == 1 {
|
||||||
|
return 0, substrings[0], substrings[1:], contentType
|
||||||
|
}
|
||||||
|
db, err := strconv.Atoi(substrings[0])
|
||||||
|
if err != nil {
|
||||||
|
cmd = substrings[0]
|
||||||
|
args = substrings[1:]
|
||||||
|
} else {
|
||||||
|
cmd = substrings[1]
|
||||||
|
args = substrings[2:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// http writer
|
||||||
|
|
||||||
|
func (w *httpWriter) genericWrite(result interface{}) {
|
||||||
|
|
||||||
|
m := map[string]interface{}{
|
||||||
|
w.cmd: result,
|
||||||
|
}
|
||||||
|
switch w.contentType {
|
||||||
|
case "json":
|
||||||
|
writeJSON(&m, w.w)
|
||||||
|
case "bson":
|
||||||
|
writeBSON(&m, w.w)
|
||||||
|
case "msgpack":
|
||||||
|
writeMsgPack(&m, w.w)
|
||||||
|
default:
|
||||||
|
log.Error("invalid content type %s", w.contentType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *httpWriter) writeError(err error) {
|
||||||
|
result := [2]interface{}{
|
||||||
|
false,
|
||||||
|
fmt.Sprintf("ERR %s", err.Error()),
|
||||||
|
}
|
||||||
|
w.genericWrite(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *httpWriter) writeStatus(status string) {
|
||||||
|
var success bool
|
||||||
|
if status == OK || status == PONG {
|
||||||
|
success = true
|
||||||
|
}
|
||||||
|
w.genericWrite([]interface{}{success, status})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *httpWriter) writeInteger(n int64) {
|
||||||
|
w.genericWrite(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *httpWriter) writeBulk(b []byte) {
|
||||||
|
if b == nil {
|
||||||
|
w.genericWrite(nil)
|
||||||
|
} else {
|
||||||
|
w.genericWrite(ledis.String(b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *httpWriter) writeArray(lst []interface{}) {
|
||||||
|
w.genericWrite(lst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *httpWriter) writeSliceArray(lst [][]byte) {
|
||||||
|
arr := make([]interface{}, len(lst))
|
||||||
|
for i, elem := range lst {
|
||||||
|
if elem == nil {
|
||||||
|
arr[i] = nil
|
||||||
|
} else {
|
||||||
|
arr[i] = ledis.String(elem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.genericWrite(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *httpWriter) writeFVPairArray(lst []ledis.FVPair) {
|
||||||
|
m := make(map[string]string)
|
||||||
|
for _, elem := range lst {
|
||||||
|
m[ledis.String(elem.Field)] = ledis.String(elem.Value)
|
||||||
|
}
|
||||||
|
w.genericWrite(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *httpWriter) writeScorePairArray(lst []ledis.ScorePair, withScores bool) {
|
||||||
|
var arr []string
|
||||||
|
if withScores {
|
||||||
|
arr = make([]string, 2*len(lst))
|
||||||
|
for i, data := range lst {
|
||||||
|
arr[2*i] = ledis.String(data.Member)
|
||||||
|
arr[2*i+1] = strconv.FormatInt(data.Score, 10)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
arr = make([]string, len(lst))
|
||||||
|
for i, data := range lst {
|
||||||
|
arr[i] = ledis.String(data.Member)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.genericWrite(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *httpWriter) writeBulkFrom(n int64, rb io.Reader) {
|
||||||
|
w.writeError(fmt.Errorf("unsuport"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *httpWriter) flush() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeJSON(resutl interface{}, w http.ResponseWriter) {
|
||||||
|
buf, err := json.Marshal(resutl)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-type", "application/json; charset=utf-8")
|
||||||
|
w.Header().Set("Content-Length", strconv.Itoa(len(buf)))
|
||||||
|
|
||||||
|
_, err = w.Write(buf)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeBSON(result interface{}, w http.ResponseWriter) {
|
||||||
|
buf, err := bson.Marshal(result)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-type", "application/octet-stream")
|
||||||
|
w.Header().Set("Content-Length", strconv.Itoa(len(buf)))
|
||||||
|
|
||||||
|
_, err = w.Write(buf)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeMsgPack(result interface{}, w http.ResponseWriter) {
|
||||||
|
w.Header().Set("Content-type", "application/octet-stream")
|
||||||
|
|
||||||
|
var mh codec.MsgpackHandle
|
||||||
|
enc := codec.NewEncoder(w, &mh)
|
||||||
|
if err := enc.Encode(result); err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,292 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"github.com/siddontang/go-log/log"
|
||||||
|
"github.com/siddontang/ledisdb/ledis"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errReadRequest = errors.New("invalid request protocol")
|
||||||
|
|
||||||
|
type respClient struct {
|
||||||
|
app *App
|
||||||
|
ldb *ledis.Ledis
|
||||||
|
db *ledis.DB
|
||||||
|
|
||||||
|
conn net.Conn
|
||||||
|
rb *bufio.Reader
|
||||||
|
|
||||||
|
req *requestContext
|
||||||
|
}
|
||||||
|
|
||||||
|
type respWriter struct {
|
||||||
|
buff *bufio.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newClientRESP(conn net.Conn, app *App) {
|
||||||
|
c := new(respClient)
|
||||||
|
|
||||||
|
c.app = app
|
||||||
|
c.conn = conn
|
||||||
|
c.ldb = app.ldb
|
||||||
|
c.db, _ = app.ldb.Select(0)
|
||||||
|
|
||||||
|
c.rb = bufio.NewReaderSize(conn, 256)
|
||||||
|
|
||||||
|
c.req = newRequestContext(app)
|
||||||
|
c.req.resp = newWriterRESP(conn)
|
||||||
|
c.req.remoteAddr = conn.RemoteAddr().String()
|
||||||
|
|
||||||
|
go c.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *respClient) run() {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
n := runtime.Stack(buf, false)
|
||||||
|
buf = buf[0:n]
|
||||||
|
|
||||||
|
log.Fatal("client run panic %s:%v", buf, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.conn.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
reqData, err := c.readRequest()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.handleRequest(reqData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *respClient) readLine() ([]byte, error) {
|
||||||
|
return ReadLine(c.rb)
|
||||||
|
}
|
||||||
|
|
||||||
|
//A client sends to the Redis server a RESP Array consisting of just Bulk Strings.
|
||||||
|
func (c *respClient) readRequest() ([][]byte, error) {
|
||||||
|
l, err := c.readLine()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if len(l) == 0 || l[0] != '*' {
|
||||||
|
return nil, errReadRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
var nparams int
|
||||||
|
if nparams, err = strconv.Atoi(ledis.String(l[1:])); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if nparams <= 0 {
|
||||||
|
return nil, errReadRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
req := make([][]byte, 0, nparams)
|
||||||
|
var n int
|
||||||
|
for i := 0; i < nparams; i++ {
|
||||||
|
if l, err = c.readLine(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(l) == 0 {
|
||||||
|
return nil, errReadRequest
|
||||||
|
} else if l[0] == '$' {
|
||||||
|
//handle resp string
|
||||||
|
if n, err = strconv.Atoi(ledis.String(l[1:])); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if n == -1 {
|
||||||
|
req = append(req, nil)
|
||||||
|
} else {
|
||||||
|
buf := make([]byte, n)
|
||||||
|
if _, err = io.ReadFull(c.rb, buf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l, err = c.readLine(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if len(l) != 0 {
|
||||||
|
return nil, errors.New("bad bulk string format")
|
||||||
|
}
|
||||||
|
|
||||||
|
req = append(req, buf)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return nil, errReadRequest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *respClient) handleRequest(reqData [][]byte) {
|
||||||
|
req := c.req
|
||||||
|
|
||||||
|
if len(reqData) == 0 {
|
||||||
|
c.req.cmd = ""
|
||||||
|
c.req.args = reqData[0:0]
|
||||||
|
} else {
|
||||||
|
c.req.cmd = strings.ToLower(ledis.String(reqData[0]))
|
||||||
|
c.req.args = reqData[1:]
|
||||||
|
}
|
||||||
|
if c.req.cmd == "quit" {
|
||||||
|
c.req.resp.writeStatus(OK)
|
||||||
|
c.req.resp.flush()
|
||||||
|
c.conn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.db = c.db
|
||||||
|
|
||||||
|
c.req.perform()
|
||||||
|
|
||||||
|
c.db = req.db // "SELECT"
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// response writer
|
||||||
|
|
||||||
|
func newWriterRESP(conn net.Conn) *respWriter {
|
||||||
|
w := new(respWriter)
|
||||||
|
w.buff = bufio.NewWriterSize(conn, 256)
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *respWriter) writeError(err error) {
|
||||||
|
w.buff.Write(ledis.Slice("-ERR"))
|
||||||
|
if err != nil {
|
||||||
|
w.buff.WriteByte(' ')
|
||||||
|
w.buff.Write(ledis.Slice(err.Error()))
|
||||||
|
}
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *respWriter) writeStatus(status string) {
|
||||||
|
w.buff.WriteByte('+')
|
||||||
|
w.buff.Write(ledis.Slice(status))
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *respWriter) writeInteger(n int64) {
|
||||||
|
w.buff.WriteByte(':')
|
||||||
|
w.buff.Write(ledis.StrPutInt64(n))
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *respWriter) writeBulk(b []byte) {
|
||||||
|
w.buff.WriteByte('$')
|
||||||
|
if b == nil {
|
||||||
|
w.buff.Write(NullBulk)
|
||||||
|
} else {
|
||||||
|
w.buff.Write(ledis.Slice(strconv.Itoa(len(b))))
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
w.buff.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *respWriter) writeArray(lst []interface{}) {
|
||||||
|
w.buff.WriteByte('*')
|
||||||
|
if lst == nil {
|
||||||
|
w.buff.Write(NullArray)
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
} else {
|
||||||
|
w.buff.Write(ledis.Slice(strconv.Itoa(len(lst))))
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
|
||||||
|
for i := 0; i < len(lst); i++ {
|
||||||
|
switch v := lst[i].(type) {
|
||||||
|
case []interface{}:
|
||||||
|
w.writeArray(v)
|
||||||
|
case []byte:
|
||||||
|
w.writeBulk(v)
|
||||||
|
case nil:
|
||||||
|
w.writeBulk(nil)
|
||||||
|
case int64:
|
||||||
|
w.writeInteger(v)
|
||||||
|
default:
|
||||||
|
panic("invalid array type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *respWriter) writeSliceArray(lst [][]byte) {
|
||||||
|
w.buff.WriteByte('*')
|
||||||
|
if lst == nil {
|
||||||
|
w.buff.Write(NullArray)
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
} else {
|
||||||
|
w.buff.Write(ledis.Slice(strconv.Itoa(len(lst))))
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
|
||||||
|
for i := 0; i < len(lst); i++ {
|
||||||
|
w.writeBulk(lst[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *respWriter) writeFVPairArray(lst []ledis.FVPair) {
|
||||||
|
w.buff.WriteByte('*')
|
||||||
|
if lst == nil {
|
||||||
|
w.buff.Write(NullArray)
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
} else {
|
||||||
|
w.buff.Write(ledis.Slice(strconv.Itoa(len(lst) * 2)))
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
|
||||||
|
for i := 0; i < len(lst); i++ {
|
||||||
|
w.writeBulk(lst[i].Field)
|
||||||
|
w.writeBulk(lst[i].Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *respWriter) writeScorePairArray(lst []ledis.ScorePair, withScores bool) {
|
||||||
|
w.buff.WriteByte('*')
|
||||||
|
if lst == nil {
|
||||||
|
w.buff.Write(NullArray)
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
} else {
|
||||||
|
if withScores {
|
||||||
|
w.buff.Write(ledis.Slice(strconv.Itoa(len(lst) * 2)))
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
} else {
|
||||||
|
w.buff.Write(ledis.Slice(strconv.Itoa(len(lst))))
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(lst); i++ {
|
||||||
|
w.writeBulk(lst[i].Member)
|
||||||
|
|
||||||
|
if withScores {
|
||||||
|
w.writeBulk(ledis.StrPutInt64(lst[i].Score))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *respWriter) writeBulkFrom(n int64, rb io.Reader) {
|
||||||
|
w.buff.WriteByte('$')
|
||||||
|
w.buff.Write(ledis.Slice(strconv.FormatInt(n, 10)))
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
|
||||||
|
io.Copy(w.buff, rb)
|
||||||
|
w.buff.Write(Delims)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *respWriter) flush() {
|
||||||
|
w.buff.Flush()
|
||||||
|
}
|
|
@ -5,36 +5,36 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func bgetCommand(c *client) error {
|
func bgetCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.BGet(args[0]); err != nil {
|
if v, err := req.db.BGet(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeBulk(v)
|
req.resp.writeBulk(v)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func bdeleteCommand(c *client) error {
|
func bdeleteCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.BDelete(args[0]); err != nil {
|
if n, err := req.db.BDelete(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func bsetbitCommand(c *client) error {
|
func bsetbitCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 3 {
|
if len(args) != 3 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -58,16 +58,16 @@ func bsetbitCommand(c *client) error {
|
||||||
return ErrBool
|
return ErrBool
|
||||||
}
|
}
|
||||||
|
|
||||||
if ori, err := c.db.BSetBit(args[0], offset, uint8(val)); err != nil {
|
if ori, err := req.db.BSetBit(args[0], offset, uint8(val)); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(int64(ori))
|
req.resp.writeInteger(int64(ori))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func bgetbitCommand(c *client) error {
|
func bgetbitCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -78,16 +78,16 @@ func bgetbitCommand(c *client) error {
|
||||||
return ErrOffset
|
return ErrOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.BGetBit(args[0], offset); err != nil {
|
if v, err := req.db.BGetBit(args[0], offset); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(int64(v))
|
req.resp.writeInteger(int64(v))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func bmsetbitCommand(c *client) error {
|
func bmsetbitCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) < 3 {
|
if len(args) < 3 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -124,16 +124,16 @@ func bmsetbitCommand(c *client) error {
|
||||||
pairs[i].Val = uint8(val)
|
pairs[i].Val = uint8(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
if place, err := c.db.BMSetBit(key, pairs...); err != nil {
|
if place, err := req.db.BMSetBit(key, pairs...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(place)
|
req.resp.writeInteger(place)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func bcountCommand(c *client) error {
|
func bcountCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
argCnt := len(args)
|
argCnt := len(args)
|
||||||
|
|
||||||
if !(argCnt > 0 && argCnt <= 3) {
|
if !(argCnt > 0 && argCnt <= 3) {
|
||||||
|
@ -159,16 +159,16 @@ func bcountCommand(c *client) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cnt, err := c.db.BCount(args[0], start, end); err != nil {
|
if cnt, err := req.db.BCount(args[0], start, end); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(int64(cnt))
|
req.resp.writeInteger(int64(cnt))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func boptCommand(c *client) error {
|
func boptCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -194,16 +194,16 @@ func boptCommand(c *client) error {
|
||||||
if len(srcKeys) == 0 {
|
if len(srcKeys) == 0 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
if blen, err := c.db.BOperation(op, dstKey, srcKeys...); err != nil {
|
if blen, err := req.db.BOperation(op, dstKey, srcKeys...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(int64(blen))
|
req.resp.writeInteger(int64(blen))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func bexpireCommand(c *client) error {
|
func bexpireCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -213,17 +213,17 @@ func bexpireCommand(c *client) error {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.BExpire(args[0], duration); err != nil {
|
if v, err := req.db.BExpire(args[0], duration); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(v)
|
req.resp.writeInteger(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func bexpireAtCommand(c *client) error {
|
func bexpireAtCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -233,40 +233,40 @@ func bexpireAtCommand(c *client) error {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.BExpireAt(args[0], when); err != nil {
|
if v, err := req.db.BExpireAt(args[0], when); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(v)
|
req.resp.writeInteger(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func bttlCommand(c *client) error {
|
func bttlCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.BTTL(args[0]); err != nil {
|
if v, err := req.db.BTTL(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(v)
|
req.resp.writeInteger(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func bpersistCommand(c *client) error {
|
func bpersistCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.BPersist(args[0]); err != nil {
|
if n, err := req.db.BPersist(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -4,87 +4,87 @@ import (
|
||||||
"github.com/siddontang/ledisdb/ledis"
|
"github.com/siddontang/ledisdb/ledis"
|
||||||
)
|
)
|
||||||
|
|
||||||
func hsetCommand(c *client) error {
|
func hsetCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 3 {
|
if len(args) != 3 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.HSet(args[0], args[1], args[2]); err != nil {
|
if n, err := req.db.HSet(args[0], args[1], args[2]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hgetCommand(c *client) error {
|
func hgetCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.HGet(args[0], args[1]); err != nil {
|
if v, err := req.db.HGet(args[0], args[1]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeBulk(v)
|
req.resp.writeBulk(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hexistsCommand(c *client) error {
|
func hexistsCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
var n int64 = 1
|
var n int64 = 1
|
||||||
if v, err := c.db.HGet(args[0], args[1]); err != nil {
|
if v, err := req.db.HGet(args[0], args[1]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
n = 0
|
n = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hdelCommand(c *client) error {
|
func hdelCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.HDel(args[0], args[1:]...); err != nil {
|
if n, err := req.db.HDel(args[0], args[1:]...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hlenCommand(c *client) error {
|
func hlenCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.HLen(args[0]); err != nil {
|
if n, err := req.db.HLen(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hincrbyCommand(c *client) error {
|
func hincrbyCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 3 {
|
if len(args) != 3 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -95,16 +95,16 @@ func hincrbyCommand(c *client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var n int64
|
var n int64
|
||||||
if n, err = c.db.HIncrBy(args[0], args[1], delta); err != nil {
|
if n, err = req.db.HIncrBy(args[0], args[1], delta); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hmsetCommand(c *client) error {
|
func hmsetCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) < 3 {
|
if len(args) < 3 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -123,107 +123,107 @@ func hmsetCommand(c *client) error {
|
||||||
kvs[i].Value = args[2*i+1]
|
kvs[i].Value = args[2*i+1]
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.db.HMset(key, kvs...); err != nil {
|
if err := req.db.HMset(key, kvs...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeStatus(OK)
|
req.resp.writeStatus(OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hmgetCommand(c *client) error {
|
func hmgetCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.HMget(args[0], args[1:]...); err != nil {
|
if v, err := req.db.HMget(args[0], args[1:]...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeSliceArray(v)
|
req.resp.writeSliceArray(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hgetallCommand(c *client) error {
|
func hgetallCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.HGetAll(args[0]); err != nil {
|
if v, err := req.db.HGetAll(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeFVPairArray(v)
|
req.resp.writeFVPairArray(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hkeysCommand(c *client) error {
|
func hkeysCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.HKeys(args[0]); err != nil {
|
if v, err := req.db.HKeys(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeSliceArray(v)
|
req.resp.writeSliceArray(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hvalsCommand(c *client) error {
|
func hvalsCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.HValues(args[0]); err != nil {
|
if v, err := req.db.HValues(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeSliceArray(v)
|
req.resp.writeSliceArray(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hclearCommand(c *client) error {
|
func hclearCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.HClear(args[0]); err != nil {
|
if n, err := req.db.HClear(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hmclearCommand(c *client) error {
|
func hmclearCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.HMclear(args...); err != nil {
|
if n, err := req.db.HMclear(args...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hexpireCommand(c *client) error {
|
func hexpireCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -233,17 +233,17 @@ func hexpireCommand(c *client) error {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.HExpire(args[0], duration); err != nil {
|
if v, err := req.db.HExpire(args[0], duration); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(v)
|
req.resp.writeInteger(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hexpireAtCommand(c *client) error {
|
func hexpireAtCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -253,40 +253,40 @@ func hexpireAtCommand(c *client) error {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.HExpireAt(args[0], when); err != nil {
|
if v, err := req.db.HExpireAt(args[0], when); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(v)
|
req.resp.writeInteger(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func httlCommand(c *client) error {
|
func httlCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.HTTL(args[0]); err != nil {
|
if v, err := req.db.HTTL(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(v)
|
req.resp.writeInteger(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hpersistCommand(c *client) error {
|
func hpersistCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.HPersist(args[0]); err != nil {
|
if n, err := req.db.HPersist(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
130
server/cmd_kv.go
130
server/cmd_kv.go
|
@ -4,112 +4,112 @@ import (
|
||||||
"github.com/siddontang/ledisdb/ledis"
|
"github.com/siddontang/ledisdb/ledis"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getCommand(c *client) error {
|
func getCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.Get(args[0]); err != nil {
|
if v, err := req.db.Get(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeBulk(v)
|
req.resp.writeBulk(v)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setCommand(c *client) error {
|
func setCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.db.Set(args[0], args[1]); err != nil {
|
if err := req.db.Set(args[0], args[1]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeStatus(OK)
|
req.resp.writeStatus(OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getsetCommand(c *client) error {
|
func getsetCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.GetSet(args[0], args[1]); err != nil {
|
if v, err := req.db.GetSet(args[0], args[1]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeBulk(v)
|
req.resp.writeBulk(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setnxCommand(c *client) error {
|
func setnxCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.SetNX(args[0], args[1]); err != nil {
|
if n, err := req.db.SetNX(args[0], args[1]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func existsCommand(c *client) error {
|
func existsCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.Exists(args[0]); err != nil {
|
if n, err := req.db.Exists(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func incrCommand(c *client) error {
|
func incrCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.Incr(c.args[0]); err != nil {
|
if n, err := req.db.Incr(req.args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decrCommand(c *client) error {
|
func decrCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.Decr(c.args[0]); err != nil {
|
if n, err := req.db.Decr(req.args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func incrbyCommand(c *client) error {
|
func incrbyCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -119,17 +119,17 @@ func incrbyCommand(c *client) error {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.IncrBy(c.args[0], delta); err != nil {
|
if n, err := req.db.IncrBy(req.args[0], delta); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decrbyCommand(c *client) error {
|
func decrbyCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -139,32 +139,32 @@ func decrbyCommand(c *client) error {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.DecrBy(c.args[0], delta); err != nil {
|
if n, err := req.db.DecrBy(req.args[0], delta); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func delCommand(c *client) error {
|
func delCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.Del(args...); err != nil {
|
if n, err := req.db.Del(args...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func msetCommand(c *client) error {
|
func msetCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) == 0 || len(args)%2 != 0 {
|
if len(args) == 0 || len(args)%2 != 0 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -175,36 +175,36 @@ func msetCommand(c *client) error {
|
||||||
kvs[i].Value = args[2*i+1]
|
kvs[i].Value = args[2*i+1]
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.db.MSet(kvs...); err != nil {
|
if err := req.db.MSet(kvs...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeStatus(OK)
|
req.resp.writeStatus(OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// func setexCommand(c *client) error {
|
// func setexCommand(req *requestContext) error {
|
||||||
// return nil
|
// return nil
|
||||||
// }
|
// }
|
||||||
|
|
||||||
func mgetCommand(c *client) error {
|
func mgetCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.MGet(args...); err != nil {
|
if v, err := req.db.MGet(args...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeSliceArray(v)
|
req.resp.writeSliceArray(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func expireCommand(c *client) error {
|
func expireCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -214,17 +214,17 @@ func expireCommand(c *client) error {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.Expire(args[0], duration); err != nil {
|
if v, err := req.db.Expire(args[0], duration); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(v)
|
req.resp.writeInteger(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func expireAtCommand(c *client) error {
|
func expireAtCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -234,40 +234,40 @@ func expireAtCommand(c *client) error {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.ExpireAt(args[0], when); err != nil {
|
if v, err := req.db.ExpireAt(args[0], when); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(v)
|
req.resp.writeInteger(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ttlCommand(c *client) error {
|
func ttlCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.TTL(args[0]); err != nil {
|
if v, err := req.db.TTL(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(v)
|
req.resp.writeInteger(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func persistCommand(c *client) error {
|
func persistCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.Persist(args[0]); err != nil {
|
if n, err := req.db.Persist(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -4,83 +4,83 @@ import (
|
||||||
"github.com/siddontang/ledisdb/ledis"
|
"github.com/siddontang/ledisdb/ledis"
|
||||||
)
|
)
|
||||||
|
|
||||||
func lpushCommand(c *client) error {
|
func lpushCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.LPush(args[0], args[1:]...); err != nil {
|
if n, err := req.db.LPush(args[0], args[1:]...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpushCommand(c *client) error {
|
func rpushCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.RPush(args[0], args[1:]...); err != nil {
|
if n, err := req.db.RPush(args[0], args[1:]...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lpopCommand(c *client) error {
|
func lpopCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.LPop(args[0]); err != nil {
|
if v, err := req.db.LPop(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeBulk(v)
|
req.resp.writeBulk(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpopCommand(c *client) error {
|
func rpopCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.RPop(args[0]); err != nil {
|
if v, err := req.db.RPop(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeBulk(v)
|
req.resp.writeBulk(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func llenCommand(c *client) error {
|
func llenCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.LLen(args[0]); err != nil {
|
if n, err := req.db.LLen(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lindexCommand(c *client) error {
|
func lindexCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -90,17 +90,17 @@ func lindexCommand(c *client) error {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.LIndex(args[0], int32(index)); err != nil {
|
if v, err := req.db.LIndex(args[0], int32(index)); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeBulk(v)
|
req.resp.writeBulk(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lrangeCommand(c *client) error {
|
func lrangeCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 3 {
|
if len(args) != 3 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -119,47 +119,47 @@ func lrangeCommand(c *client) error {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.LRange(args[0], int32(start), int32(stop)); err != nil {
|
if v, err := req.db.LRange(args[0], int32(start), int32(stop)); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeSliceArray(v)
|
req.resp.writeSliceArray(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lclearCommand(c *client) error {
|
func lclearCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.LClear(args[0]); err != nil {
|
if n, err := req.db.LClear(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lmclearCommand(c *client) error {
|
func lmclearCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.LMclear(args...); err != nil {
|
if n, err := req.db.LMclear(args...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lexpireCommand(c *client) error {
|
func lexpireCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -169,17 +169,17 @@ func lexpireCommand(c *client) error {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.LExpire(args[0], duration); err != nil {
|
if v, err := req.db.LExpire(args[0], duration); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(v)
|
req.resp.writeInteger(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lexpireAtCommand(c *client) error {
|
func lexpireAtCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -189,40 +189,40 @@ func lexpireAtCommand(c *client) error {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.LExpireAt(args[0], when); err != nil {
|
if v, err := req.db.LExpireAt(args[0], when); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(v)
|
req.resp.writeInteger(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lttlCommand(c *client) error {
|
func lttlCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.LTTL(args[0]); err != nil {
|
if v, err := req.db.LTTL(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(v)
|
req.resp.writeInteger(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lpersistCommand(c *client) error {
|
func lpersistCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.LPersist(args[0]); err != nil {
|
if n, err := req.db.LPersist(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -11,8 +11,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func slaveofCommand(c *client) error {
|
func slaveofCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
|
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
|
@ -31,23 +31,23 @@ func slaveofCommand(c *client) error {
|
||||||
masterAddr = fmt.Sprintf("%s:%s", args[0], args[1])
|
masterAddr = fmt.Sprintf("%s:%s", args[0], args[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.app.slaveof(masterAddr); err != nil {
|
if err := req.app.slaveof(masterAddr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.writeStatus(OK)
|
req.resp.writeStatus(OK)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fullsyncCommand(c *client) error {
|
func fullsyncCommand(req *requestContext) error {
|
||||||
//todo, multi fullsync may use same dump file
|
//todo, multi fullsync may use same dump file
|
||||||
dumpFile, err := ioutil.TempFile(c.app.cfg.DataDir, "dump_")
|
dumpFile, err := ioutil.TempFile(req.app.cfg.DataDir, "dump_")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = c.app.ldb.Dump(dumpFile); err != nil {
|
if err = req.app.ldb.Dump(dumpFile); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ func fullsyncCommand(c *client) error {
|
||||||
|
|
||||||
dumpFile.Seek(0, os.SEEK_SET)
|
dumpFile.Seek(0, os.SEEK_SET)
|
||||||
|
|
||||||
c.writeBulkFrom(n, dumpFile)
|
req.resp.writeBulkFrom(n, dumpFile)
|
||||||
|
|
||||||
name := dumpFile.Name()
|
name := dumpFile.Name()
|
||||||
dumpFile.Close()
|
dumpFile.Close()
|
||||||
|
@ -68,8 +68,8 @@ func fullsyncCommand(c *client) error {
|
||||||
|
|
||||||
var reserveInfoSpace = make([]byte, 16)
|
var reserveInfoSpace = make([]byte, 16)
|
||||||
|
|
||||||
func syncCommand(c *client) error {
|
func syncCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -87,32 +87,32 @@ func syncCommand(c *client) error {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
c.syncBuf.Reset()
|
req.syncBuf.Reset()
|
||||||
|
|
||||||
//reserve space to write master info
|
//reserve space to write master info
|
||||||
if _, err := c.syncBuf.Write(reserveInfoSpace); err != nil {
|
if _, err := req.syncBuf.Write(reserveInfoSpace); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &ledis.MasterInfo{logIndex, logPos}
|
m := &ledis.MasterInfo{logIndex, logPos}
|
||||||
|
|
||||||
if _, err := c.app.ldb.ReadEventsTo(m, &c.syncBuf); err != nil {
|
if _, err := req.app.ldb.ReadEventsTo(m, &req.syncBuf); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
buf := c.syncBuf.Bytes()
|
buf := req.syncBuf.Bytes()
|
||||||
|
|
||||||
binary.BigEndian.PutUint64(buf[0:], uint64(m.LogFileIndex))
|
binary.BigEndian.PutUint64(buf[0:], uint64(m.LogFileIndex))
|
||||||
binary.BigEndian.PutUint64(buf[8:], uint64(m.LogPos))
|
binary.BigEndian.PutUint64(buf[8:], uint64(m.LogPos))
|
||||||
|
|
||||||
if len(c.compressBuf) < snappy.MaxEncodedLen(len(buf)) {
|
if len(req.compressBuf) < snappy.MaxEncodedLen(len(buf)) {
|
||||||
c.compressBuf = make([]byte, snappy.MaxEncodedLen(len(buf)))
|
req.compressBuf = make([]byte, snappy.MaxEncodedLen(len(buf)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if buf, err = snappy.Encode(c.compressBuf, buf); err != nil {
|
if buf, err = snappy.Encode(req.compressBuf, buf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.writeBulk(buf)
|
req.resp.writeBulk(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -12,8 +12,8 @@ import (
|
||||||
|
|
||||||
var errScoreOverflow = errors.New("zset score overflow")
|
var errScoreOverflow = errors.New("zset score overflow")
|
||||||
|
|
||||||
func zaddCommand(c *client) error {
|
func zaddCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) < 3 {
|
if len(args) < 3 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -36,66 +36,66 @@ func zaddCommand(c *client) error {
|
||||||
params[i].Member = args[2*i+1]
|
params[i].Member = args[2*i+1]
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.ZAdd(key, params...); err != nil {
|
if n, err := req.db.ZAdd(key, params...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zcardCommand(c *client) error {
|
func zcardCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.ZCard(args[0]); err != nil {
|
if n, err := req.db.ZCard(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zscoreCommand(c *client) error {
|
func zscoreCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if s, err := c.db.ZScore(args[0], args[1]); err != nil {
|
if s, err := req.db.ZScore(args[0], args[1]); err != nil {
|
||||||
if err == ledis.ErrScoreMiss {
|
if err == ledis.ErrScoreMiss {
|
||||||
c.writeBulk(nil)
|
req.resp.writeBulk(nil)
|
||||||
} else {
|
} else {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.writeBulk(ledis.StrPutInt64(s))
|
req.resp.writeBulk(ledis.StrPutInt64(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zremCommand(c *client) error {
|
func zremCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.ZRem(args[0], args[1:]...); err != nil {
|
if n, err := req.db.ZRem(args[0], args[1:]...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zincrbyCommand(c *client) error {
|
func zincrbyCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 3 {
|
if len(args) != 3 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -107,10 +107,10 @@ func zincrbyCommand(c *client) error {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.ZIncrBy(key, delta, args[2]); err != nil {
|
if v, err := req.db.ZIncrBy(key, delta, args[2]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeBulk(ledis.StrPutInt64(v))
|
req.resp.writeBulk(ledis.StrPutInt64(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -157,6 +157,10 @@ func zparseScoreRange(minBuf []byte, maxBuf []byte) (min int64, max int64, err e
|
||||||
err = ErrCmdParams
|
err = ErrCmdParams
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if maxBuf[0] == '(' {
|
||||||
|
ropen = true
|
||||||
|
maxBuf = maxBuf[1:]
|
||||||
|
}
|
||||||
|
|
||||||
if maxBuf[0] == '(' {
|
if maxBuf[0] == '(' {
|
||||||
ropen = true
|
ropen = true
|
||||||
|
@ -182,8 +186,8 @@ func zparseScoreRange(minBuf []byte, maxBuf []byte) (min int64, max int64, err e
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func zcountCommand(c *client) error {
|
func zcountCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 3 {
|
if len(args) != 3 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -194,77 +198,77 @@ func zcountCommand(c *client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if min > max {
|
if min > max {
|
||||||
c.writeInteger(0)
|
req.resp.writeInteger(0)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.ZCount(args[0], min, max); err != nil {
|
if n, err := req.db.ZCount(args[0], min, max); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zrankCommand(c *client) error {
|
func zrankCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.ZRank(args[0], args[1]); err != nil {
|
if n, err := req.db.ZRank(args[0], args[1]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if n == -1 {
|
} else if n == -1 {
|
||||||
c.writeBulk(nil)
|
req.resp.writeBulk(nil)
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zrevrankCommand(c *client) error {
|
func zrevrankCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.ZRevRank(args[0], args[1]); err != nil {
|
if n, err := req.db.ZRevRank(args[0], args[1]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if n == -1 {
|
} else if n == -1 {
|
||||||
c.writeBulk(nil)
|
req.resp.writeBulk(nil)
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zremrangebyrankCommand(c *client) error {
|
func zremrangebyrankCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 3 {
|
if len(args) != 3 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
key := args[0]
|
key := args[0]
|
||||||
|
|
||||||
start, stop, err := zparseRange(c, args[1], args[2])
|
start, stop, err := zparseRange(req, args[1], args[2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.ZRemRangeByRank(key, start, stop); err != nil {
|
if n, err := req.db.ZRemRangeByRank(key, start, stop); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zremrangebyscoreCommand(c *client) error {
|
func zremrangebyscoreCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 3 {
|
if len(args) != 3 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -275,16 +279,16 @@ func zremrangebyscoreCommand(c *client) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.ZRemRangeByScore(key, min, max); err != nil {
|
if n, err := req.db.ZRemRangeByScore(key, min, max); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zparseRange(c *client, a1 []byte, a2 []byte) (start int, stop int, err error) {
|
func zparseRange(req *requestContext, a1 []byte, a2 []byte) (start int, stop int, err error) {
|
||||||
if start, err = strconv.Atoi(ledis.String(a1)); err != nil {
|
if start, err = strconv.Atoi(ledis.String(a1)); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -296,15 +300,15 @@ func zparseRange(c *client, a1 []byte, a2 []byte) (start int, stop int, err erro
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func zrangeGeneric(c *client, reverse bool) error {
|
func zrangeGeneric(req *requestContext, reverse bool) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) < 3 {
|
if len(args) < 3 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
key := args[0]
|
key := args[0]
|
||||||
|
|
||||||
start, stop, err := zparseRange(c, args[1], args[2])
|
start, stop, err := zparseRange(req, args[1], args[2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
@ -323,24 +327,24 @@ func zrangeGeneric(c *client, reverse bool) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if datas, err := c.db.ZRangeGeneric(key, start, stop, reverse); err != nil {
|
if datas, err := req.db.ZRangeGeneric(key, start, stop, reverse); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeScorePairArray(datas, withScores)
|
req.resp.writeScorePairArray(datas, withScores)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zrangeCommand(c *client) error {
|
func zrangeCommand(req *requestContext) error {
|
||||||
return zrangeGeneric(c, false)
|
return zrangeGeneric(req, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func zrevrangeCommand(c *client) error {
|
func zrevrangeCommand(req *requestContext) error {
|
||||||
return zrangeGeneric(c, true)
|
return zrangeGeneric(req, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func zrangebyscoreGeneric(c *client, reverse bool) error {
|
func zrangebyscoreGeneric(req *requestContext, reverse bool) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) < 3 {
|
if len(args) < 3 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -396,59 +400,59 @@ func zrangebyscoreGeneric(c *client, reverse bool) error {
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
//for ledis, if offset < 0, a empty will return
|
//for ledis, if offset < 0, a empty will return
|
||||||
//so here we directly return a empty array
|
//so here we directly return a empty array
|
||||||
c.writeArray([]interface{}{})
|
req.resp.writeArray([]interface{}{})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if datas, err := c.db.ZRangeByScoreGeneric(key, min, max, offset, count, reverse); err != nil {
|
if datas, err := req.db.ZRangeByScoreGeneric(key, min, max, offset, count, reverse); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeScorePairArray(datas, withScores)
|
req.resp.writeScorePairArray(datas, withScores)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zrangebyscoreCommand(c *client) error {
|
func zrangebyscoreCommand(req *requestContext) error {
|
||||||
return zrangebyscoreGeneric(c, false)
|
return zrangebyscoreGeneric(req, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func zrevrangebyscoreCommand(c *client) error {
|
func zrevrangebyscoreCommand(req *requestContext) error {
|
||||||
return zrangebyscoreGeneric(c, true)
|
return zrangebyscoreGeneric(req, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func zclearCommand(c *client) error {
|
func zclearCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.ZClear(args[0]); err != nil {
|
if n, err := req.db.ZClear(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zmclearCommand(c *client) error {
|
func zmclearCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.ZMclear(args...); err != nil {
|
if n, err := req.db.ZMclear(args...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zexpireCommand(c *client) error {
|
func zexpireCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -458,17 +462,17 @@ func zexpireCommand(c *client) error {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.ZExpire(args[0], duration); err != nil {
|
if v, err := req.db.ZExpire(args[0], duration); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(v)
|
req.resp.writeInteger(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zexpireAtCommand(c *client) error {
|
func zexpireAtCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
@ -478,39 +482,39 @@ func zexpireAtCommand(c *client) error {
|
||||||
return ErrValue
|
return ErrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.ZExpireAt(args[0], when); err != nil {
|
if v, err := req.db.ZExpireAt(args[0], when); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(v)
|
req.resp.writeInteger(v)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zttlCommand(c *client) error {
|
func zttlCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := c.db.ZTTL(args[0]); err != nil {
|
if v, err := req.db.ZTTL(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(v)
|
req.resp.writeInteger(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zpersistCommand(c *client) error {
|
func zpersistCommand(req *requestContext) error {
|
||||||
args := c.args
|
args := req.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := c.db.ZPersist(args[0]); err != nil {
|
if n, err := req.db.ZPersist(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeInteger(n)
|
req.resp.writeInteger(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommandFunc func(c *client) error
|
type CommandFunc func(req *requestContext) error
|
||||||
|
|
||||||
var regCmds = map[string]CommandFunc{}
|
var regCmds = map[string]CommandFunc{}
|
||||||
|
|
||||||
|
@ -20,33 +20,33 @@ func register(name string, f CommandFunc) {
|
||||||
regCmds[name] = f
|
regCmds[name] = f
|
||||||
}
|
}
|
||||||
|
|
||||||
func pingCommand(c *client) error {
|
func pingCommand(req *requestContext) error {
|
||||||
c.writeStatus(PONG)
|
req.resp.writeStatus(PONG)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func echoCommand(c *client) error {
|
func echoCommand(req *requestContext) error {
|
||||||
if len(c.args) != 1 {
|
if len(req.args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
c.writeBulk(c.args[0])
|
req.resp.writeBulk(req.args[0])
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectCommand(c *client) error {
|
func selectCommand(req *requestContext) error {
|
||||||
if len(c.args) != 1 {
|
if len(req.args) != 1 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if index, err := strconv.Atoi(ledis.String(c.args[0])); err != nil {
|
if index, err := strconv.Atoi(ledis.String(req.args[0])); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
if db, err := c.ldb.Select(index); err != nil {
|
if db, err := req.ldb.Select(index); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.db = db
|
req.db = db
|
||||||
c.writeStatus(OK)
|
req.resp.writeStatus(OK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Addr string `json:"addr"`
|
Addr string `json:"addr"`
|
||||||
|
|
||||||
|
HttpAddr string `json:"http_addr"`
|
||||||
|
|
||||||
DataDir string `json:"data_dir"`
|
DataDir string `json:"data_dir"`
|
||||||
|
|
||||||
DB struct {
|
DB struct {
|
||||||
|
|
|
@ -26,4 +26,14 @@
|
||||||
//
|
//
|
||||||
// After you send slaveof command, the slave will start to sync master's binlog and replicate from binlog.
|
// After you send slaveof command, the slave will start to sync master's binlog and replicate from binlog.
|
||||||
//
|
//
|
||||||
|
// HTTP Interface
|
||||||
|
//
|
||||||
|
// LedisDB provides http interfaces for most commands(except the replication commands)
|
||||||
|
//
|
||||||
|
// curl http://127.0.0.1:11181/SET/hello/world
|
||||||
|
// → {"SET":[true,"OK"]}
|
||||||
|
//
|
||||||
|
// curl http://127.0.0.1:11181/0/GET/hello?type=json
|
||||||
|
// → {"GET":"world"}
|
||||||
|
//
|
||||||
package server
|
package server
|
||||||
|
|
|
@ -72,8 +72,8 @@ func (m *MasterInfo) Load(filePath string) error {
|
||||||
type master struct {
|
type master struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
|
|
||||||
c net.Conn
|
conn net.Conn
|
||||||
rb *bufio.Reader
|
rb *bufio.Reader
|
||||||
|
|
||||||
app *App
|
app *App
|
||||||
|
|
||||||
|
@ -114,9 +114,9 @@ func (m *master) Close() {
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.c != nil {
|
if m.conn != nil {
|
||||||
m.c.Close()
|
m.conn.Close()
|
||||||
m.c = nil
|
m.conn = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
m.wg.Wait()
|
m.wg.Wait()
|
||||||
|
@ -135,17 +135,17 @@ func (m *master) connect() error {
|
||||||
return fmt.Errorf("no assign master addr")
|
return fmt.Errorf("no assign master addr")
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.c != nil {
|
if m.conn != nil {
|
||||||
m.c.Close()
|
m.conn.Close()
|
||||||
m.c = nil
|
m.conn = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if c, err := net.Dial("tcp", m.info.Addr); err != nil {
|
if conn, err := net.Dial("tcp", m.info.Addr); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
m.c = c
|
m.conn = conn
|
||||||
|
|
||||||
m.rb = bufio.NewReaderSize(m.c, 4096)
|
m.rb = bufio.NewReaderSize(m.conn, 4096)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -248,7 +248,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *master) fullSync() error {
|
func (m *master) fullSync() error {
|
||||||
if _, err := m.c.Write(fullSyncCmd); err != nil {
|
if _, err := m.conn.Write(fullSyncCmd); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,7 +291,7 @@ func (m *master) sync() error {
|
||||||
|
|
||||||
cmd := ledis.Slice(fmt.Sprintf(syncCmdFormat, len(logIndexStr),
|
cmd := ledis.Slice(fmt.Sprintf(syncCmdFormat, len(logIndexStr),
|
||||||
logIndexStr, len(logPosStr), logPosStr))
|
logIndexStr, len(logPosStr), logPosStr))
|
||||||
if _, err := m.c.Write(cmd); err != nil {
|
if _, err := m.conn.Write(cmd); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/siddontang/ledisdb/ledis"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type responseWriter interface {
|
||||||
|
writeError(error)
|
||||||
|
writeStatus(string)
|
||||||
|
writeInteger(int64)
|
||||||
|
writeBulk([]byte)
|
||||||
|
writeArray([]interface{})
|
||||||
|
writeSliceArray([][]byte)
|
||||||
|
writeFVPairArray([]ledis.FVPair)
|
||||||
|
writeScorePairArray([]ledis.ScorePair, bool)
|
||||||
|
writeBulkFrom(int64, io.Reader)
|
||||||
|
flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
type requestContext struct {
|
||||||
|
app *App
|
||||||
|
ldb *ledis.Ledis
|
||||||
|
db *ledis.DB
|
||||||
|
|
||||||
|
remoteAddr string
|
||||||
|
cmd string
|
||||||
|
args [][]byte
|
||||||
|
|
||||||
|
resp responseWriter
|
||||||
|
|
||||||
|
syncBuf bytes.Buffer
|
||||||
|
compressBuf []byte
|
||||||
|
|
||||||
|
reqErr chan error
|
||||||
|
|
||||||
|
buf bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRequestContext(app *App) *requestContext {
|
||||||
|
req := new(requestContext)
|
||||||
|
|
||||||
|
req.app = app
|
||||||
|
req.ldb = app.ldb
|
||||||
|
req.db, _ = app.ldb.Select(0) //use default db
|
||||||
|
|
||||||
|
req.compressBuf = make([]byte, 256)
|
||||||
|
req.reqErr = make(chan error)
|
||||||
|
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *requestContext) perform() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
if len(req.cmd) == 0 {
|
||||||
|
err = ErrEmptyCommand
|
||||||
|
} else if exeCmd, ok := regCmds[req.cmd]; !ok {
|
||||||
|
err = ErrNotFound
|
||||||
|
} else {
|
||||||
|
go func() {
|
||||||
|
req.reqErr <- exeCmd(req)
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = <-req.reqErr
|
||||||
|
}
|
||||||
|
|
||||||
|
duration := time.Since(start)
|
||||||
|
|
||||||
|
if req.app.access != nil {
|
||||||
|
fullCmd := req.catGenericCommand()
|
||||||
|
cost := duration.Nanoseconds() / 1000000
|
||||||
|
|
||||||
|
truncateLen := len(fullCmd)
|
||||||
|
if truncateLen > 256 {
|
||||||
|
truncateLen = 256
|
||||||
|
}
|
||||||
|
|
||||||
|
req.app.access.Log(req.remoteAddr, cost, fullCmd[:truncateLen], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
req.resp.writeError(err)
|
||||||
|
}
|
||||||
|
req.resp.flush()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (h *requestHandler) catFullCommand(req *requestContext) []byte {
|
||||||
|
//
|
||||||
|
// // if strings.HasSuffix(cmd, "expire") {
|
||||||
|
// // catExpireCommand(c, buffer)
|
||||||
|
// // } else {
|
||||||
|
// // catGenericCommand(c, buffer)
|
||||||
|
// // }
|
||||||
|
//
|
||||||
|
// return h.catGenericCommand(req)
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (req *requestContext) catGenericCommand() []byte {
|
||||||
|
buffer := req.buf
|
||||||
|
buffer.Reset()
|
||||||
|
|
||||||
|
buffer.Write([]byte(req.cmd))
|
||||||
|
|
||||||
|
for _, arg := range req.args {
|
||||||
|
buffer.WriteByte(' ')
|
||||||
|
buffer.Write(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.Bytes()
|
||||||
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package mdb
|
package mdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
mdb "github.com/siddontang/gomdb"
|
|
||||||
"github.com/siddontang/ledisdb/store/driver"
|
"github.com/siddontang/ledisdb/store/driver"
|
||||||
|
mdb "github.com/szferi/gomdb"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ type MDB struct {
|
||||||
func Open(c *Config) (MDB, error) {
|
func Open(c *Config) (MDB, error) {
|
||||||
path := c.Path
|
path := c.Path
|
||||||
if c.MapSize == 0 {
|
if c.MapSize == 0 {
|
||||||
c.MapSize = 1024 * 1024 * 1024
|
c.MapSize = 500 * 1024 * 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
env, err := mdb.NewEnv()
|
env, err := mdb.NewEnv()
|
||||||
|
@ -90,7 +90,7 @@ func (db MDB) BatchPut(writes []driver.Write) error {
|
||||||
|
|
||||||
for _, w := range writes {
|
for _, w := range writes {
|
||||||
if w.Value == nil {
|
if w.Value == nil {
|
||||||
itr.key, itr.value, itr.err = itr.c.Get(w.Key, mdb.SET)
|
itr.key, itr.value, itr.err = itr.c.Get(w.Key, nil, mdb.SET)
|
||||||
if itr.err == nil {
|
if itr.err == nil {
|
||||||
itr.err = itr.c.Del(0)
|
itr.err = itr.c.Del(0)
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ func (db MDB) Delete(key []byte) error {
|
||||||
itr := db.iterator(false)
|
itr := db.iterator(false)
|
||||||
defer itr.Close()
|
defer itr.Close()
|
||||||
|
|
||||||
itr.key, itr.value, itr.err = itr.c.Get(key, mdb.SET)
|
itr.key, itr.value, itr.err = itr.c.Get(key, nil, mdb.SET)
|
||||||
if itr.err == nil {
|
if itr.err == nil {
|
||||||
itr.err = itr.c.Del(0)
|
itr.err = itr.c.Del(0)
|
||||||
}
|
}
|
||||||
|
@ -161,31 +161,31 @@ func (itr *MDBIterator) Error() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (itr *MDBIterator) getCurrent() {
|
func (itr *MDBIterator) getCurrent() {
|
||||||
itr.key, itr.value, itr.err = itr.c.Get(nil, mdb.GET_CURRENT)
|
itr.key, itr.value, itr.err = itr.c.Get(nil, nil, mdb.GET_CURRENT)
|
||||||
itr.setState()
|
itr.setState()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (itr *MDBIterator) Seek(key []byte) {
|
func (itr *MDBIterator) Seek(key []byte) {
|
||||||
itr.key, itr.value, itr.err = itr.c.Get(key, mdb.SET_RANGE)
|
itr.key, itr.value, itr.err = itr.c.Get(key, nil, mdb.SET_RANGE)
|
||||||
itr.setState()
|
itr.setState()
|
||||||
}
|
}
|
||||||
func (itr *MDBIterator) Next() {
|
func (itr *MDBIterator) Next() {
|
||||||
itr.key, itr.value, itr.err = itr.c.Get(nil, mdb.NEXT)
|
itr.key, itr.value, itr.err = itr.c.Get(nil, nil, mdb.NEXT)
|
||||||
itr.setState()
|
itr.setState()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (itr *MDBIterator) Prev() {
|
func (itr *MDBIterator) Prev() {
|
||||||
itr.key, itr.value, itr.err = itr.c.Get(nil, mdb.PREV)
|
itr.key, itr.value, itr.err = itr.c.Get(nil, nil, mdb.PREV)
|
||||||
itr.setState()
|
itr.setState()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (itr *MDBIterator) First() {
|
func (itr *MDBIterator) First() {
|
||||||
itr.key, itr.value, itr.err = itr.c.Get(nil, mdb.FIRST)
|
itr.key, itr.value, itr.err = itr.c.Get(nil, nil, mdb.FIRST)
|
||||||
itr.setState()
|
itr.setState()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (itr *MDBIterator) Last() {
|
func (itr *MDBIterator) Last() {
|
||||||
itr.key, itr.value, itr.err = itr.c.Get(nil, mdb.LAST)
|
itr.key, itr.value, itr.err = itr.c.Get(nil, nil, mdb.LAST)
|
||||||
itr.setState()
|
itr.setState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package mdb
|
package mdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
mdb "github.com/siddontang/gomdb"
|
|
||||||
"github.com/siddontang/ledisdb/store/driver"
|
"github.com/siddontang/ledisdb/store/driver"
|
||||||
|
mdb "github.com/szferi/gomdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Tx struct {
|
type Tx struct {
|
||||||
|
@ -53,7 +53,7 @@ func (t *Tx) BatchPut(writes []driver.Write) error {
|
||||||
|
|
||||||
for _, w := range writes {
|
for _, w := range writes {
|
||||||
if w.Value == nil {
|
if w.Value == nil {
|
||||||
itr.key, itr.value, itr.err = itr.c.Get(w.Key, mdb.SET)
|
itr.key, itr.value, itr.err = itr.c.Get(w.Key, nil, mdb.SET)
|
||||||
if itr.err == nil {
|
if itr.err == nil {
|
||||||
itr.err = itr.c.Del(0)
|
itr.err = itr.c.Del(0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
Loading…
Reference in New Issue