mirror of https://github.com/ledisdb/ledisdb.git
Merge branch 'develop'
This commit is contained in:
commit
a3faef12e6
|
@ -40,7 +40,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/siddontang/goleveldb/leveldb",
|
"ImportPath": "github.com/siddontang/goleveldb/leveldb",
|
||||||
"Rev": "c1f6d721561c48f467b26a277741e55fd224df1e"
|
"Rev": "614b6126cf79eee8be803e7e65a3ef24fc12e44a"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/szferi/gomdb",
|
"ImportPath": "github.com/szferi/gomdb",
|
||||||
|
|
|
@ -137,6 +137,7 @@ class Ledis(object):
|
||||||
'PING': lambda r: nativestr(r) == 'PONG',
|
'PING': lambda r: nativestr(r) == 'PONG',
|
||||||
'SET': lambda r: r and nativestr(r) == 'OK',
|
'SET': lambda r: r and nativestr(r) == 'OK',
|
||||||
'INFO': parse_info,
|
'INFO': parse_info,
|
||||||
|
'TIME': lambda x: (int(x[0]), int(x[1])),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1035,6 +1036,13 @@ class Ledis(object):
|
||||||
def scriptflush(self):
|
def scriptflush(self):
|
||||||
return self.execute_command('SCRIPT', 'FLUSH')
|
return self.execute_command('SCRIPT', 'FLUSH')
|
||||||
|
|
||||||
|
def time(self):
|
||||||
|
"""
|
||||||
|
Returns the server time as a 2-item tuple of ints:
|
||||||
|
(seconds since epoch, microseconds into this second).
|
||||||
|
"""
|
||||||
|
return self.execute_command('TIME')
|
||||||
|
|
||||||
|
|
||||||
class Transaction(Ledis):
|
class Transaction(Ledis):
|
||||||
def __init__(self, connection_pool, response_callbacks):
|
def __init__(self, connection_pool, response_callbacks):
|
||||||
|
|
|
@ -8,6 +8,8 @@ module.exports = [
|
||||||
"info",
|
"info",
|
||||||
"flushall",
|
"flushall",
|
||||||
"flushdb",
|
"flushdb",
|
||||||
|
"time",
|
||||||
|
"config",
|
||||||
|
|
||||||
"bget",
|
"bget",
|
||||||
"bdelete",
|
"bdelete",
|
||||||
|
|
|
@ -153,6 +153,8 @@ local commands = {
|
||||||
"info",
|
"info",
|
||||||
"flushall",
|
"flushall",
|
||||||
"flushdb",
|
"flushdb",
|
||||||
|
"time",
|
||||||
|
"config",
|
||||||
|
|
||||||
-- [[transaction]]
|
-- [[transaction]]
|
||||||
"begin",
|
"begin",
|
||||||
|
|
|
@ -17,6 +17,7 @@ var number = flag.Int("n", 1000, "request number")
|
||||||
var clients = flag.Int("c", 50, "number of clients")
|
var clients = flag.Int("c", 50, "number of clients")
|
||||||
var reverse = flag.Bool("rev", false, "enable zset rev benchmark")
|
var reverse = flag.Bool("rev", false, "enable zset rev benchmark")
|
||||||
var round = flag.Int("r", 1, "benchmark round number")
|
var round = flag.Int("r", 1, "benchmark round number")
|
||||||
|
var del = flag.Bool("del", true, "enable del benchmark")
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
@ -281,18 +282,27 @@ func main() {
|
||||||
benchSet()
|
benchSet()
|
||||||
benchGet()
|
benchGet()
|
||||||
benchRandGet()
|
benchRandGet()
|
||||||
|
|
||||||
|
if *del == true {
|
||||||
benchDel()
|
benchDel()
|
||||||
|
}
|
||||||
|
|
||||||
benchPushList()
|
benchPushList()
|
||||||
benchRangeList10()
|
benchRangeList10()
|
||||||
benchRangeList50()
|
benchRangeList50()
|
||||||
benchRangeList100()
|
benchRangeList100()
|
||||||
|
|
||||||
|
if *del == true {
|
||||||
benchPopList()
|
benchPopList()
|
||||||
|
}
|
||||||
|
|
||||||
benchHset()
|
benchHset()
|
||||||
benchHGet()
|
benchHGet()
|
||||||
benchHRandGet()
|
benchHRandGet()
|
||||||
|
|
||||||
|
if *del == true {
|
||||||
benchHDel()
|
benchHDel()
|
||||||
|
}
|
||||||
|
|
||||||
benchZAdd()
|
benchZAdd()
|
||||||
benchZIncr()
|
benchZIncr()
|
||||||
|
@ -306,7 +316,9 @@ func main() {
|
||||||
benchZRevRangeByScore()
|
benchZRevRangeByScore()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *del == true {
|
||||||
benchZDel()
|
benchZDel()
|
||||||
|
}
|
||||||
|
|
||||||
println("")
|
println("")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//This file was generated by .tools/generate_commands.py on Thu Oct 02 2014 15:24:07 +0800
|
//This file was generated by .tools/generate_commands.py on Wed Oct 08 2014 16:36:20 +0800
|
||||||
package main
|
package main
|
||||||
|
|
||||||
var helpCommands = [][]string{
|
var helpCommands = [][]string{
|
||||||
|
@ -16,6 +16,7 @@ var helpCommands = [][]string{
|
||||||
{"BTTL", "key", "Bitmap"},
|
{"BTTL", "key", "Bitmap"},
|
||||||
{"BXSCAN", "key [MATCH match] [COUNT count]", "Bitmap"},
|
{"BXSCAN", "key [MATCH match] [COUNT count]", "Bitmap"},
|
||||||
{"COMMIT", "-", "Transaction"},
|
{"COMMIT", "-", "Transaction"},
|
||||||
|
{"CONFIG REWRITE", "-", "Server"},
|
||||||
{"DECR", "key", "KV"},
|
{"DECR", "key", "KV"},
|
||||||
{"DECRBY", "key decrement", "KV"},
|
{"DECRBY", "key decrement", "KV"},
|
||||||
{"DEL", "key [key ...]", "KV"},
|
{"DEL", "key [key ...]", "KV"},
|
||||||
|
@ -96,6 +97,7 @@ var helpCommands = [][]string{
|
||||||
{"SUNIONSTORE", "destination key [key ...]", "Set"},
|
{"SUNIONSTORE", "destination key [key ...]", "Set"},
|
||||||
{"SXSCAN", "key [MATCH match] [COUNT count]", "Set"},
|
{"SXSCAN", "key [MATCH match] [COUNT count]", "Set"},
|
||||||
{"SYNC", "logid", "Replication"},
|
{"SYNC", "logid", "Replication"},
|
||||||
|
{"TIME", "-", "Server"},
|
||||||
{"TTL", "key", "KV"},
|
{"TTL", "key", "KV"},
|
||||||
{"XSCAN", "key [MATCH match] [COUNT count]", "KV"},
|
{"XSCAN", "key [MATCH match] [COUNT count]", "KV"},
|
||||||
{"ZADD", "key score member [score member ...]", "ZSet"},
|
{"ZADD", "key score member [score member ...]", "ZSet"},
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
|
"github.com/siddontang/go/ioutil2"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Size int
|
var (
|
||||||
|
ErrNoConfigFile = errors.New("Running without a config file")
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultAddr string = "127.0.0.1:6380"
|
DefaultAddr string = "127.0.0.1:6380"
|
||||||
|
@ -39,6 +45,8 @@ type ReplicationConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
FileName string `toml:"-"`
|
||||||
|
|
||||||
Addr string `toml:"addr"`
|
Addr string `toml:"addr"`
|
||||||
|
|
||||||
HttpAddr string `toml:"http_addr"`
|
HttpAddr string `toml:"http_addr"`
|
||||||
|
@ -67,7 +75,12 @@ func NewConfigWithFile(fileName string) (*Config, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewConfigWithData(data)
|
if cfg, err := NewConfigWithData(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
cfg.FileName = fileName
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfigWithData(data []byte) (*Config, error) {
|
func NewConfigWithData(data []byte) (*Config, error) {
|
||||||
|
@ -123,3 +136,27 @@ func (cfg *LevelDBConfig) Adjust() {
|
||||||
cfg.MaxOpenFiles = 1024
|
cfg.MaxOpenFiles = 1024
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) Dump(w io.Writer) error {
|
||||||
|
e := toml.NewEncoder(w)
|
||||||
|
e.Indent = ""
|
||||||
|
return e.Encode(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) DumpFile(fileName string) error {
|
||||||
|
var b bytes.Buffer
|
||||||
|
|
||||||
|
if err := cfg.Dump(&b); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutil2.WriteFileAtomic(fileName, b.Bytes(), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) Rewrite() error {
|
||||||
|
if len(cfg.FileName) == 0 {
|
||||||
|
return ErrNoConfigFile
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg.DumpFile(cfg.FileName)
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,42 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConfig(t *testing.T) {
|
func TestConfig(t *testing.T) {
|
||||||
_, err := NewConfigWithFile("./ledis.toml")
|
cfg, err := NewConfigWithFile("./config.toml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bakFile := "./config.toml.bak"
|
||||||
|
|
||||||
|
defer os.Remove(bakFile)
|
||||||
|
if err := cfg.DumpFile(bakFile); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c, err := NewConfigWithFile(bakFile); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
c.FileName = cfg.FileName
|
||||||
|
if !reflect.DeepEqual(cfg, c) {
|
||||||
|
t.Fatal("must equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.FileName = bakFile
|
||||||
|
c.SlaveOf = "127.0.0.1:6381"
|
||||||
|
if err := c.Rewrite(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c1, err := NewConfigWithFile(bakFile); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if !reflect.DeepEqual(c, c1) {
|
||||||
|
t.Fatalf("must equal %v != %v", c, c1)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -628,6 +628,17 @@
|
||||||
"arguments" : "-",
|
"arguments" : "-",
|
||||||
"group": "Script",
|
"group": "Script",
|
||||||
"readonly": false
|
"readonly": false
|
||||||
}
|
},
|
||||||
|
|
||||||
|
"TIME": {
|
||||||
|
"arguments" : "-",
|
||||||
|
"group": "Server",
|
||||||
|
"readonly": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"CONFIG REWRITE": {
|
||||||
|
"arguments" : "-",
|
||||||
|
"group": "Server",
|
||||||
|
"readonly": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,6 +132,8 @@ Table of Contents
|
||||||
- [FLUSHALL](#flushall)
|
- [FLUSHALL](#flushall)
|
||||||
- [FLUSHDB](#flushdb)
|
- [FLUSHDB](#flushdb)
|
||||||
- [INFO [section]](#info-section)
|
- [INFO [section]](#info-section)
|
||||||
|
- [TIME](#time)
|
||||||
|
- [CONFIG REWRITE](#config-rewrite)
|
||||||
- [Transaction](#transaction)
|
- [Transaction](#transaction)
|
||||||
- [BEGIN](#begin)
|
- [BEGIN](#begin)
|
||||||
- [ROLLBACK](#rollback)
|
- [ROLLBACK](#rollback)
|
||||||
|
@ -2570,6 +2572,24 @@ The optional parameter can be used to select a specific section of information:
|
||||||
|
|
||||||
When no parameter is provided, all will return.
|
When no parameter is provided, all will return.
|
||||||
|
|
||||||
|
### TIME
|
||||||
|
|
||||||
|
The TIME command returns the current server time as a two items lists: a Unix timestamp and the amount of microseconds already elapsed in the current second
|
||||||
|
|
||||||
|
**Return value**
|
||||||
|
|
||||||
|
array: two elements, one is unix time in seconds, the other is microseconds.
|
||||||
|
|
||||||
|
### CONFIG REWRITE
|
||||||
|
|
||||||
|
Rewrites the config file the server was started with.
|
||||||
|
|
||||||
|
**Unlike Redis rewrite, it will discard all comments in origin config file.**
|
||||||
|
|
||||||
|
**Return value**
|
||||||
|
|
||||||
|
String: OK or error msg.
|
||||||
|
|
||||||
## Transaction
|
## Transaction
|
||||||
|
|
||||||
### BEGIN
|
### BEGIN
|
||||||
|
|
|
@ -2,8 +2,11 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/siddontang/go/hack"
|
"github.com/siddontang/go/hack"
|
||||||
|
"github.com/siddontang/go/num"
|
||||||
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func pingCommand(c *client) error {
|
func pingCommand(c *client) error {
|
||||||
|
@ -85,6 +88,47 @@ func flushdbCommand(c *client) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func timeCommand(c *client) error {
|
||||||
|
if len(c.args) != 0 {
|
||||||
|
return ErrCmdParams
|
||||||
|
}
|
||||||
|
|
||||||
|
t := time.Now()
|
||||||
|
|
||||||
|
//seconds
|
||||||
|
s := t.Unix()
|
||||||
|
n := t.UnixNano()
|
||||||
|
|
||||||
|
//micro seconds
|
||||||
|
m := (n - s*1e9) / 1e3
|
||||||
|
|
||||||
|
ay := []interface{}{
|
||||||
|
num.FormatInt64ToSlice(s),
|
||||||
|
num.FormatInt64ToSlice(m),
|
||||||
|
}
|
||||||
|
|
||||||
|
c.resp.writeArray(ay)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func configCommand(c *client) error {
|
||||||
|
if len(c.args) < 1 {
|
||||||
|
return ErrCmdParams
|
||||||
|
}
|
||||||
|
|
||||||
|
switch strings.ToLower(hack.String(c.args[0])) {
|
||||||
|
case "rewrite":
|
||||||
|
if err := c.app.cfg.Rewrite(); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
c.resp.writeStatus(OK)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return ErrCmdParams
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
register("ping", pingCommand)
|
register("ping", pingCommand)
|
||||||
register("echo", echoCommand)
|
register("echo", echoCommand)
|
||||||
|
@ -92,4 +136,6 @@ func init() {
|
||||||
register("info", infoCommand)
|
register("info", infoCommand)
|
||||||
register("flushall", flushallCommand)
|
register("flushall", flushallCommand)
|
||||||
register("flushdb", flushdbCommand)
|
register("flushdb", flushdbCommand)
|
||||||
|
register("time", timeCommand)
|
||||||
|
register("config", configCommand)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
## Notice
|
## Notice
|
||||||
|
|
||||||
1. The tool doesn't support `set` data type.
|
|
||||||
2. The tool doesn't support `bitmap` data type.
|
2. The tool doesn't support `bitmap` data type.
|
||||||
2. Our `zset` use integer instead of double, so the zset float score in Redis
|
2. Our `zset` use integer instead of double, so the zset float score in Redis
|
||||||
will be **converted to integer**.
|
will be **converted to integer**.
|
||||||
|
|
|
@ -35,7 +35,7 @@ def set_ttl(redis_client, ledis_client, key, k_type):
|
||||||
"string": ledis_client.expire,
|
"string": ledis_client.expire,
|
||||||
"list": ledis_client.lexpire,
|
"list": ledis_client.lexpire,
|
||||||
"hash": ledis_client.hexpire,
|
"hash": ledis_client.hexpire,
|
||||||
"set": ledis_client.zexpire,
|
"set": ledis_client.sexpire,
|
||||||
"zset": ledis_client.zexpire
|
"zset": ledis_client.zexpire
|
||||||
}
|
}
|
||||||
timeout = redis_client.ttl(key)
|
timeout = redis_client.ttl(key)
|
||||||
|
@ -46,6 +46,7 @@ def set_ttl(redis_client, ledis_client, key, k_type):
|
||||||
def copy_key(redis_client, ledis_client, key, convert=False):
|
def copy_key(redis_client, ledis_client, key, convert=False):
|
||||||
global entries
|
global entries
|
||||||
k_type = redis_client.type(key)
|
k_type = redis_client.type(key)
|
||||||
|
|
||||||
if k_type == "string":
|
if k_type == "string":
|
||||||
value = redis_client.get(key)
|
value = redis_client.get(key)
|
||||||
ledis_client.set(key, value)
|
ledis_client.set(key, value)
|
||||||
|
@ -66,6 +67,7 @@ def copy_key(redis_client, ledis_client, key, convert=False):
|
||||||
entries += 1
|
entries += 1
|
||||||
|
|
||||||
elif k_type == "zset":
|
elif k_type == "zset":
|
||||||
|
# dangerous to do this?
|
||||||
out = redis_client.zrange(key, 0, -1, withscores=True)
|
out = redis_client.zrange(key, 0, -1, withscores=True)
|
||||||
pieces = od()
|
pieces = od()
|
||||||
for i in od(out).iteritems():
|
for i in od(out).iteritems():
|
||||||
|
@ -74,6 +76,14 @@ def copy_key(redis_client, ledis_client, key, convert=False):
|
||||||
set_ttl(redis_client, ledis_client, key, k_type)
|
set_ttl(redis_client, ledis_client, key, k_type)
|
||||||
entries += 1
|
entries += 1
|
||||||
|
|
||||||
|
elif k_type == "set":
|
||||||
|
mbs = list(redis_client.smembers(key))
|
||||||
|
|
||||||
|
if mbs is not None:
|
||||||
|
ledis_client.sadd(key, *mbs)
|
||||||
|
set_ttl(redis_client, ledis_client, key, k_type)
|
||||||
|
entries += 1
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print "KEY %s of TYPE %s is not supported by LedisDB." % (key, k_type)
|
print "KEY %s of TYPE %s is not supported by LedisDB." % (key, k_type)
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,9 @@ def random_zset(client, words, length=1000):
|
||||||
client.zadd("zsetName", **d)
|
client.zadd("zsetName", **d)
|
||||||
|
|
||||||
|
|
||||||
|
def random_set(client, words, length=1000):
|
||||||
|
client.sadd("setName", *words)
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
words = get_words()
|
words = get_words()
|
||||||
print "Flush all redis data before insert new."
|
print "Flush all redis data before insert new."
|
||||||
|
@ -64,10 +67,9 @@ def test():
|
||||||
random_zset(rds, words)
|
random_zset(rds, words)
|
||||||
print "random_zset done"
|
print "random_zset done"
|
||||||
|
|
||||||
|
random_set(rds, words)
|
||||||
|
print "random_set done"
|
||||||
|
|
||||||
lds.lclear("listName")
|
|
||||||
lds.hclear("hashName")
|
|
||||||
lds.zclear("zsetName")
|
|
||||||
copy(rds, lds, convert=True)
|
copy(rds, lds, convert=True)
|
||||||
|
|
||||||
# for all keys
|
# for all keys
|
||||||
|
@ -101,6 +103,7 @@ def ledis_ttl(ledis_client, key, k_type):
|
||||||
"list": lds.lttl,
|
"list": lds.lttl,
|
||||||
"hash": lds.httl,
|
"hash": lds.httl,
|
||||||
"zset": lds.zttl,
|
"zset": lds.zttl,
|
||||||
|
"set": lds.sttl,
|
||||||
}
|
}
|
||||||
return ttls[k_type](key)
|
return ttls[k_type](key)
|
||||||
|
|
||||||
|
@ -115,6 +118,9 @@ def test_ttl():
|
||||||
assert ledis_ttl(lds, key, k_type) > 0
|
assert ledis_ttl(lds, key, k_type) > 0
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
rds.flushdb()
|
||||||
|
lds.flushdb()
|
||||||
|
|
||||||
test()
|
test()
|
||||||
test_ttl()
|
test_ttl()
|
||||||
print "Test passed."
|
print "Test passed."
|
||||||
|
|
Loading…
Reference in New Issue