Merge branch 'develop'

This commit is contained in:
siddontang 2014-10-08 16:39:49 +08:00
commit a3faef12e6
14 changed files with 199 additions and 15 deletions

2
Godeps/Godeps.json generated
View File

@ -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",

View File

@ -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):

View File

@ -8,6 +8,8 @@ module.exports = [
"info", "info",
"flushall", "flushall",
"flushdb", "flushdb",
"time",
"config",
"bget", "bget",
"bdelete", "bdelete",

View File

@ -153,6 +153,8 @@ local commands = {
"info", "info",
"flushall", "flushall",
"flushdb", "flushdb",
"time",
"config",
-- [[transaction]] -- [[transaction]]
"begin", "begin",

View File

@ -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()
benchDel()
if *del == true {
benchDel()
}
benchPushList() benchPushList()
benchRangeList10() benchRangeList10()
benchRangeList50() benchRangeList50()
benchRangeList100() benchRangeList100()
benchPopList()
if *del == true {
benchPopList()
}
benchHset() benchHset()
benchHGet() benchHGet()
benchHRandGet() benchHRandGet()
benchHDel()
if *del == true {
benchHDel()
}
benchZAdd() benchZAdd()
benchZIncr() benchZIncr()
@ -306,7 +316,9 @@ func main() {
benchZRevRangeByScore() benchZRevRangeByScore()
} }
benchZDel() if *del == true {
benchZDel()
}
println("") println("")
} }

View File

@ -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"},

View File

@ -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)
}

View File

@ -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)
}
}
} }

View File

@ -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
}
} }

View File

@ -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

View File

@ -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)
} }

View File

@ -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**.

View File

@ -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)

View File

@ -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."