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",
|
||||
"Rev": "c1f6d721561c48f467b26a277741e55fd224df1e"
|
||||
"Rev": "614b6126cf79eee8be803e7e65a3ef24fc12e44a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/szferi/gomdb",
|
||||
|
|
|
@ -137,6 +137,7 @@ class Ledis(object):
|
|||
'PING': lambda r: nativestr(r) == 'PONG',
|
||||
'SET': lambda r: r and nativestr(r) == 'OK',
|
||||
'INFO': parse_info,
|
||||
'TIME': lambda x: (int(x[0]), int(x[1])),
|
||||
}
|
||||
|
||||
|
||||
|
@ -1035,6 +1036,13 @@ class Ledis(object):
|
|||
def scriptflush(self):
|
||||
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):
|
||||
def __init__(self, connection_pool, response_callbacks):
|
||||
|
|
|
@ -8,6 +8,8 @@ module.exports = [
|
|||
"info",
|
||||
"flushall",
|
||||
"flushdb",
|
||||
"time",
|
||||
"config",
|
||||
|
||||
"bget",
|
||||
"bdelete",
|
||||
|
|
|
@ -153,6 +153,8 @@ local commands = {
|
|||
"info",
|
||||
"flushall",
|
||||
"flushdb",
|
||||
"time",
|
||||
"config",
|
||||
|
||||
-- [[transaction]]
|
||||
"begin",
|
||||
|
|
|
@ -17,6 +17,7 @@ var number = flag.Int("n", 1000, "request number")
|
|||
var clients = flag.Int("c", 50, "number of clients")
|
||||
var reverse = flag.Bool("rev", false, "enable zset rev benchmark")
|
||||
var round = flag.Int("r", 1, "benchmark round number")
|
||||
var del = flag.Bool("del", true, "enable del benchmark")
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
|
@ -281,18 +282,27 @@ func main() {
|
|||
benchSet()
|
||||
benchGet()
|
||||
benchRandGet()
|
||||
|
||||
if *del == true {
|
||||
benchDel()
|
||||
}
|
||||
|
||||
benchPushList()
|
||||
benchRangeList10()
|
||||
benchRangeList50()
|
||||
benchRangeList100()
|
||||
|
||||
if *del == true {
|
||||
benchPopList()
|
||||
}
|
||||
|
||||
benchHset()
|
||||
benchHGet()
|
||||
benchHRandGet()
|
||||
|
||||
if *del == true {
|
||||
benchHDel()
|
||||
}
|
||||
|
||||
benchZAdd()
|
||||
benchZIncr()
|
||||
|
@ -306,7 +316,9 @@ func main() {
|
|||
benchZRevRangeByScore()
|
||||
}
|
||||
|
||||
if *del == true {
|
||||
benchZDel()
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
var helpCommands = [][]string{
|
||||
|
@ -16,6 +16,7 @@ var helpCommands = [][]string{
|
|||
{"BTTL", "key", "Bitmap"},
|
||||
{"BXSCAN", "key [MATCH match] [COUNT count]", "Bitmap"},
|
||||
{"COMMIT", "-", "Transaction"},
|
||||
{"CONFIG REWRITE", "-", "Server"},
|
||||
{"DECR", "key", "KV"},
|
||||
{"DECRBY", "key decrement", "KV"},
|
||||
{"DEL", "key [key ...]", "KV"},
|
||||
|
@ -96,6 +97,7 @@ var helpCommands = [][]string{
|
|||
{"SUNIONSTORE", "destination key [key ...]", "Set"},
|
||||
{"SXSCAN", "key [MATCH match] [COUNT count]", "Set"},
|
||||
{"SYNC", "logid", "Replication"},
|
||||
{"TIME", "-", "Server"},
|
||||
{"TTL", "key", "KV"},
|
||||
{"XSCAN", "key [MATCH match] [COUNT count]", "KV"},
|
||||
{"ZADD", "key score member [score member ...]", "ZSet"},
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/siddontang/go/ioutil2"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
type Size int
|
||||
var (
|
||||
ErrNoConfigFile = errors.New("Running without a config file")
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultAddr string = "127.0.0.1:6380"
|
||||
|
@ -39,6 +45,8 @@ type ReplicationConfig struct {
|
|||
}
|
||||
|
||||
type Config struct {
|
||||
FileName string `toml:"-"`
|
||||
|
||||
Addr string `toml:"addr"`
|
||||
|
||||
HttpAddr string `toml:"http_addr"`
|
||||
|
@ -67,7 +75,12 @@ func NewConfigWithFile(fileName string) (*Config, error) {
|
|||
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) {
|
||||
|
@ -123,3 +136,27 @@ func (cfg *LevelDBConfig) Adjust() {
|
|||
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
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
_, err := NewConfigWithFile("./ledis.toml")
|
||||
cfg, err := NewConfigWithFile("./config.toml")
|
||||
if err != nil {
|
||||
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" : "-",
|
||||
"group": "Script",
|
||||
"readonly": false
|
||||
}
|
||||
},
|
||||
|
||||
"TIME": {
|
||||
"arguments" : "-",
|
||||
"group": "Server",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"CONFIG REWRITE": {
|
||||
"arguments" : "-",
|
||||
"group": "Server",
|
||||
"readonly": false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,6 +132,8 @@ Table of Contents
|
|||
- [FLUSHALL](#flushall)
|
||||
- [FLUSHDB](#flushdb)
|
||||
- [INFO [section]](#info-section)
|
||||
- [TIME](#time)
|
||||
- [CONFIG REWRITE](#config-rewrite)
|
||||
- [Transaction](#transaction)
|
||||
- [BEGIN](#begin)
|
||||
- [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.
|
||||
|
||||
### 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
|
||||
|
||||
### BEGIN
|
||||
|
|
|
@ -2,8 +2,11 @@ package server
|
|||
|
||||
import (
|
||||
"github.com/siddontang/go/hack"
|
||||
"github.com/siddontang/go/num"
|
||||
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func pingCommand(c *client) error {
|
||||
|
@ -85,6 +88,47 @@ func flushdbCommand(c *client) error {
|
|||
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() {
|
||||
register("ping", pingCommand)
|
||||
register("echo", echoCommand)
|
||||
|
@ -92,4 +136,6 @@ func init() {
|
|||
register("info", infoCommand)
|
||||
register("flushall", flushallCommand)
|
||||
register("flushdb", flushdbCommand)
|
||||
register("time", timeCommand)
|
||||
register("config", configCommand)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
## Notice
|
||||
|
||||
1. The tool doesn't support `set` 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
|
||||
will be **converted to integer**.
|
||||
|
|
|
@ -35,7 +35,7 @@ def set_ttl(redis_client, ledis_client, key, k_type):
|
|||
"string": ledis_client.expire,
|
||||
"list": ledis_client.lexpire,
|
||||
"hash": ledis_client.hexpire,
|
||||
"set": ledis_client.zexpire,
|
||||
"set": ledis_client.sexpire,
|
||||
"zset": ledis_client.zexpire
|
||||
}
|
||||
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):
|
||||
global entries
|
||||
k_type = redis_client.type(key)
|
||||
|
||||
if k_type == "string":
|
||||
value = redis_client.get(key)
|
||||
ledis_client.set(key, value)
|
||||
|
@ -66,6 +67,7 @@ def copy_key(redis_client, ledis_client, key, convert=False):
|
|||
entries += 1
|
||||
|
||||
elif k_type == "zset":
|
||||
# dangerous to do this?
|
||||
out = redis_client.zrange(key, 0, -1, withscores=True)
|
||||
pieces = od()
|
||||
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)
|
||||
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:
|
||||
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)
|
||||
|
||||
|
||||
def random_set(client, words, length=1000):
|
||||
client.sadd("setName", *words)
|
||||
|
||||
def test():
|
||||
words = get_words()
|
||||
print "Flush all redis data before insert new."
|
||||
|
@ -64,10 +67,9 @@ def test():
|
|||
random_zset(rds, words)
|
||||
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)
|
||||
|
||||
# for all keys
|
||||
|
@ -101,6 +103,7 @@ def ledis_ttl(ledis_client, key, k_type):
|
|||
"list": lds.lttl,
|
||||
"hash": lds.httl,
|
||||
"zset": lds.zttl,
|
||||
"set": lds.sttl,
|
||||
}
|
||||
return ttls[k_type](key)
|
||||
|
||||
|
@ -115,6 +118,9 @@ def test_ttl():
|
|||
assert ledis_ttl(lds, key, k_type) > 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
rds.flushdb()
|
||||
lds.flushdb()
|
||||
|
||||
test()
|
||||
test_ttl()
|
||||
print "Test passed."
|
||||
|
|
Loading…
Reference in New Issue