Merge branch 'develop'

This commit is contained in:
siddontang 2015-03-12 11:53:07 +08:00
commit add345c119
64 changed files with 2052 additions and 1002 deletions

3
.gitignore vendored
View File

@ -4,4 +4,5 @@ build
nohup.out
build_config.mk
var*
*.log
*.log
bin

10
Godeps/Godeps.json generated
View File

@ -1,6 +1,6 @@
{
"ImportPath": "github.com/siddontang/ledisdb",
"GoVersion": "go1.4",
"GoVersion": "go1.4.2",
"Packages": [
"./..."
],
@ -58,6 +58,14 @@
"ImportPath": "github.com/siddontang/go/sync2",
"Rev": "c2b33271306fcb7c6532efceac33ec45ee2439e0"
},
{
"ImportPath": "github.com/siddontang/goredis",
"Rev": "6d2857b0488d1e8b9f96b46802eacb68e29fb003"
},
{
"ImportPath": "github.com/siddontang/rdb",
"Rev": "fc89ed2e418d27e3ea76e708e54276d2b44ae9cf"
},
{
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
"Rev": "e9e2c8f6d3b9c313fb4acaac5ab06285bcf30b04"

View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 siddontang
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.

View File

@ -1,4 +1,4 @@
package ledis
package goredis
import (
"container/list"
@ -7,17 +7,23 @@ import (
"sync"
)
type Config struct {
Addr string
MaxIdleConns int
ReadBufferSize int
WriteBufferSize int
type PoolConn struct {
*Conn
c *Client
}
func (c *PoolConn) Close() {
c.c.put(c.Conn)
}
type Client struct {
sync.Mutex
cfg *Config
addr string
maxIdleConns int
readBufferSize int
writeBufferSize int
password string
conns *list.List
}
@ -30,22 +36,36 @@ func getProto(addr string) string {
}
}
func NewClient(cfg *Config) *Client {
func NewClient(addr string, password string) *Client {
c := new(Client)
c.cfg = cfg
if c.cfg.ReadBufferSize == 0 {
c.cfg.ReadBufferSize = 4096
}
if c.cfg.WriteBufferSize == 0 {
c.cfg.WriteBufferSize = 4096
}
c.addr = addr
c.maxIdleConns = 4
c.readBufferSize = 1024
c.writeBufferSize = 1024
c.password = password
c.conns = list.New()
return c
}
func (c *Client) SetPassword(pass string) {
c.password = pass
}
func (c *Client) SetReadBufferSize(s int) {
c.readBufferSize = s
}
func (c *Client) SetWriteBufferSize(s int) {
c.writeBufferSize = s
}
func (c *Client) SetMaxIdleConns(n int) {
c.maxIdleConns = n
}
func (c *Client) Do(cmd string, args ...interface{}) (interface{}, error) {
var co *Conn
var err error
@ -59,7 +79,7 @@ func (c *Client) Do(cmd string, args ...interface{}) (interface{}, error) {
r, err = co.Do(cmd, args...)
if err != nil {
co.finalize()
co.Close()
if e, ok := err.(*net.OpError); ok && strings.Contains(e.Error(), "use of closed network connection") {
//send to a closed connection, try again
@ -86,36 +106,41 @@ func (c *Client) Close() {
co := e.Value.(*Conn)
c.conns.Remove(e)
co.finalize()
co.Close()
}
}
func (c *Client) Get() (*Conn, error) {
return c.get()
func (c *Client) Get() (*PoolConn, error) {
co, err := c.get()
if err != nil {
return nil, err
}
return &PoolConn{co, c}, err
}
func (c *Client) get() (*Conn, error) {
func (c *Client) get() (co *Conn, err error) {
c.Lock()
if c.conns.Len() == 0 {
c.Unlock()
return c.newConn(c.cfg.Addr)
co, err = c.newConn(c.addr, c.password)
} else {
e := c.conns.Front()
co := e.Value.(*Conn)
co = e.Value.(*Conn)
c.conns.Remove(e)
c.Unlock()
return co, nil
}
return
}
func (c *Client) put(conn *Conn) {
c.Lock()
if c.conns.Len() >= c.cfg.MaxIdleConns {
if c.conns.Len() >= c.maxIdleConns {
c.Unlock()
conn.finalize()
conn.Close()
} else {
c.conns.PushFront(conn)
c.Unlock()

View File

@ -1,4 +1,4 @@
package ledis
package goredis
import (
"bufio"
@ -16,9 +16,14 @@ type Error string
func (err Error) Error() string { return string(err) }
type Conn struct {
client *Client
type sizeWriter int64
func (s *sizeWriter) Write(p []byte) (int, error) {
*s += sizeWriter(len(p))
return len(p), nil
}
type Conn struct {
c net.Conn
br *bufio.Reader
bw *bufio.Writer
@ -29,10 +34,13 @@ type Conn struct {
// Scratch space for formatting integers and floats.
numScratch [40]byte
totalReadSize sizeWriter
totalWriteSize sizeWriter
}
func Connect(addr string) (*Conn, error) {
return ConnectWithSize(addr, 4096, 4096)
return ConnectWithSize(addr, 1024, 1024)
}
func ConnectWithSize(addr string, readSize int, writeSize int) (*Conn, error) {
@ -44,18 +52,22 @@ func ConnectWithSize(addr string, readSize int, writeSize int) (*Conn, error) {
return nil, err
}
c.br = bufio.NewReaderSize(c.c, readSize)
c.bw = bufio.NewWriterSize(c.c, writeSize)
c.br = bufio.NewReaderSize(io.TeeReader(c.c, &c.totalReadSize), readSize)
c.bw = bufio.NewWriterSize(io.MultiWriter(c.c, &c.totalWriteSize), writeSize)
return c, nil
}
func (c *Conn) Close() {
if c.client != nil {
c.client.put(c)
} else {
c.finalize()
}
c.c.Close()
}
func (c *Conn) GetTotalReadSize() int64 {
return int64(c.totalReadSize)
}
func (c *Conn) GetTotalWriteSize() int64 {
return int64(c.totalWriteSize)
}
func (c *Conn) SetReadDeadline(t time.Time) {
@ -76,12 +88,12 @@ func (c *Conn) Do(cmd string, args ...interface{}) (interface{}, error) {
func (c *Conn) Send(cmd string, args ...interface{}) error {
if err := c.writeCommand(cmd, args); err != nil {
c.finalize()
c.Close()
return err
}
if err := c.bw.Flush(); err != nil {
c.finalize()
c.Close()
return err
}
return nil
@ -89,7 +101,7 @@ func (c *Conn) Send(cmd string, args ...interface{}) error {
func (c *Conn) Receive() (interface{}, error) {
if reply, err := c.readReply(); err != nil {
c.finalize()
c.Close()
return nil, err
} else {
if e, ok := reply.(Error); ok {
@ -104,16 +116,12 @@ func (c *Conn) ReceiveBulkTo(w io.Writer) error {
err := c.readBulkReplyTo(w)
if err != nil {
if _, ok := err.(Error); !ok {
c.finalize()
c.Close()
}
}
return err
}
func (c *Conn) finalize() {
c.c.Close()
}
func (c *Conn) writeLen(prefix byte, n int) error {
c.lenScratch[len(c.lenScratch)-1] = '\n'
c.lenScratch[len(c.lenScratch)-2] = '\r'
@ -191,14 +199,14 @@ func (c *Conn) writeCommand(cmd string, args []interface{}) (err error) {
func (c *Conn) readLine() ([]byte, error) {
p, err := c.br.ReadSlice('\n')
if err == bufio.ErrBufferFull {
return nil, errors.New("ledis: long response line")
return nil, errors.New("long response line")
}
if err != nil {
return nil, err
}
i := len(p) - 2
if i < 0 || p[i] != '\r' {
return nil, errors.New("ledis: bad response line terminator")
return nil, errors.New("bad response line terminator")
}
return p[:i], nil
}
@ -206,7 +214,7 @@ func (c *Conn) readLine() ([]byte, error) {
// parseLen parses bulk string and array lengths.
func parseLen(p []byte) (int, error) {
if len(p) == 0 {
return -1, errors.New("ledis: malformed length")
return -1, errors.New("malformed length")
}
if p[0] == '-' && len(p) == 2 && p[1] == '1' {
@ -218,7 +226,7 @@ func parseLen(p []byte) (int, error) {
for _, b := range p {
n *= 10
if b < '0' || b > '9' {
return -1, errors.New("ledis: illegal bytes in length")
return -1, errors.New("illegal bytes in length")
}
n += int(b - '0')
}
@ -229,7 +237,7 @@ func parseLen(p []byte) (int, error) {
// parseInt parses an integer reply.
func parseInt(p []byte) (interface{}, error) {
if len(p) == 0 {
return 0, errors.New("ledis: malformed integer")
return 0, errors.New("malformed integer")
}
var negate bool
@ -237,7 +245,7 @@ func parseInt(p []byte) (interface{}, error) {
negate = true
p = p[1:]
if len(p) == 0 {
return 0, errors.New("ledis: malformed integer")
return 0, errors.New("malformed integer")
}
}
@ -245,7 +253,7 @@ func parseInt(p []byte) (interface{}, error) {
for _, b := range p {
n *= 10
if b < '0' || b > '9' {
return 0, errors.New("ledis: illegal bytes in length")
return 0, errors.New("illegal bytes in length")
}
n += int64(b - '0')
}
@ -288,11 +296,11 @@ func (c *Conn) readBulkReplyTo(w io.Writer) error {
if line, err := c.readLine(); err != nil {
return err
} else if len(line) != 0 {
return errors.New("ledis: bad bulk string format")
return errors.New("bad bulk string format")
}
return nil
default:
return fmt.Errorf("ledis: not invalid bulk string type, but %c", line[0])
return fmt.Errorf("not invalid bulk string type, but %c", line[0])
}
}
@ -302,7 +310,7 @@ func (c *Conn) readReply() (interface{}, error) {
return nil, err
}
if len(line) == 0 {
return nil, errors.New("ledis: short response line")
return nil, errors.New("short response line")
}
switch line[0] {
case '+':
@ -333,7 +341,7 @@ func (c *Conn) readReply() (interface{}, error) {
if line, err := c.readLine(); err != nil {
return nil, err
} else if len(line) != 0 {
return nil, errors.New("ledis: bad bulk string format")
return nil, errors.New("bad bulk string format")
}
return p, nil
case '*':
@ -350,15 +358,22 @@ func (c *Conn) readReply() (interface{}, error) {
}
return r, nil
}
return nil, errors.New("ledis: unexpected response line")
return nil, errors.New("unexpected response line")
}
func (c *Client) newConn(addr string) (*Conn, error) {
co, err := ConnectWithSize(addr, c.cfg.ReadBufferSize, c.cfg.WriteBufferSize)
func (c *Client) newConn(addr string, pass string) (*Conn, error) {
co, err := ConnectWithSize(addr, c.readBufferSize, c.writeBufferSize)
if err != nil {
return nil, err
}
co.client = c
if len(pass) > 0 {
_, err = co.Do("AUTH", pass)
if err != nil {
co.Close()
return nil, err
}
}
return co, nil
}

View File

@ -0,0 +1,34 @@
// Package goredis is a client for the redis and ledisdb.
//
// Client
//
// The client is the primary interface for redis. You must first create a client with redis address for working.
//
// c := NewClient("127.0.0.1:6380")
//
// The most important function for client is Do function to send commands to remote server.
//
// reply, err := c.Do("ping")
//
// reply, err := c.Do("set", "key", "value")
//
// reply, err := c.Do("get", "key")
//
// Connection
//
// You can use an independent connection to send commands.
//
// //get a connection
// conn, _ := c.Get()
//
// //connection send command
// conn.Do("ping")
//
// Reply Helper
//
// You can use reply helper to convert a reply to a specific type.
//
// exists, err := Bool(c.Do("exists", "key"))
//
// score, err := Int64(c.Do("zscore", "key", "member"))
package goredis

View File

@ -0,0 +1,39 @@
package goredis
import (
"github.com/alicebob/miniredis"
"testing"
)
func Test(t *testing.T) {
s, err := miniredis.Run()
if err != nil {
t.Fatal(err)
}
defer s.Close()
s.RequireAuth("123456")
addr := s.Addr()
c := NewClient(addr, "123456")
defer c.Close()
conn, err := c.Get()
if err != nil {
t.Fatal(err)
}
defer conn.Close()
if pong, err := String(conn.Do("PING")); err != nil {
t.Fatal(err)
} else if pong != "PONG" {
t.Fatal(pong)
}
if pong, err := String(conn.Do("PING")); err != nil {
t.Fatal(err)
} else if pong != "PONG" {
t.Fatal(pong)
}
}

View File

@ -1,4 +1,4 @@
package ledis
package goredis
import (
"errors"
@ -7,7 +7,7 @@ import (
)
// ErrNil indicates that a reply value is nil.
var ErrNil = errors.New("ledis: nil returned")
var ErrNil = errors.New("nil returned")
// Int is a helper that converts a command reply to an integer. If err is not
// equal to nil, then Int returns 0, err. Otherwise, Int converts the
@ -37,7 +37,7 @@ func Int(reply interface{}, err error) (int, error) {
case Error:
return 0, reply
}
return 0, fmt.Errorf("ledis: unexpected type for Int, got type %T", reply)
return 0, fmt.Errorf("unexpected type for Int, got type %T", reply)
}
// Int64 is a helper that converts a command reply to 64 bit integer. If err is
@ -64,10 +64,10 @@ func Int64(reply interface{}, err error) (int64, error) {
case Error:
return 0, reply
}
return 0, fmt.Errorf("ledis: unexpected type for Int64, got type %T", reply)
return 0, fmt.Errorf("unexpected type for Int64, got type %T", reply)
}
var errNegativeInt = errors.New("ledis: unexpected value for Uint64")
var errNegativeInt = errors.New("unexpected value for Uint64")
// Uint64 is a helper that converts a command reply to 64 bit integer. If err is
// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
@ -96,7 +96,7 @@ func Uint64(reply interface{}, err error) (uint64, error) {
case Error:
return 0, reply
}
return 0, fmt.Errorf("ledis: unexpected type for Uint64, got type %T", reply)
return 0, fmt.Errorf("unexpected type for Uint64, got type %T", reply)
}
// Float64 is a helper that converts a command reply to 64 bit float. If err is
@ -120,7 +120,7 @@ func Float64(reply interface{}, err error) (float64, error) {
case Error:
return 0, reply
}
return 0, fmt.Errorf("ledis: unexpected type for Float64, got type %T", reply)
return 0, fmt.Errorf("unexpected type for Float64, got type %T", reply)
}
// String is a helper that converts a command reply to a string. If err is not
@ -146,7 +146,7 @@ func String(reply interface{}, err error) (string, error) {
case Error:
return "", reply
}
return "", fmt.Errorf("ledis: unexpected type for String, got type %T", reply)
return "", fmt.Errorf("unexpected type for String, got type %T", reply)
}
// Bytes is a helper that converts a command reply to a slice of bytes. If err
@ -172,7 +172,7 @@ func Bytes(reply interface{}, err error) ([]byte, error) {
case Error:
return nil, reply
}
return nil, fmt.Errorf("ledis: unexpected type for Bytes, got type %T", reply)
return nil, fmt.Errorf("unexpected type for Bytes, got type %T", reply)
}
// Bool is a helper that converts a command reply to a boolean. If err is not
@ -198,7 +198,7 @@ func Bool(reply interface{}, err error) (bool, error) {
case Error:
return false, reply
}
return false, fmt.Errorf("ledis: unexpected type for Bool, got type %T", reply)
return false, fmt.Errorf("unexpected type for Bool, got type %T", reply)
}
// MultiBulk is deprecated. Use Values.
@ -224,7 +224,7 @@ func Values(reply interface{}, err error) ([]interface{}, error) {
case Error:
return nil, reply
}
return nil, fmt.Errorf("ledis: unexpected type for Values, got type %T", reply)
return nil, fmt.Errorf("unexpected type for Values, got type %T", reply)
}
// Strings is a helper that converts an array command reply to a []string. If
@ -243,7 +243,7 @@ func Strings(reply interface{}, err error) ([]string, error) {
}
p, ok := reply[i].([]byte)
if !ok {
return nil, fmt.Errorf("ledis: unexpected element type for Strings, got type %T", reply[i])
return nil, fmt.Errorf("unexpected element type for Strings, got type %T", reply[i])
}
result[i] = string(p)
}
@ -253,5 +253,5 @@ func Strings(reply interface{}, err error) ([]string, error) {
case Error:
return nil, reply
}
return nil, fmt.Errorf("ledis: unexpected type for Strings, got type %T", reply)
return nil, fmt.Errorf("unexpected type for Strings, got type %T", reply)
}

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2014 siddontang
Copyright (c) 2015 siddontang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -0,0 +1,3 @@
# rdb
Handling Redis RDB format.

View File

@ -40,7 +40,7 @@ func (d *decoder) Set(key, value []byte, expiry int64) {
}
func (d *decoder) StartHash(key []byte, length, expiry int64) {
d.initObject(HashMap(nil))
d.initObject(Hash(nil))
}
func (d *decoder) Hset(key, field, value []byte) {
@ -50,7 +50,7 @@ func (d *decoder) Hset(key, field, value []byte) {
switch h := d.obj.(type) {
default:
d.err = fmt.Errorf("invalid object, not a hashmap")
case HashMap:
case Hash:
v := struct {
Field, Value []byte
}{
@ -118,7 +118,7 @@ func (d *decoder) Zadd(key []byte, score float64, member []byte) {
type String []byte
type List [][]byte
type HashMap []struct {
type Hash []struct {
Field, Value []byte
}
type Set [][]byte

View File

@ -0,0 +1,106 @@
// Copyright 2014 Wandoujia Inc. All Rights Reserved.
// Licensed under the MIT (MIT-LICENSE.txt) license.
package rdb
import (
"encoding/binary"
"hash"
)
var crc64_table = [256]uint64{
0x0000000000000000, 0x7ad870c830358979, 0xf5b0e190606b12f2, 0x8f689158505e9b8b,
0xc038e5739841b68f, 0xbae095bba8743ff6, 0x358804e3f82aa47d, 0x4f50742bc81f2d04,
0xab28ecb46814fe75, 0xd1f09c7c5821770c, 0x5e980d24087fec87, 0x24407dec384a65fe,
0x6b1009c7f05548fa, 0x11c8790fc060c183, 0x9ea0e857903e5a08, 0xe478989fa00bd371,
0x7d08ff3b88be6f81, 0x07d08ff3b88be6f8, 0x88b81eabe8d57d73, 0xf2606e63d8e0f40a,
0xbd301a4810ffd90e, 0xc7e86a8020ca5077, 0x4880fbd87094cbfc, 0x32588b1040a14285,
0xd620138fe0aa91f4, 0xacf86347d09f188d, 0x2390f21f80c18306, 0x594882d7b0f40a7f,
0x1618f6fc78eb277b, 0x6cc0863448deae02, 0xe3a8176c18803589, 0x997067a428b5bcf0,
0xfa11fe77117cdf02, 0x80c98ebf2149567b, 0x0fa11fe77117cdf0, 0x75796f2f41224489,
0x3a291b04893d698d, 0x40f16bccb908e0f4, 0xcf99fa94e9567b7f, 0xb5418a5cd963f206,
0x513912c379682177, 0x2be1620b495da80e, 0xa489f35319033385, 0xde51839b2936bafc,
0x9101f7b0e12997f8, 0xebd98778d11c1e81, 0x64b116208142850a, 0x1e6966e8b1770c73,
0x8719014c99c2b083, 0xfdc17184a9f739fa, 0x72a9e0dcf9a9a271, 0x08719014c99c2b08,
0x4721e43f0183060c, 0x3df994f731b68f75, 0xb29105af61e814fe, 0xc849756751dd9d87,
0x2c31edf8f1d64ef6, 0x56e99d30c1e3c78f, 0xd9810c6891bd5c04, 0xa3597ca0a188d57d,
0xec09088b6997f879, 0x96d1784359a27100, 0x19b9e91b09fcea8b, 0x636199d339c963f2,
0xdf7adabd7a6e2d6f, 0xa5a2aa754a5ba416, 0x2aca3b2d1a053f9d, 0x50124be52a30b6e4,
0x1f423fcee22f9be0, 0x659a4f06d21a1299, 0xeaf2de5e82448912, 0x902aae96b271006b,
0x74523609127ad31a, 0x0e8a46c1224f5a63, 0x81e2d7997211c1e8, 0xfb3aa75142244891,
0xb46ad37a8a3b6595, 0xceb2a3b2ba0eecec, 0x41da32eaea507767, 0x3b024222da65fe1e,
0xa2722586f2d042ee, 0xd8aa554ec2e5cb97, 0x57c2c41692bb501c, 0x2d1ab4dea28ed965,
0x624ac0f56a91f461, 0x1892b03d5aa47d18, 0x97fa21650afae693, 0xed2251ad3acf6fea,
0x095ac9329ac4bc9b, 0x7382b9faaaf135e2, 0xfcea28a2faafae69, 0x8632586aca9a2710,
0xc9622c4102850a14, 0xb3ba5c8932b0836d, 0x3cd2cdd162ee18e6, 0x460abd1952db919f,
0x256b24ca6b12f26d, 0x5fb354025b277b14, 0xd0dbc55a0b79e09f, 0xaa03b5923b4c69e6,
0xe553c1b9f35344e2, 0x9f8bb171c366cd9b, 0x10e3202993385610, 0x6a3b50e1a30ddf69,
0x8e43c87e03060c18, 0xf49bb8b633338561, 0x7bf329ee636d1eea, 0x012b592653589793,
0x4e7b2d0d9b47ba97, 0x34a35dc5ab7233ee, 0xbbcbcc9dfb2ca865, 0xc113bc55cb19211c,
0x5863dbf1e3ac9dec, 0x22bbab39d3991495, 0xadd33a6183c78f1e, 0xd70b4aa9b3f20667,
0x985b3e827bed2b63, 0xe2834e4a4bd8a21a, 0x6debdf121b863991, 0x1733afda2bb3b0e8,
0xf34b37458bb86399, 0x8993478dbb8deae0, 0x06fbd6d5ebd3716b, 0x7c23a61ddbe6f812,
0x3373d23613f9d516, 0x49aba2fe23cc5c6f, 0xc6c333a67392c7e4, 0xbc1b436e43a74e9d,
0x95ac9329ac4bc9b5, 0xef74e3e19c7e40cc, 0x601c72b9cc20db47, 0x1ac40271fc15523e,
0x5594765a340a7f3a, 0x2f4c0692043ff643, 0xa02497ca54616dc8, 0xdafce7026454e4b1,
0x3e847f9dc45f37c0, 0x445c0f55f46abeb9, 0xcb349e0da4342532, 0xb1eceec59401ac4b,
0xfebc9aee5c1e814f, 0x8464ea266c2b0836, 0x0b0c7b7e3c7593bd, 0x71d40bb60c401ac4,
0xe8a46c1224f5a634, 0x927c1cda14c02f4d, 0x1d148d82449eb4c6, 0x67ccfd4a74ab3dbf,
0x289c8961bcb410bb, 0x5244f9a98c8199c2, 0xdd2c68f1dcdf0249, 0xa7f41839ecea8b30,
0x438c80a64ce15841, 0x3954f06e7cd4d138, 0xb63c61362c8a4ab3, 0xcce411fe1cbfc3ca,
0x83b465d5d4a0eece, 0xf96c151de49567b7, 0x76048445b4cbfc3c, 0x0cdcf48d84fe7545,
0x6fbd6d5ebd3716b7, 0x15651d968d029fce, 0x9a0d8ccedd5c0445, 0xe0d5fc06ed698d3c,
0xaf85882d2576a038, 0xd55df8e515432941, 0x5a3569bd451db2ca, 0x20ed197575283bb3,
0xc49581ead523e8c2, 0xbe4df122e51661bb, 0x3125607ab548fa30, 0x4bfd10b2857d7349,
0x04ad64994d625e4d, 0x7e7514517d57d734, 0xf11d85092d094cbf, 0x8bc5f5c11d3cc5c6,
0x12b5926535897936, 0x686de2ad05bcf04f, 0xe70573f555e26bc4, 0x9ddd033d65d7e2bd,
0xd28d7716adc8cfb9, 0xa85507de9dfd46c0, 0x273d9686cda3dd4b, 0x5de5e64efd965432,
0xb99d7ed15d9d8743, 0xc3450e196da80e3a, 0x4c2d9f413df695b1, 0x36f5ef890dc31cc8,
0x79a59ba2c5dc31cc, 0x037deb6af5e9b8b5, 0x8c157a32a5b7233e, 0xf6cd0afa9582aa47,
0x4ad64994d625e4da, 0x300e395ce6106da3, 0xbf66a804b64ef628, 0xc5bed8cc867b7f51,
0x8aeeace74e645255, 0xf036dc2f7e51db2c, 0x7f5e4d772e0f40a7, 0x05863dbf1e3ac9de,
0xe1fea520be311aaf, 0x9b26d5e88e0493d6, 0x144e44b0de5a085d, 0x6e963478ee6f8124,
0x21c640532670ac20, 0x5b1e309b16452559, 0xd476a1c3461bbed2, 0xaeaed10b762e37ab,
0x37deb6af5e9b8b5b, 0x4d06c6676eae0222, 0xc26e573f3ef099a9, 0xb8b627f70ec510d0,
0xf7e653dcc6da3dd4, 0x8d3e2314f6efb4ad, 0x0256b24ca6b12f26, 0x788ec2849684a65f,
0x9cf65a1b368f752e, 0xe62e2ad306bafc57, 0x6946bb8b56e467dc, 0x139ecb4366d1eea5,
0x5ccebf68aecec3a1, 0x2616cfa09efb4ad8, 0xa97e5ef8cea5d153, 0xd3a62e30fe90582a,
0xb0c7b7e3c7593bd8, 0xca1fc72bf76cb2a1, 0x45775673a732292a, 0x3faf26bb9707a053,
0x70ff52905f188d57, 0x0a2722586f2d042e, 0x854fb3003f739fa5, 0xff97c3c80f4616dc,
0x1bef5b57af4dc5ad, 0x61372b9f9f784cd4, 0xee5fbac7cf26d75f, 0x9487ca0fff135e26,
0xdbd7be24370c7322, 0xa10fceec0739fa5b, 0x2e675fb4576761d0, 0x54bf2f7c6752e8a9,
0xcdcf48d84fe75459, 0xb71738107fd2dd20, 0x387fa9482f8c46ab, 0x42a7d9801fb9cfd2,
0x0df7adabd7a6e2d6, 0x772fdd63e7936baf, 0xf8474c3bb7cdf024, 0x829f3cf387f8795d,
0x66e7a46c27f3aa2c, 0x1c3fd4a417c62355, 0x935745fc4798b8de, 0xe98f353477ad31a7,
0xa6df411fbfb21ca3, 0xdc0731d78f8795da, 0x536fa08fdfd90e51, 0x29b7d047efec8728}
type digest struct {
crc uint64
}
func (d *digest) update(p []byte) {
for _, b := range p {
d.crc = crc64_table[byte(d.crc)^b] ^ (d.crc >> 8)
}
}
func newDigest() hash.Hash64 {
d := &digest{}
return d
}
func (d *digest) Write(p []byte) (int, error) {
d.update(p)
return len(p), nil
}
func (d *digest) Sum(in []byte) []byte {
buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, d.crc)
return append(in, buf...)
}
func (d *digest) Sum64() uint64 { return d.crc }
func (d *digest) BlockSize() int { return 1 }
func (d *digest) Size() int { return 8 }
func (d *digest) Reset() { d.crc = 0 }

View File

@ -15,7 +15,7 @@ func Dump(obj interface{}) ([]byte, error) {
case String:
e.EncodeType(rdb.TypeString)
e.EncodeString(v)
case HashMap:
case Hash:
e.EncodeType(rdb.TypeHash)
e.EncodeLength(uint32(len(v)))

View File

@ -0,0 +1,112 @@
// Copyright 2014 Wandoujia Inc. All Rights Reserved.
// Licensed under the MIT (MIT-LICENSE.txt) license.
package rdb
import (
"bytes"
"encoding/binary"
"fmt"
"hash"
"io"
"strconv"
)
type Loader struct {
*rdbReader
crc hash.Hash64
db uint32
}
func NewLoader(r io.Reader) *Loader {
l := &Loader{}
l.crc = newDigest()
l.rdbReader = newRdbReader(io.TeeReader(r, l.crc))
return l
}
func (l *Loader) LoadHeader() error {
header := make([]byte, 9)
if err := l.readFull(header); err != nil {
return err
}
if !bytes.Equal(header[:5], []byte("REDIS")) {
return fmt.Errorf("verify magic string, invalid file format")
}
if version, err := strconv.ParseInt(string(header[5:]), 10, 64); err != nil {
return err
} else if version <= 0 || version > Version {
return fmt.Errorf("verify version, invalid RDB version number %d", version)
}
return nil
}
func (l *Loader) LoadChecksum() error {
crc1 := l.crc.Sum64()
if crc2, err := l.readUint64(); err != nil {
return err
} else if crc1 != crc2 {
return fmt.Errorf("checksum validation failed")
}
return nil
}
type Entry struct {
DB uint32
Key []byte
ValDump []byte
ExpireAt uint64
}
func (l *Loader) LoadEntry() (entry *Entry, err error) {
var expireat uint64
for {
var otype byte
if otype, err = l.readByte(); err != nil {
return
}
switch otype {
case rdbFlagExpiryMS:
if expireat, err = l.readUint64(); err != nil {
return
}
case rdbFlagExpiry:
var sec uint32
if sec, err = l.readUint32(); err != nil {
return
}
expireat = uint64(sec) * 1000
case rdbFlagSelectDB:
if l.db, err = l.readLength(); err != nil {
return
}
case rdbFlagEOF:
return
default:
var key, obj []byte
if key, err = l.readString(); err != nil {
return
}
if obj, err = l.readObject(otype); err != nil {
return
}
entry = &Entry{}
entry.DB = l.db
entry.Key = key
entry.ValDump = createValDump(otype, obj)
entry.ExpireAt = expireat
return
}
}
}
func createValDump(otype byte, obj []byte) []byte {
var b bytes.Buffer
c := newDigest()
w := io.MultiWriter(&b, c)
w.Write([]byte{otype})
w.Write(obj)
binary.Write(w, binary.LittleEndian, uint16(Version))
binary.Write(w, binary.LittleEndian, c.Sum64())
return b.Bytes()
}

View File

@ -0,0 +1,373 @@
// Copyright 2014 Wandoujia Inc. All Rights Reserved.
// Licensed under the MIT (MIT-LICENSE.txt) license.
package rdb
import (
"bytes"
"encoding/hex"
"fmt"
"math"
"strconv"
"strings"
"testing"
)
func AssertNoError(t *testing.T, err error) {
if err == nil {
return
}
t.Fatal(err)
}
func Assert(t *testing.T, b bool) {
if b {
return
}
t.Fatal("assertion failed")
}
func DecodeHexRdb(t *testing.T, s string, n int) map[string]*Entry {
p, err := hex.DecodeString(strings.NewReplacer("\t", "", "\r", "", "\n", "", " ", "").Replace(s))
AssertNoError(t, err)
r := bytes.NewReader(p)
l := NewLoader(r)
AssertNoError(t, l.LoadHeader())
entries := make(map[string]*Entry)
var i int = 0
for {
e, err := l.LoadEntry()
AssertNoError(t, err)
if e == nil {
break
}
Assert(t, e.DB == 0)
entries[string(e.Key)] = e
i++
}
AssertNoError(t, l.LoadChecksum())
Assert(t, r.Len() == 0)
Assert(t, len(entries) == i && i == n)
return entries
}
func getobj(t *testing.T, entries map[string]*Entry, key string) (*Entry, interface{}) {
e := entries[key]
Assert(t, e != nil)
val, err := DecodeDump(e.ValDump)
AssertNoError(t, err)
return e, val
}
/*
#!/bin/bash
./redis-cli flushall
for i in 1 255 256 65535 65536 2147483647 2147483648 4294967295 4294967296 -2147483648; do
./redis-cli set string_${i} ${i}
done
./redis-cli save && xxd -p -c 32 dump.rdb
*/
func TestLoadIntString(t *testing.T) {
s := `
524544495330303036fe00000a737472696e675f323535c1ff00000873747269
6e675f31c0010011737472696e675f343239343936373239360a343239343936
373239360011737472696e675f343239343936373239350a3432393439363732
39350012737472696e675f2d32313437343833363438c200000080000c737472
696e675f3635353335c2ffff00000011737472696e675f323134373438333634
380a32313437343833363438000c737472696e675f3635353336c20000010000
0a737472696e675f323536c100010011737472696e675f323134373438333634
37c2ffffff7fffe49d9f131fb5c3b5
`
values := []int{1, 255, 256, 65535, 65536, 2147483647, 2147483648, 4294967295, 4294967296, -2147483648}
entries := DecodeHexRdb(t, s, len(values))
for _, value := range values {
key := fmt.Sprintf("string_%d", value)
_, obj := getobj(t, entries, key)
val := obj.(String)
Assert(t, bytes.Equal([]byte(val), []byte(strconv.Itoa(value))))
}
}
/*
#!/bin/bash
./redis-cli flushall
./redis-cli set string_ttls string_ttls
./redis-cli expireat string_ttls 1500000000
./redis-cli set string_ttlms string_ttlms
./redis-cli pexpireat string_ttlms 1500000000000
./redis-cli save && xxd -p -c 32 dump.rdb
*/
func TestLoadStringTTL(t *testing.T) {
s := `
524544495330303036fe00fc0098f73e5d010000000c737472696e675f74746c
6d730c737472696e675f74746c6d73fc0098f73e5d010000000b737472696e67
5f74746c730b737472696e675f74746c73ffd15acd935a3fe949
`
expireat := uint64(1500000000000)
entries := DecodeHexRdb(t, s, 2)
keys := []string{"string_ttls", "string_ttlms"}
for _, key := range keys {
e, obj := getobj(t, entries, key)
val := obj.(String)
Assert(t, bytes.Equal([]byte(val), []byte(key)))
Assert(t, e.ExpireAt == expireat)
}
}
/*
#!/bin/bash
s="01"
for ((i=0;i<15;i++)); do
s=$s$s
done
./redis-cli flushall
./redis-cli set string_long $s
./redis-cli save && xxd -p -c 32 dump.rdb
*/
func TestLoadLongString(t *testing.T) {
s := `
524544495330303036fe00000b737472696e675f6c6f6e67c342f28000010000
02303130e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0
ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01
e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff
01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0
ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01
e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff
01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0
ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01
e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff
01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0
ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01
e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff
01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0
ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01
e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff
01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0
ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01
e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff
01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0
ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01
e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff
01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0
ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01
e0ff01e0ff01e0ff01e0ff01e03201013031ffdfdb02bd6d5da5e6
`
entries := DecodeHexRdb(t, s, 1)
_, obj := getobj(t, entries, "string_long")
val := []byte(obj.(String))
for i := 0; i < (1 << 15); i++ {
var c uint8 = '0'
if i%2 != 0 {
c = '1'
}
Assert(t, val[i] == c)
}
}
/*
#!/bin/bash
./redis-cli flushall
for ((i=0;i<256;i++)); do
./redis-cli rpush list_lzf 0
./redis-cli rpush list_lzf 1
done
./redis-cli save && xxd -p -c 32 dump.rdb
*/
func TestLoadListZipmap(t *testing.T) {
s := `
524544495330303036fe000a086c6973745f6c7a66c31f440b040b0400000820
0306000200f102f202e0ff03e1ff07e1ff07e1d90701f2ffff6a1c2d51c02301
16
`
entries := DecodeHexRdb(t, s, 1)
_, obj := getobj(t, entries, "list_lzf")
val := obj.(List)
Assert(t, len(val) == 512)
for i := 0; i < 256; i++ {
var s string = "0"
if i%2 != 0 {
s = "1"
}
Assert(t, string(val[i]) == s)
}
}
/*
#!/bin/bash
./redis-cli flushall
for ((i=0;i<32;i++)); do
./redis-cli rpush list ${i}
done
./redis-cli save && xxd -p -c 32 dump.rdb
*/
func TestLoadList(t *testing.T) {
s := `
524544495330303036fe0001046c69737420c000c001c002c003c004c005c006
c007c008c009c00ac00bc00cc00dc00ec00fc010c011c012c013c014c015c016
c017c018c019c01ac01bc01cc01dc01ec01fff756ea1fa90adefe3
`
entries := DecodeHexRdb(t, s, 1)
_, obj := getobj(t, entries, "list")
val := obj.(List)
Assert(t, len(val) == 32)
for i := 0; i < 32; i++ {
Assert(t, string(val[i]) == strconv.Itoa(i))
}
}
/*
#!/bin/bash
./redis-cli flushall
for ((i=0;i<16;i++)); do
./redis-cli sadd set1 ${i}
done
for ((i=0;i<32;i++)); do
./redis-cli sadd set2 ${i}
done
./redis-cli save && xxd -p -c 32 dump.rdb
*/
func TestLoadSetAndSetIntset(t *testing.T) {
s := `
524544495330303036fe0002047365743220c016c00dc01bc012c01ac004c014
c002c017c01dc01cc013c019c01ec008c006c000c001c007c00fc009c01fc00e
c003c00ac015c010c00bc018c011c00cc0050b04736574312802000000100000
0000000100020003000400050006000700080009000a000b000c000d000e000f
00ff3a0a9697324d19c3
`
entries := DecodeHexRdb(t, s, 2)
_, obj1 := getobj(t, entries, "set1")
val1 := obj1.(Set)
set1 := make(map[string]bool)
for _, mem := range val1 {
set1[string(mem)] = true
}
Assert(t, len(set1) == 16)
Assert(t, len(set1) == len(val1))
for i := 0; i < 16; i++ {
_, ok := set1[strconv.Itoa(i)]
Assert(t, ok)
}
_, obj2 := getobj(t, entries, "set2")
val2 := obj2.(Set)
set2 := make(map[string]bool)
for _, mem := range val2 {
set2[string(mem)] = true
}
Assert(t, len(set2) == 32)
Assert(t, len(set2) == len(val2))
for i := 0; i < 32; i++ {
_, ok := set2[strconv.Itoa(i)]
Assert(t, ok)
}
}
/*
#!/bin/bash
./redis-cli flushall
for ((i=0;i<16;i++)); do
./redis-cli hset hash1 ${i}
done
for ((i=-16;i<16;i++)); do
./redis-cli hset hash2 ${i}
done
./redis-cli save && xxd -p -c 32 dump.rdb
*/
func TestLoadHashAndHashZiplist(t *testing.T) {
s := `
524544495330303036fe000405686173683220c00dc00dc0fcc0fcc0ffc0ffc0
04c004c002c002c0fbc0fbc0f0c0f0c0f9c0f9c008c008c0fac0fac006c006c0
00c000c001c001c0fec0fec007c007c0f6c0f6c00fc00fc009c009c0f7c0f7c0
fdc0fdc0f1c0f1c0f2c0f2c0f3c0f3c00ec00ec003c003c00ac00ac00bc00bc0
f8c0f8c00cc00cc0f5c0f5c0f4c0f4c005c0050d056861736831405151000000
4d000000200000f102f102f202f202f302f302f402f402f502f502f602f602f7
02f702f802f802f902f902fa02fa02fb02fb02fc02fc02fd02fd02fe0d03fe0d
03fe0e03fe0e03fe0f03fe0fffffa423d3036c15e534
`
entries := DecodeHexRdb(t, s, 2)
_, obj1 := getobj(t, entries, "hash1")
val1 := obj1.(Hash)
hash1 := make(map[string]string)
for _, ent := range val1 {
hash1[string(ent.Field)] = string(ent.Value)
}
Assert(t, len(hash1) == 16)
Assert(t, len(hash1) == len(val1))
for i := 0; i < 16; i++ {
s := strconv.Itoa(i)
Assert(t, hash1[s] == s)
}
_, obj2 := getobj(t, entries, "hash2")
val2 := obj2.(Hash)
hash2 := make(map[string]string)
for _, ent := range val2 {
hash2[string(ent.Field)] = string(ent.Value)
}
Assert(t, len(hash2) == 32)
Assert(t, len(hash2) == len(val2))
for i := -16; i < 16; i++ {
s := strconv.Itoa(i)
Assert(t, hash2[s] == s)
}
}
/*
#!/bin/bash
./redis-cli flushall
for ((i=0;i<16;i++)); do
./redis-cli zadd zset1 ${i} ${i}
done
for ((i=0;i<32;i++)); do
./redis-cli zadd zset2 -${i} ${i}
done
./redis-cli save && xxd -p -c 32 dump.rdb
*/
func TestLoadZSetAndZSetZiplist(t *testing.T) {
s := `
524544495330303036fe0003057a7365743220c016032d3232c00d032d3133c0
1b032d3237c012032d3138c01a032d3236c004022d34c014032d3230c002022d
32c017032d3233c01d032d3239c01c032d3238c013032d3139c019032d3235c0
1e032d3330c008022d38c006022d36c000022d30c001022d31c007022d37c009
022d39c00f032d3135c01f032d3331c00e032d3134c003022d33c00a032d3130
c015032d3231c010032d3136c00b032d3131c018032d3234c011032d3137c00c
032d3132c005022d350c057a736574314051510000004d000000200000f102f1
02f202f202f302f302f402f402f502f502f602f602f702f702f802f802f902f9
02fa02fa02fb02fb02fc02fc02fd02fd02fe0d03fe0d03fe0e03fe0e03fe0f03
fe0fffff2addedbf4f5a8f93
`
entries := DecodeHexRdb(t, s, 2)
_, obj1 := getobj(t, entries, "zset1")
val1 := obj1.(ZSet)
zset1 := make(map[string]float64)
for _, ent := range val1 {
zset1[string(ent.Member)] = ent.Score
}
Assert(t, len(zset1) == 16)
Assert(t, len(zset1) == len(val1))
for i := 0; i < 16; i++ {
s := strconv.Itoa(i)
score, ok := zset1[s]
Assert(t, ok)
Assert(t, math.Abs(score-float64(i)) < 1e-10)
}
_, obj2 := getobj(t, entries, "zset2")
val2 := obj2.(ZSet)
zset2 := make(map[string]float64)
for _, ent := range val2 {
zset2[string(ent.Member)] = ent.Score
}
Assert(t, len(zset2) == 32)
Assert(t, len(zset2) == len(val2))
for i := 0; i < 32; i++ {
s := strconv.Itoa(i)
score, ok := zset2[s]
Assert(t, ok)
Assert(t, math.Abs(score+float64(i)) < 1e-10)
}
}

View File

@ -0,0 +1,332 @@
// Copyright 2014 Wandoujia Inc. All Rights Reserved.
// Licensed under the MIT (MIT-LICENSE.txt) license.
package rdb
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"math"
"strconv"
)
const (
Version = 6
)
const (
rdbTypeString = 0
rdbTypeList = 1
rdbTypeSet = 2
rdbTypeZSet = 3
rdbTypeHash = 4
rdbTypeHashZipmap = 9
rdbTypeListZiplist = 10
rdbTypeSetIntset = 11
rdbTypeZSetZiplist = 12
rdbTypeHashZiplist = 13
rdbFlagExpiryMS = 0xfc
rdbFlagExpiry = 0xfd
rdbFlagSelectDB = 0xfe
rdbFlagEOF = 0xff
)
const (
rdb6bitLen = 0
rdb14bitLen = 1
rdb32bitLen = 2
rdbEncVal = 3
rdbEncInt8 = 0
rdbEncInt16 = 1
rdbEncInt32 = 2
rdbEncLZF = 3
rdbZiplist6bitlenString = 0
rdbZiplist14bitlenString = 1
rdbZiplist32bitlenString = 2
rdbZiplistInt16 = 0xc0
rdbZiplistInt32 = 0xd0
rdbZiplistInt64 = 0xe0
rdbZiplistInt24 = 0xf0
rdbZiplistInt8 = 0xfe
rdbZiplistInt4 = 15
)
type rdbReader struct {
raw io.Reader
buf [8]byte
nread int64
}
func newRdbReader(r io.Reader) *rdbReader {
return &rdbReader{raw: r}
}
func (r *rdbReader) Read(p []byte) (int, error) {
n, err := r.raw.Read(p)
r.nread += int64(n)
return n, err
}
func (r *rdbReader) offset() int64 {
return r.nread
}
func (r *rdbReader) readObject(otype byte) ([]byte, error) {
var b bytes.Buffer
r = newRdbReader(io.TeeReader(r, &b))
switch otype {
default:
return nil, fmt.Errorf("unknown object-type %02x", otype)
case rdbTypeHashZipmap:
fallthrough
case rdbTypeListZiplist:
fallthrough
case rdbTypeSetIntset:
fallthrough
case rdbTypeZSetZiplist:
fallthrough
case rdbTypeHashZiplist:
fallthrough
case rdbTypeString:
if _, err := r.readString(); err != nil {
return nil, err
}
case rdbTypeList, rdbTypeSet:
if n, err := r.readLength(); err != nil {
return nil, err
} else {
for i := 0; i < int(n); i++ {
if _, err := r.readString(); err != nil {
return nil, err
}
}
}
case rdbTypeZSet:
if n, err := r.readLength(); err != nil {
return nil, err
} else {
for i := 0; i < int(n); i++ {
if _, err := r.readString(); err != nil {
return nil, err
}
if _, err := r.readFloat(); err != nil {
return nil, err
}
}
}
case rdbTypeHash:
if n, err := r.readLength(); err != nil {
return nil, err
} else {
for i := 0; i < int(n); i++ {
if _, err := r.readString(); err != nil {
return nil, err
}
if _, err := r.readString(); err != nil {
return nil, err
}
}
}
}
return b.Bytes(), nil
}
func (r *rdbReader) readString() ([]byte, error) {
length, encoded, err := r.readEncodedLength()
if err != nil {
return nil, err
}
if !encoded {
return r.readBytes(int(length))
}
switch t := uint8(length); t {
default:
return nil, fmt.Errorf("invalid encoded-string %02x", t)
case rdbEncInt8:
i, err := r.readInt8()
return []byte(strconv.FormatInt(int64(i), 10)), err
case rdbEncInt16:
i, err := r.readInt16()
return []byte(strconv.FormatInt(int64(i), 10)), err
case rdbEncInt32:
i, err := r.readInt32()
return []byte(strconv.FormatInt(int64(i), 10)), err
case rdbEncLZF:
var inlen, outlen uint32
if inlen, err = r.readLength(); err != nil {
return nil, err
}
if outlen, err = r.readLength(); err != nil {
return nil, err
}
if in, err := r.readBytes(int(inlen)); err != nil {
return nil, err
} else {
return lzfDecompress(in, int(outlen))
}
}
}
func (r *rdbReader) readEncodedLength() (length uint32, encoded bool, err error) {
var u uint8
if u, err = r.readUint8(); err != nil {
return
}
length = uint32(u & 0x3f)
switch u >> 6 {
case rdb6bitLen:
case rdb14bitLen:
u, err = r.readUint8()
length = (length << 8) + uint32(u)
case rdbEncVal:
encoded = true
default:
length, err = r.readUint32BigEndian()
}
return
}
func (r *rdbReader) readLength() (uint32, error) {
length, encoded, err := r.readEncodedLength()
if err == nil && encoded {
err = fmt.Errorf("encoded-length")
}
return length, err
}
func (r *rdbReader) readFloat() (float64, error) {
u, err := r.readUint8()
if err != nil {
return 0, err
}
switch u {
case 253:
return math.NaN(), nil
case 254:
return math.Inf(0), nil
case 255:
return math.Inf(-1), nil
default:
if b, err := r.readBytes(int(u)); err != nil {
return 0, err
} else {
v, err := strconv.ParseFloat(string(b), 64)
return v, err
}
}
}
func (r *rdbReader) readByte() (byte, error) {
b := r.buf[:1]
_, err := r.Read(b)
return b[0], err
}
func (r *rdbReader) readFull(p []byte) error {
_, err := io.ReadFull(r, p)
return err
}
func (r *rdbReader) readBytes(n int) ([]byte, error) {
p := make([]byte, n)
return p, r.readFull(p)
}
func (r *rdbReader) readUint8() (uint8, error) {
b, err := r.readByte()
return uint8(b), err
}
func (r *rdbReader) readUint16() (uint16, error) {
b := r.buf[:2]
err := r.readFull(b)
return binary.LittleEndian.Uint16(b), err
}
func (r *rdbReader) readUint32() (uint32, error) {
b := r.buf[:4]
err := r.readFull(b)
return binary.LittleEndian.Uint32(b), err
}
func (r *rdbReader) readUint64() (uint64, error) {
b := r.buf[:8]
err := r.readFull(b)
return binary.LittleEndian.Uint64(b), err
}
func (r *rdbReader) readUint32BigEndian() (uint32, error) {
b := r.buf[:4]
err := r.readFull(b)
return binary.BigEndian.Uint32(b), err
}
func (r *rdbReader) readInt8() (int8, error) {
u, err := r.readUint8()
return int8(u), err
}
func (r *rdbReader) readInt16() (int16, error) {
u, err := r.readUint16()
return int16(u), err
}
func (r *rdbReader) readInt32() (int32, error) {
u, err := r.readUint32()
return int32(u), err
}
func (r *rdbReader) readInt64() (int64, error) {
u, err := r.readUint64()
return int64(u), err
}
func (r *rdbReader) readInt32BigEndian() (int32, error) {
u, err := r.readUint32BigEndian()
return int32(u), err
}
func lzfDecompress(in []byte, outlen int) (out []byte, err error) {
defer func() {
if x := recover(); x != nil {
err = fmt.Errorf("decompress exception: %v", x)
}
}()
out = make([]byte, outlen)
i, o := 0, 0
for i < len(in) {
ctrl := int(in[i])
i++
if ctrl < 32 {
for x := 0; x <= ctrl; x++ {
out[o] = in[i]
i++
o++
}
} else {
length := ctrl >> 5
if length == 7 {
length = length + int(in[i])
i++
}
ref := o - ((ctrl & 0x1f) << 8) - int(in[i]) - 1
i++
for x := 0; x <= length+1; x++ {
out[o] = out[ref]
ref++
o++
}
}
}
if o != outlen {
return nil, fmt.Errorf("decompress length is %d != expected %d", o, outlen)
}
return out, nil
}

View File

@ -14,14 +14,17 @@ export GO_BUILD_TAGS
all: build
build:
$(GO) install -tags '$(GO_BUILD_TAGS)' ./...
clean:
$(GO) clean -i ./...
$(GO) install -tags 'linenoise $(GO_BUILD_TAGS)' ./...
build_use_lmdb:
$(GO) install -tags 'linenoise $(GO_BUILD_TAGS) lmdb' ./...
test:
$(GO) test --race -tags '$(GO_BUILD_TAGS)' ./...
test_use_lmdb:
$(GO) test --race -tags '$(GO_BUILD_TAGS) lmdb' ./...
test_ledis:
$(GO) test --race -tags '$(GO_BUILD_TAGS)' ./ledis
@ -34,5 +37,8 @@ test_store:
test_rpl:
$(GO) test --race -tags '$(GO_BUILD_TAGS)' ./rpl
clean:
$(GO) clean -i ./...
fmt:
go fmt ./...

View File

@ -19,3 +19,5 @@ go get -u github.com/syndtr/goleveldb/leveldb
go get -u github.com/cupcake/rdb
go get -u github.com/siddontang/go
go get -u github.com/siddontang/goredis
go get -u github.com/siddontang/rdb

View File

@ -1 +0,0 @@
[Clients](https://github.com/siddontang/ledisdb/wiki/Clients)

View File

@ -1,45 +0,0 @@
// Package ledis is a client for the ledisdb.
//
// Config
//
// Config struct contains configuration for ledisdb:
//
// Addr ledisdb server address, like 127.0.0.1:6380
// MaxIdleConns max idle connections for ledisdb
//
// Client
//
// The client is the primary interface for ledisdb. You must first create a client with proper config for working.
//
// cfg := new(Config)
// cfg.Addr = "127.0.0.1:6380"
// cfg.MaxIdleConns = 4
//
// c := NewClient(cfg)
//
// The most important function for client is Do function to send commands to remote server.
//
// reply, err := c.Do("ping")
//
// reply, err := c.Do("set", "key", "value")
//
// reply, err := c.Do("get", "key")
//
// Connection
//
// You can use an independent connection to send commands.
//
// //get a connection
// conn := c.Get()
//
// //connection send command
// conn.Do("ping")
//
// Reply Helper
//
// You can use reply helper to convert a reply to a specific type.
//
// exists, err := ledis.Bool(c.Do("exists", "key"))
//
// score, err := ledis.Int64(c.Do("zscore", "key", "member"))
package ledis

View File

@ -1,15 +0,0 @@
package ledis
import (
"testing"
)
func TestClient(t *testing.T) {
cfg := new(Config)
cfg.Addr = "127.0.0.1:6380"
cfg.MaxIdleConns = 4
c := NewClient(cfg)
c.Close()
}

View File

@ -3,7 +3,7 @@ package main
import (
"flag"
"fmt"
"github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"math/rand"
"runtime"
"strings"
@ -21,10 +21,10 @@ var valueSize = flag.Int("vsize", 100, "kv value size")
var tests = flag.String("t", "set,get,randget,del,lpush,lrange,lpop,hset,hget,hdel,zadd,zincr,zrange,zrevrange,zdel", "only run the comma separated list of tests")
var wg sync.WaitGroup
var client *ledis.Client
var client *goredis.Client
var loop int = 0
func waitBench(c *ledis.Conn, cmd string, args ...interface{}) {
func waitBench(c *goredis.PoolConn, cmd string, args ...interface{}) {
_, err := c.Do(strings.ToUpper(cmd), args...)
if err != nil {
fmt.Printf("do %s error %s\n", cmd, err.Error())
@ -32,7 +32,7 @@ func waitBench(c *ledis.Conn, cmd string, args ...interface{}) {
}
func bench(cmd string, f func(c *ledis.Conn)) {
func bench(cmd string, f func(c *goredis.PoolConn)) {
wg.Add(*clients)
t1 := time.Now()
@ -66,7 +66,7 @@ var kvIncrBase int64 = 0
var kvDelBase int64 = 0
func benchSet() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
value := make([]byte, *valueSize)
n := atomic.AddInt64(&kvSetBase, 1)
waitBench(c, "SET", n, value)
@ -76,7 +76,7 @@ func benchSet() {
}
func benchGet() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
n := atomic.AddInt64(&kvGetBase, 1)
waitBench(c, "GET", n)
}
@ -85,7 +85,7 @@ func benchGet() {
}
func benchRandGet() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
n := rand.Int() % *number
waitBench(c, "GET", n)
}
@ -94,7 +94,7 @@ func benchRandGet() {
}
func benchDel() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
n := atomic.AddInt64(&kvDelBase, 1)
waitBench(c, "DEL", n)
}
@ -103,7 +103,7 @@ func benchDel() {
}
func benchPushList() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
value := make([]byte, 100)
waitBench(c, "RPUSH", "mytestlist", value)
}
@ -112,7 +112,7 @@ func benchPushList() {
}
func benchRangeList10() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
waitBench(c, "LRANGE", "mytestlist", 0, 10)
}
@ -120,7 +120,7 @@ func benchRangeList10() {
}
func benchRangeList50() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
waitBench(c, "LRANGE", "mytestlist", 0, 50)
}
@ -128,7 +128,7 @@ func benchRangeList50() {
}
func benchRangeList100() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
waitBench(c, "LRANGE", "mytestlist", 0, 100)
}
@ -136,7 +136,7 @@ func benchRangeList100() {
}
func benchPopList() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
waitBench(c, "LPOP", "mytestlist")
}
@ -149,7 +149,7 @@ var hashGetBase int64 = 0
var hashDelBase int64 = 0
func benchHset() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
value := make([]byte, 100)
n := atomic.AddInt64(&hashSetBase, 1)
@ -160,7 +160,7 @@ func benchHset() {
}
func benchHGet() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
n := atomic.AddInt64(&hashGetBase, 1)
waitBench(c, "HGET", "myhashkey", n)
}
@ -169,7 +169,7 @@ func benchHGet() {
}
func benchHRandGet() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
n := rand.Int() % *number
waitBench(c, "HGET", "myhashkey", n)
}
@ -178,7 +178,7 @@ func benchHRandGet() {
}
func benchHDel() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
n := atomic.AddInt64(&hashDelBase, 1)
waitBench(c, "HDEL", "myhashkey", n)
}
@ -191,7 +191,7 @@ var zsetDelBase int64 = 0
var zsetIncrBase int64 = 0
func benchZAdd() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
member := make([]byte, 16)
n := atomic.AddInt64(&zsetAddBase, 1)
waitBench(c, "ZADD", "myzsetkey", n, member)
@ -201,7 +201,7 @@ func benchZAdd() {
}
func benchZDel() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
n := atomic.AddInt64(&zsetDelBase, 1)
waitBench(c, "ZREM", "myzsetkey", n)
}
@ -210,7 +210,7 @@ func benchZDel() {
}
func benchZIncr() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
n := atomic.AddInt64(&zsetIncrBase, 1)
waitBench(c, "ZINCRBY", "myzsetkey", 1, n)
}
@ -219,7 +219,7 @@ func benchZIncr() {
}
func benchZRangeByScore() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
waitBench(c, "ZRANGEBYSCORE", "myzsetkey", 0, rand.Int(), "withscores", "limit", rand.Int()%100, 100)
}
@ -227,7 +227,7 @@ func benchZRangeByScore() {
}
func benchZRangeByRank() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
waitBench(c, "ZRANGE", "myzsetkey", 0, rand.Int()%100)
}
@ -235,7 +235,7 @@ func benchZRangeByRank() {
}
func benchZRevRangeByScore() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
waitBench(c, "ZREVRANGEBYSCORE", "myzsetkey", 0, rand.Int(), "withscores", "limit", rand.Int()%100, 100)
}
@ -243,7 +243,7 @@ func benchZRevRangeByScore() {
}
func benchZRevRangeByRank() {
f := func(c *ledis.Conn) {
f := func(c *goredis.PoolConn) {
waitBench(c, "ZREVRANGE", "myzsetkey", 0, rand.Int()%100)
}
@ -269,12 +269,10 @@ func main() {
addr := fmt.Sprintf("%s:%d", *ip, *port)
cfg := new(ledis.Config)
cfg.Addr = addr
cfg.MaxIdleConns = *clients
cfg.ReadBufferSize = 10240
cfg.WriteBufferSize = 10240
client = ledis.NewClient(cfg)
client = goredis.NewClient(addr, "")
client.SetReadBufferSize(10240)
client.SetWriteBufferSize(10240)
client.SetMaxIdleConns(16)
for i := 0; i < *clients; i++ {
c, _ := client.Get()

View File

@ -0,0 +1,16 @@
package main
// CompletionHandler provides possible completions for given input
type CompletionHandler func(input string) []string
// DefaultCompletionHandler simply returns an empty slice.
var DefaultCompletionHandler = func(input string) []string {
return make([]string, 0)
}
var complHandler = DefaultCompletionHandler
// SetCompletionHandler sets the CompletionHandler to be used for completion
func SetCompletionHandler(c CompletionHandler) {
complHandler = c
}

View File

@ -1,4 +1,4 @@
//This file was generated by .tools/generate_commands.py on Wed Mar 04 2015 09:31:59 +0800
//This file was generated by .tools/generate_commands.py on Thu Mar 05 2015 15:42:49 +0800
package main
var helpCommands = [][]string{
@ -106,10 +106,13 @@ var helpCommands = [][]string{
{"SYNC", "logid", "Replication"},
{"TIME", "-", "Server"},
{"TTL", "key", "KV"},
{"XHSCAN", "key cursor [MATCH match] [COUNT count]", "Hash"},
{"XSCAN", "type cursor [MATCH match] [COUNT count]", "Server"},
{"XSSCAN", "key cursor [MATCH match] [COUNT count]", "Set"},
{"XZSCAN", "key cursor [MATCH match] [COUNT count]", "ZSet"},
{"XHSCAN", "key cursor [MATCH match] [COUNT count] [ASC|DESC]", "Hash"},
{"XLSORT", "key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]", "List"},
{"XSCAN", "type cursor [MATCH match] [COUNT count] [ASC|DESC]", "Server"},
{"XSSCAN", "key cursor [MATCH match] [COUNT count] [ASC|DESC]", "Set"},
{"XSSORT", "key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]", "Set"},
{"XZSCAN", "key cursor [MATCH match] [COUNT count] [ASC|DESC]", "ZSet"},
{"XZSORT", "key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]", "ZSet"},
{"ZADD", "key score member [score member ...]", "ZSet"},
{"ZCARD", "key", "ZSet"},
{"ZCLEAR", "key", "ZSet"},
@ -119,7 +122,7 @@ var helpCommands = [][]string{
{"ZEXPIREAT", "key timestamp", "ZSet"},
{"ZINCRBY", "key increment member", "ZSet"},
{"ZINTERSTORE", "destkey numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]", "ZSet"},
{"ZKEYEXISTS", "ZSet", "Hash"},
{"ZKEYEXISTS", "key", "ZSet"},
{"ZLEXCOUNT", "key min max", "ZSet"},
{"ZMCLEAR", "key [key ...]", "ZSet"},
{"ZPERSIST", "key", "ZSet"},

View File

@ -1,3 +1,5 @@
// +build linenoise
/* linenoise.c -- guerrilla line editing library against the idea that a
* line editing lib needs to be 20,000 lines of C code.
*

View File

@ -1,3 +1,5 @@
// +build linenoise
package main
//#include <stdlib.h>
@ -47,21 +49,6 @@ func setHistoryCapacity(capacity int) error {
return nil
}
// CompletionHandler provides possible completions for given input
type CompletionHandler func(input string) []string
// DefaultCompletionHandler simply returns an empty slice.
var DefaultCompletionHandler = func(input string) []string {
return make([]string, 0)
}
var complHandler = DefaultCompletionHandler
// SetCompletionHandler sets the CompletionHandler to be used for completion
func SetCompletionHandler(c CompletionHandler) {
complHandler = c
}
// typedef struct linenoiseCompletions {
// size_t len;
// char **cvec;

View File

@ -1,3 +1,5 @@
// +build linenoise
/* linenoise.h -- guerrilla line editing library against the idea that a
* line editing lib needs to be 20,000 lines of C code.
*

View File

@ -1,3 +1,4 @@
// +build linenoise
#include "linenoiseCompletionCallbackHook.h"

View File

@ -1,3 +1,4 @@
// +build linenoise
#include <stdlib.h>
#include "linenoise.h"

View File

@ -1,9 +1,11 @@
// +build linenoise
package main
import (
"flag"
"fmt"
"github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"regexp"
"strconv"
"strings"
@ -17,16 +19,15 @@ var dbn = flag.Int("n", 0, "ledisdb database number(default 0)")
func main() {
flag.Parse()
cfg := new(ledis.Config)
var addr string
if len(*socket) > 0 {
cfg.Addr = *socket
addr = *socket
} else {
cfg.Addr = fmt.Sprintf("%s:%d", *ip, *port)
addr = fmt.Sprintf("%s:%d", *ip, *port)
}
cfg.MaxIdleConns = 1
c := ledis.NewClient(cfg)
c := goredis.NewClient(addr, "")
c.SetMaxIdleConns(1)
sendSelect(c, *dbn)
SetCompletionHandler(completionHandler)
@ -38,9 +39,9 @@ func main() {
for {
if *dbn > 0 && *dbn < 16 {
prompt = fmt.Sprintf("%s[%d]>", cfg.Addr, *dbn)
prompt = fmt.Sprintf("%s[%d]>", addr, *dbn)
} else {
prompt = fmt.Sprintf("%s>", cfg.Addr)
prompt = fmt.Sprintf("%s>", addr)
}
cmd, err := line(prompt)
@ -102,7 +103,7 @@ func printReply(level int, reply interface{}) {
fmt.Printf("%q", reply)
case nil:
fmt.Printf("(nil)")
case ledis.Error:
case goredis.Error:
fmt.Printf("%s", string(reply))
case []interface{}:
for i, v := range reply {
@ -154,7 +155,7 @@ func printHelp(cmds []string) {
}
}
func sendSelect(client *ledis.Client, index int) {
func sendSelect(client *goredis.Client, index int) {
if index > 16 || index < 0 {
index = 0
fmt.Println("index out of range, should less than 16")

View File

@ -0,0 +1,7 @@
// +build !linenoise
package main
func main() {
println("Please use linenoise to build again, or use redis-cli instead")
}

View File

@ -1,177 +0,0 @@
package main
import (
"flag"
"fmt"
"github.com/siddontang/go/num"
"github.com/siddontang/ledisdb/config"
"github.com/siddontang/ledisdb/ledis"
"os"
"runtime"
"sync"
"sync/atomic"
"time"
)
var KB = config.KB
var MB = config.MB
var GB = config.GB
var name = flag.String("db_name", "goleveldb", "db name")
var number = flag.Int("n", 10000, "request number")
var clients = flag.Int("c", 50, "number of clients")
var round = flag.Int("r", 1, "benchmark round number")
var valueSize = flag.Int("vsize", 100, "kv value size")
var wg sync.WaitGroup
var ldb *ledis.Ledis
var db *ledis.DB
var loop int = 0
func bench(cmd string, f func()) {
wg.Add(*clients)
t1 := time.Now()
for i := 0; i < *clients; i++ {
go func() {
for j := 0; j < loop; j++ {
f()
}
wg.Done()
}()
}
wg.Wait()
t2 := time.Now()
d := t2.Sub(t1)
fmt.Printf("%s %s: %0.3f micros/op, %0.2fmb/s %0.2fop/s\n",
cmd,
d.String(),
float64(d.Nanoseconds()/1e3)/float64(*number),
float64((*valueSize+16)*(*number))/(1024.0*1024.0*(d.Seconds())),
float64(*number)/d.Seconds())
}
var kvSetBase int64 = 0
var kvGetBase int64 = 0
var value []byte
func benchSet() {
f := func() {
n := atomic.AddInt64(&kvSetBase, 1)
db.Set(num.Int64ToBytes(n), value)
}
bench("set", f)
}
func benchGet() {
kvGetBase = 0
f := func() {
n := atomic.AddInt64(&kvGetBase, 1)
v, err := db.Get(num.Int64ToBytes(n))
if err != nil {
println(err.Error())
} else if len(v) != *valueSize {
println(len(v), *valueSize)
}
}
bench("get", f)
}
var kvGetSliceBase int64 = 0
func benchGetSlice() {
kvGetSliceBase = 0
f := func() {
n := atomic.AddInt64(&kvGetSliceBase, 1)
v, err := db.GetSlice(num.Int64ToBytes(n))
if err != nil {
println(err.Error())
} else if v != nil {
v.Free()
}
}
bench("getslice", f)
}
func setRocksDB(cfg *config.RocksDBConfig) {
cfg.BlockSize = 64 * KB
cfg.WriteBufferSize = 64 * MB
cfg.MaxWriteBufferNum = 2
cfg.MaxBytesForLevelBase = 512 * MB
cfg.TargetFileSizeBase = 64 * MB
cfg.BackgroundThreads = 4
cfg.HighPriorityBackgroundThreads = 1
cfg.MaxBackgroundCompactions = 3
cfg.MaxBackgroundFlushes = 1
cfg.CacheSize = 512 * MB
cfg.EnableStatistics = true
cfg.StatsDumpPeriodSec = 5
cfg.Level0FileNumCompactionTrigger = 8
cfg.MaxBytesForLevelMultiplier = 8
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
flag.Parse()
value = make([]byte, *valueSize)
cfg := config.NewConfigDefault()
cfg.DataDir = "./var/ledis_dbbench"
cfg.DBName = *name
os.RemoveAll(cfg.DBPath)
defer os.RemoveAll(cfg.DBPath)
os.MkdirAll(cfg.DBPath, 0755)
cfg.LevelDB.BlockSize = 32 * KB
cfg.LevelDB.CacheSize = 512 * MB
cfg.LevelDB.WriteBufferSize = 64 * MB
cfg.LevelDB.MaxOpenFiles = 1000
setRocksDB(&cfg.RocksDB)
var err error
ldb, err = ledis.Open(cfg)
if err != nil {
println(err.Error())
return
}
db, _ = ldb.Select(0)
if *number <= 0 {
panic("invalid number")
return
}
if *clients <= 0 || *number < *clients {
panic("invalid client number")
return
}
loop = *number / *clients
if *round <= 0 {
*round = 1
}
for i := 0; i < *round; i++ {
benchSet()
benchGet()
benchGetSlice()
benchGet()
benchGetSlice()
println("")
}
}

View File

@ -3,7 +3,7 @@ package main
import (
"flag"
"fmt"
"github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"os"
)
@ -32,7 +32,7 @@ func main() {
addr = fmt.Sprintf("%s:%d", *host, *port)
}
c, err := ledis.ConnectWithSize(addr, 16*1024, 4096)
c, err := goredis.ConnectWithSize(addr, 16*1024, 4096)
if err != nil {
println(err.Error())
return

View File

@ -1,158 +0,0 @@
package main
import (
"bufio"
"bytes"
"flag"
"fmt"
"github.com/siddontang/go/arena"
"github.com/siddontang/ledisdb/config"
"github.com/siddontang/ledisdb/ledis"
"github.com/siddontang/ledisdb/server"
"net"
"os"
"runtime"
"time"
)
var KB = config.KB
var MB = config.MB
var GB = config.GB
var addr = flag.String("addr", ":6380", "listen addr")
var name = flag.String("db_name", "", "db name")
var ldb *ledis.Ledis
var db *ledis.DB
func setRocksDB(cfg *config.RocksDBConfig) {
cfg.BlockSize = 64 * KB
cfg.WriteBufferSize = 64 * MB
cfg.MaxWriteBufferNum = 2
cfg.MaxBytesForLevelBase = 512 * MB
cfg.TargetFileSizeBase = 64 * MB
cfg.BackgroundThreads = 4
cfg.HighPriorityBackgroundThreads = 1
cfg.MaxBackgroundCompactions = 3
cfg.MaxBackgroundFlushes = 1
cfg.CacheSize = 512 * MB
cfg.EnableStatistics = true
cfg.StatsDumpPeriodSec = 5
cfg.Level0FileNumCompactionTrigger = 8
cfg.MaxBytesForLevelMultiplier = 8
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
flag.Parse()
l, err := net.Listen("tcp", *addr)
println("listen", *addr)
if err != nil {
fmt.Println(err.Error())
return
}
if len(*name) > 0 {
cfg := config.NewConfigDefault()
cfg.DataDir = "./var/ledis_respbench"
cfg.DBName = *name
os.RemoveAll(cfg.DBPath)
defer os.RemoveAll(cfg.DBPath)
os.MkdirAll(cfg.DBPath, 0755)
cfg.LevelDB.BlockSize = 32 * KB
cfg.LevelDB.CacheSize = 512 * MB
cfg.LevelDB.WriteBufferSize = 64 * MB
cfg.LevelDB.MaxOpenFiles = 1000
setRocksDB(&cfg.RocksDB)
ldb, err = ledis.Open(cfg)
if err != nil {
println(err.Error())
return
}
db, _ = ldb.Select(0)
}
for {
c, err := l.Accept()
if err != nil {
println(err.Error())
continue
}
go run(c)
}
}
func run(c net.Conn) {
//buf := make([]byte, 10240)
ok := []byte("+OK\r\n")
data := []byte("$4096\r\n")
data = append(data, make([]byte, 4096)...)
data = append(data, "\r\n"...)
var rt time.Duration
var wt time.Duration
var st time.Duration
var gt time.Duration
rb := bufio.NewReaderSize(c, 10240)
wb := bufio.NewWriterSize(c, 10240)
a := arena.NewArena(10240)
for {
t1 := time.Now()
a.Reset()
req, err := server.ReadRequest(rb, a)
if err != nil {
break
}
t2 := time.Now()
rt += t2.Sub(t1)
cmd := string(bytes.ToUpper(req[0]))
switch cmd {
case "SET":
if db != nil {
db.Set(req[1], req[2])
st += time.Now().Sub(t2)
}
wb.Write(ok)
case "GET":
if db != nil {
d, _ := db.GetSlice(req[1])
gt += time.Now().Sub(t2)
if d == nil {
wb.Write(data)
} else {
wb.WriteString(fmt.Sprintf("$%d\r\n", d.Size()))
wb.Write(d.Data())
wb.WriteString("\r\n")
d.Free()
}
} else {
wb.Write(data)
}
default:
wb.WriteString(fmt.Sprintf("-Err %s Not Supported Now\r\n", req[0]))
}
wb.Flush()
t3 := time.Now()
wt += t3.Sub(t2)
}
fmt.Printf("rt:%s wt %s, gt:%s, st:%s\n", rt.String(), wt.String(), gt.String(), st.String())
}

View File

@ -604,25 +604,25 @@
},
"XSCAN": {
"arguments": "type cursor [MATCH match] [COUNT count]",
"arguments": "type cursor [MATCH match] [COUNT count] [ASC|DESC]",
"group": "Server",
"readonly": true
},
"XHSCAN": {
"arguments": "key cursor [MATCH match] [COUNT count]",
"arguments": "key cursor [MATCH match] [COUNT count] [ASC|DESC]",
"group": "Hash",
"readonly": true
},
"XSSCAN": {
"arguments": "key cursor [MATCH match] [COUNT count]",
"arguments": "key cursor [MATCH match] [COUNT count] [ASC|DESC]",
"group": "Set",
"readonly": true
},
"XZSCAN": {
"arguments": "key cursor [MATCH match] [COUNT count]",
"arguments": "key cursor [MATCH match] [COUNT count] [ASC|DESC]",
"group": "ZSet",
"readonly": true
},
@ -712,8 +712,26 @@
},
"ZKEYEXISTS": {
"arguments" : "ZSet",
"group" : "Hash",
"arguments" : "key",
"group" : "ZSet",
"readonly" : true
},
"XLSORT": {
"arguments" : "key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]",
"group" : "List",
"readonly" : false
},
"XSSORT": {
"arguments" : "key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]",
"group" : "Set",
"readonly" : false
},
"XZSORT": {
"arguments" : "key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]",
"group" : "ZSet",
"readonly" : false
}
}

View File

@ -125,10 +125,14 @@ Most of the Ledisdb's commands are the same as Redis's, you can see the redis co
- [ZDUMP key](#zdump-key)
- [ZKEYEXISTS key](#zkeyexists-key)
- [Scan](#scan)
- [XSCAN type cursor [MATCH match] [COUNT count]](#xscan-type-cursor-match-match-count-count)
- [XHSCAN key cursor [MATCH match] [COUNT count]](#xhscan-key-cursor-match-match-count-count)
- [XSSCAN key cursor [MATCH match] [COUNT count]](#xsscan-key-cursor-match-match-count-count)
- [XZSCAN key cursor [MATCH match] [COUNT count]](#xzscan-key-cursor-match-match-count-count)
- [XSCAN type cursor [MATCH match] [COUNT count] [ASC|DESC]](#xscan-type-cursor-match-match-count-count-asc|desc)
- [XHSCAN key cursor [MATCH match] [COUNT count] [ASC|DESC]](#xhscan-key-cursor-match-match-count-count-asc|desc)
- [XSSCAN key cursor [MATCH match] [COUNT count] [ASC|DESC]](#xsscan-key-cursor-match-match-count-count-asc|desc)
- [XZSCAN key cursor [MATCH match] [COUNT count] [ASC|DESC]](#xzscan-key-cursor-match-match-count-count-asc|desc)
- [Sort](#sort)
- [XLSORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]](#xlsort-key-by-pattern-limit-offset-count-get-pattern-get-pattern--asc|desc-alpha-store-destination)
- [XSSORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]](#xssort-key-by-pattern-limit-offset-count-get-pattern-get-pattern--asc|desc-alpha-store-destination)
- [XZSORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]](#xzsort-key-by-pattern-limit-offset-count-get-pattern-get-pattern--asc|desc-alpha-store-destination)
- [Replication](#replication)
- [SLAVEOF host port [RESTART] [READONLY]](#slaveof-host-port-restart-readonly)
- [FULLSYNC [NEW]](#fullsync-new)
@ -2371,7 +2375,7 @@ Check key exists for zset data, like [EXISTS key](#exists-key)
## Scan
### XSCAN type cursor [MATCH match] [COUNT count]
### XSCAN type cursor [MATCH match] [COUNT count] [ASC|DESC]
Iterate data type keys incrementally.
@ -2379,6 +2383,7 @@ Type is "KV", "LIST", "HASH", "SET" or "ZSET".
Cursor is the start for the current iteration.
Match is the regexp for checking matched key.
Count is the maximum retrieved elememts number, default is 10.
DESC for reverse iterator.
**Return value**
@ -2410,20 +2415,33 @@ ledis>xscan "KV" "c" count 1
2) []
```
### XHSCAN key cursor [MATCH match] [COUNT count]
### XHSCAN key cursor [MATCH match] [COUNT count] [ASC|DESC]
Same like [XSCAN type cursor [MATCH match] [COUNT count]](#xscan-type-cursor-match-match-count-count), but return array of elements
Same like XSCAN, but return array of elements.
contains two elements, a field and a value.
### XSSCAN key cursor [MATCH match] [COUNT count]
### XSSCAN key cursor [MATCH match] [COUNT count] [ASC|DESC]
Same like [XSCAN type cursor [MATCH match] [COUNT count]](#xscan-type-cursor-match-match-count-count)
Same like XSCAN.
### XZSCAN key cursor [MATCH match] [COUNT count]
### XZSCAN key cursor [MATCH match] [COUNT count] [ASC|DESC]
Same like [XSCAN type cursor [MATCH match] [COUNT count]](#xscan-type-cursor-match-match-count-count), but return array of elements
Same like XSCAN, but return array of elements.
contains two elements, a member and its associated score.
## Sort
### XLSORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]
Returns or stores the elements contained in the list at key.
### XSSORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]
Returns or stores the elements contained in the set at key.
### XZSORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]
Returns or stores the elements contained in the zset at key.
## Replication

View File

@ -2,7 +2,7 @@ package ledis
import (
"fmt"
"github.com/siddontang/ledisdb/ledis/rdb"
"github.com/siddontang/rdb"
)
/*
@ -43,7 +43,7 @@ func (db *DB) HDump(key []byte) ([]byte, error) {
return nil, err
}
o := make(rdb.HashMap, len(v))
o := make(rdb.Hash, len(v))
for i := 0; i < len(v); i++ {
o[i].Field = v[i].Field
o[i].Value = v[i].Value
@ -110,7 +110,7 @@ func (db *DB) Restore(key []byte, ttl int64, data []byte) error {
return err
}
}
case rdb.HashMap:
case rdb.Hash:
//first clear old key
if _, err = db.HClear(key); err != nil {
return err

View File

@ -61,44 +61,19 @@ func buildMatchRegexp(match string) (*regexp.Regexp, error) {
return r, nil
}
func (db *DB) scanGeneric(storeDataType byte, key []byte, count int,
inclusive bool, match string, reverse bool) ([][]byte, error) {
var minKey, maxKey []byte
r, err := buildMatchRegexp(match)
if err != nil {
return nil, err
}
func (db *DB) buildScanIterator(minKey []byte, maxKey []byte, inclusive bool, reverse bool) *store.RangeLimitIterator {
tp := store.RangeOpen
if !reverse {
if minKey, err = db.encodeScanMinKey(storeDataType, key); err != nil {
return nil, err
}
if maxKey, err = db.encodeScanMaxKey(storeDataType, nil); err != nil {
return nil, err
}
if inclusive {
tp = store.RangeROpen
}
} else {
if minKey, err = db.encodeScanMinKey(storeDataType, nil); err != nil {
return nil, err
}
if maxKey, err = db.encodeScanMaxKey(storeDataType, key); err != nil {
return nil, err
}
if inclusive {
tp = store.RangeLOpen
}
}
if count <= 0 {
count = defaultScanCount
}
var it *store.RangeLimitIterator
if !reverse {
it = db.bucket.RangeIterator(minKey, maxKey, tp)
@ -106,6 +81,53 @@ func (db *DB) scanGeneric(storeDataType byte, key []byte, count int,
it = db.bucket.RevRangeIterator(minKey, maxKey, tp)
}
return it
}
func (db *DB) buildScanKeyRange(storeDataType byte, key []byte, reverse bool) (minKey []byte, maxKey []byte, err error) {
if !reverse {
if minKey, err = db.encodeScanMinKey(storeDataType, key); err != nil {
return
}
if maxKey, err = db.encodeScanMaxKey(storeDataType, nil); err != nil {
return
}
} else {
if minKey, err = db.encodeScanMinKey(storeDataType, nil); err != nil {
return
}
if maxKey, err = db.encodeScanMaxKey(storeDataType, key); err != nil {
return
}
}
return
}
func checkScanCount(count int) int {
if count <= 0 {
count = defaultScanCount
}
return count
}
func (db *DB) scanGeneric(storeDataType byte, key []byte, count int,
inclusive bool, match string, reverse bool) ([][]byte, error) {
r, err := buildMatchRegexp(match)
if err != nil {
return nil, err
}
minKey, maxKey, err := db.buildScanKeyRange(storeDataType, key, reverse)
if err != nil {
return nil, err
}
count = checkScanCount(count)
it := db.buildScanIterator(minKey, maxKey, inclusive, reverse)
v := make([][]byte, 0, count)
for i := 0; it.Valid() && i < count; it.Next() {
@ -123,22 +145,11 @@ func (db *DB) scanGeneric(storeDataType byte, key []byte, count int,
}
func (db *DB) encodeScanMinKey(storeDataType byte, key []byte) ([]byte, error) {
if len(key) == 0 {
return db.encodeScanKey(storeDataType, nil)
} else {
if err := checkKeySize(key); err != nil {
return nil, err
}
return db.encodeScanKey(storeDataType, key)
}
return db.encodeScanKey(storeDataType, key)
}
func (db *DB) encodeScanMaxKey(storeDataType byte, key []byte) ([]byte, error) {
if len(key) > 0 {
if err := checkKeySize(key); err != nil {
return nil, err
}
return db.encodeScanKey(storeDataType, key)
}
@ -162,12 +173,11 @@ func (db *DB) encodeScanKey(storeDataType byte, key []byte) ([]byte, error) {
return db.zEncodeSizeKey(key), nil
case SSizeType:
return db.sEncodeSizeKey(key), nil
// case BitMetaType:
// return db.bEncodeMetaKey(key), nil
default:
return nil, errDataType
}
}
func (db *DB) decodeScanKey(storeDataType byte, ek []byte) ([]byte, error) {
if len(ek) < 2 || ek[0] != db.index || ek[1] != storeDataType {
return nil, errMetaKey
@ -177,33 +187,89 @@ func (db *DB) decodeScanKey(storeDataType byte, ek []byte) ([]byte, error) {
// for specail data scan
func (db *DB) buildDataScanIterator(start []byte, stop []byte, inclusive bool) *store.RangeLimitIterator {
tp := store.RangeROpen
if !inclusive {
tp = store.RangeOpen
func (db *DB) buildDataScanKeyRange(storeDataType byte, key []byte, cursor []byte, reverse bool) (minKey []byte, maxKey []byte, err error) {
if !reverse {
if minKey, err = db.encodeDataScanMinKey(storeDataType, key, cursor); err != nil {
return
}
if maxKey, err = db.encodeDataScanMaxKey(storeDataType, key, nil); err != nil {
return
}
} else {
if minKey, err = db.encodeDataScanMinKey(storeDataType, key, nil); err != nil {
return
}
if maxKey, err = db.encodeDataScanMaxKey(storeDataType, key, cursor); err != nil {
return
}
}
it := db.bucket.RangeIterator(start, stop, tp)
return it
return
}
func (db *DB) HScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([]FVPair, error) {
func (db *DB) encodeDataScanMinKey(storeDataType byte, key []byte, cursor []byte) ([]byte, error) {
return db.encodeDataScanKey(storeDataType, key, cursor)
}
func (db *DB) encodeDataScanMaxKey(storeDataType byte, key []byte, cursor []byte) ([]byte, error) {
if len(cursor) > 0 {
return db.encodeDataScanKey(storeDataType, key, cursor)
}
k, err := db.encodeDataScanKey(storeDataType, key, nil)
if err != nil {
return nil, err
}
// here, the last byte is the start seperator, set it to stop seperator
k[len(k)-1] = k[len(k)-1] + 1
return k, nil
}
func (db *DB) encodeDataScanKey(storeDataType byte, key []byte, cursor []byte) ([]byte, error) {
switch storeDataType {
case HashType:
return db.hEncodeHashKey(key, cursor), nil
case ZSetType:
return db.zEncodeSetKey(key, cursor), nil
case SetType:
return db.sEncodeSetKey(key, cursor), nil
default:
return nil, errDataType
}
}
func (db *DB) buildDataScanIterator(storeDataType byte, key []byte, cursor []byte, count int,
inclusive bool, reverse bool) (*store.RangeLimitIterator, error) {
if err := checkKeySize(key); err != nil {
return nil, err
}
start := db.hEncodeHashKey(key, cursor)
stop := db.hEncodeStopKey(key)
minKey, maxKey, err := db.buildDataScanKeyRange(storeDataType, key, cursor, reverse)
if err != nil {
return nil, err
}
v := make([]FVPair, 0, 16)
it := db.buildScanIterator(minKey, maxKey, inclusive, reverse)
return it, nil
}
func (db *DB) hScanGeneric(key []byte, cursor []byte, count int, inclusive bool, match string, reverse bool) ([]FVPair, error) {
count = checkScanCount(count)
r, err := buildMatchRegexp(match)
if err != nil {
return nil, err
}
it := db.buildDataScanIterator(start, stop, inclusive)
v := make([]FVPair, 0, count)
it, err := db.buildDataScanIterator(HashType, key, cursor, count, inclusive, reverse)
if err != nil {
return nil, err
}
defer it.Close()
for i := 0; it.Valid() && i < count; it.Next() {
@ -222,22 +288,29 @@ func (db *DB) HScan(key []byte, cursor []byte, count int, inclusive bool, match
return v, nil
}
func (db *DB) SScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([][]byte, error) {
if err := checkKeySize(key); err != nil {
return nil, err
}
func (db *DB) HScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([]FVPair, error) {
return db.hScanGeneric(key, cursor, count, inclusive, match, false)
}
start := db.sEncodeSetKey(key, cursor)
stop := db.sEncodeStopKey(key)
func (db *DB) HRevScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([]FVPair, error) {
return db.hScanGeneric(key, cursor, count, inclusive, match, true)
}
v := make([][]byte, 0, 16)
func (db *DB) sScanGeneric(key []byte, cursor []byte, count int, inclusive bool, match string, reverse bool) ([][]byte, error) {
count = checkScanCount(count)
r, err := buildMatchRegexp(match)
if err != nil {
return nil, err
}
it := db.buildDataScanIterator(start, stop, inclusive)
v := make([][]byte, 0, count)
it, err := db.buildDataScanIterator(SetType, key, cursor, count, inclusive, reverse)
if err != nil {
return nil, err
}
defer it.Close()
for i := 0; it.Valid() && i < count; it.Next() {
@ -256,22 +329,29 @@ func (db *DB) SScan(key []byte, cursor []byte, count int, inclusive bool, match
return v, nil
}
func (db *DB) ZScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([]ScorePair, error) {
if err := checkKeySize(key); err != nil {
return nil, err
}
func (db *DB) SScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([][]byte, error) {
return db.sScanGeneric(key, cursor, count, inclusive, match, false)
}
start := db.zEncodeSetKey(key, cursor)
stop := db.zEncodeStopSetKey(key)
func (db *DB) SRevScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([][]byte, error) {
return db.sScanGeneric(key, cursor, count, inclusive, match, true)
}
v := make([]ScorePair, 0, 16)
func (db *DB) zScanGeneric(key []byte, cursor []byte, count int, inclusive bool, match string, reverse bool) ([]ScorePair, error) {
count = checkScanCount(count)
r, err := buildMatchRegexp(match)
if err != nil {
return nil, err
}
it := db.buildDataScanIterator(start, stop, inclusive)
v := make([]ScorePair, 0, count)
it, err := db.buildDataScanIterator(ZSetType, key, cursor, count, inclusive, reverse)
if err != nil {
return nil, err
}
defer it.Close()
for i := 0; it.Valid() && i < count; it.Next() {
@ -294,3 +374,11 @@ func (db *DB) ZScan(key []byte, cursor []byte, count int, inclusive bool, match
return v, nil
}
func (db *DB) ZScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([]ScorePair, error) {
return db.zScanGeneric(key, cursor, count, inclusive, match, false)
}
func (db *DB) ZRevScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([]ScorePair, error) {
return db.zScanGeneric(key, cursor, count, inclusive, match, true)
}

View File

@ -323,6 +323,16 @@ func TestDBHScan(t *testing.T) {
} else if string(v[0].Field) != "222" {
t.Fatal(string(v[0].Field))
}
v, err = db.HRevScan(key, []byte("19"), 1, false, "")
if err != nil {
t.Fatal(err)
} else if len(v) != 1 {
t.Fatal("invalid count", len(v))
} else if string(v[0].Field) != "1234" {
t.Fatal(string(v[0].Field))
}
}
func TestDBSScan(t *testing.T) {
@ -346,6 +356,16 @@ func TestDBSScan(t *testing.T) {
} else if string(v[0]) != "222" {
t.Fatal(string(v[0]))
}
v, err = db.SRevScan(key, []byte("19"), 1, false, "")
if err != nil {
t.Fatal(err)
} else if len(v) != 1 {
t.Fatal("invalid count", len(v))
} else if string(v[0]) != "1234" {
t.Fatal(string(v[0]))
}
}
func TestDBZScan(t *testing.T) {
@ -369,4 +389,14 @@ func TestDBZScan(t *testing.T) {
} else if string(v[0].Member) != "222" {
t.Fatal(string(v[0].Member))
}
v, err = db.ZRevScan(key, []byte("19"), 1, false, "")
if err != nil {
t.Fatal(err)
} else if len(v) != 1 {
t.Fatal("invalid count", len(v))
} else if string(v[0].Member) != "1234" {
t.Fatal(string(v[0].Member))
}
}

View File

@ -3,6 +3,7 @@ package ledis
import (
"errors"
"fmt"
"github.com/siddontang/go/log"
"github.com/siddontang/ledisdb/store"
)
@ -26,6 +27,8 @@ func (db *DB) IsTransaction() bool {
// Begin a transaction, it will block all other write operations before calling Commit or Rollback.
// You must be very careful to prevent long-time transaction.
func (db *DB) Begin() (*Tx, error) {
log.Warn("Transaction support will be removed later, use your own risk!!!")
if db.IsTransaction() {
return nil, ErrNestTx
}

View File

@ -1,7 +1,7 @@
package server
import (
goledis "github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"github.com/siddontang/ledisdb/config"
"github.com/siddontang/ledisdb/ledis"
"net"
@ -44,8 +44,9 @@ type App struct {
rcm sync.Mutex
rcs map[*respClient]struct{}
migrateM sync.Mutex
migrateClients map[string]*goledis.Client
migrateM sync.Mutex
migrateClients map[string]*goredis.Client
migrateKeyLockers map[string]*migrateKeyLocker
}
func netType(s string) string {
@ -75,7 +76,8 @@ func NewApp(cfg *config.Config) (*App, error) {
app.rcs = make(map[*respClient]struct{})
app.migrateClients = make(map[string]*goledis.Client)
app.migrateClients = make(map[string]*goredis.Client)
app.newMigrateKeyLockers()
var err error

View File

@ -1,7 +1,7 @@
package server
import (
"github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"github.com/siddontang/ledisdb/config"
"os"
"sync"
@ -11,16 +11,14 @@ import (
var testAppOnce sync.Once
var testApp *App
var testLedisClient *ledis.Client
var testLedisClient *goredis.Client
func newTestLedisClient() {
cfg := new(ledis.Config)
cfg.Addr = "127.0.0.1:16380"
cfg.MaxIdleConns = 4
testLedisClient = ledis.NewClient(cfg)
testLedisClient = goredis.NewClient("127.0.0.1:16380", "")
testLedisClient.SetMaxIdleConns(4)
}
func getTestConn() *ledis.Conn {
func getTestConn() *goredis.PoolConn {
startTestApp()
conn, _ := testLedisClient.Get()
return conn

View File

@ -1,7 +1,7 @@
package server
// import (
// "github.com/siddontang/ledisdb/client/goledis"
// "github.com/siddontang/goredis"
// "testing"
// )
@ -19,19 +19,19 @@ package server
// key := []byte("test_cmd_bin_basic")
// // get / set
// if v, err := ledis.Int(c.Do("bgetbit", key, 1024)); err != nil {
// if v, err := goredis.Int(c.Do("bgetbit", key, 1024)); err != nil {
// t.Fatal(err)
// } else if v != 0 {
// t.Fatal(v)
// }
// if ori, err := ledis.Int(c.Do("bsetbit", key, 1024, 1)); err != nil {
// if ori, err := goredis.Int(c.Do("bsetbit", key, 1024, 1)); err != nil {
// t.Fatal(err)
// } else if ori != 0 {
// t.Fatal(ori)
// }
// if v, err := ledis.Int(c.Do("bgetbit", key, 1024)); err != nil {
// if v, err := goredis.Int(c.Do("bgetbit", key, 1024)); err != nil {
// t.Fatal(err)
// } else if v != 1 {
// t.Fatal(v)
@ -40,26 +40,26 @@ package server
// // fetch from revert pos
// c.Do("bsetbit", key, 1000, 1)
// if v, err := ledis.Int(c.Do("bgetbit", key, -1)); err != nil {
// if v, err := goredis.Int(c.Do("bgetbit", key, -1)); err != nil {
// t.Fatal(err)
// } else if v != 1 {
// t.Fatal(v)
// }
// if v, err := ledis.Int(c.Do("bgetbit", key, -25)); err != nil {
// if v, err := goredis.Int(c.Do("bgetbit", key, -25)); err != nil {
// t.Fatal(err)
// } else if v != 1 {
// t.Fatal(v)
// }
// // delete
// if drop, err := ledis.Int(c.Do("bdelete", key)); err != nil {
// if drop, err := goredis.Int(c.Do("bdelete", key)); err != nil {
// t.Fatal(err)
// } else if drop != 1 {
// t.Fatal(drop)
// }
// if drop, err := ledis.Int(c.Do("bdelete", key)); err != nil {
// if drop, err := goredis.Int(c.Do("bdelete", key)); err != nil {
// t.Fatal(err)
// } else if drop != 0 {
// t.Fatal(drop)
@ -72,7 +72,7 @@ package server
// key := []byte("test_cmd_bin_mset")
// if n, err := ledis.Int(
// if n, err := goredis.Int(
// c.Do("bmsetbit", key,
// 500, 0,
// 100, 1,
@ -90,14 +90,14 @@ package server
// fillPos := []int{100, 200, 300, 1000, 100000, 500000}
// for _, pos := range fillPos {
// v, err := ledis.Int(c.Do("bgetbit", key, pos))
// v, err := goredis.Int(c.Do("bgetbit", key, pos))
// if err != nil || v != 1 {
// t.Fatal(pos)
// }
// }
// // err
// if n, err := ledis.Int(
// if n, err := goredis.Int(
// c.Do("bmsetbit", key, 3, 0, 2, 1, 3, 0, 1, 1)); err == nil || n != 0 {
// t.Fatal(n) // duplication on pos
// }
@ -114,7 +114,7 @@ package server
// sum++
// }
// if n, err := ledis.Int(c.Do("bcount", key)); err != nil {
// if n, err := goredis.Int(c.Do("bcount", key)); err != nil {
// t.Fatal(err)
// } else if n != sum {
// t.Fatal(n)
@ -137,21 +137,21 @@ package server
// // todo ...
// // case - 'not' on inexisting key
// if blen, err := ledis.Int(
// if blen, err := goredis.Int(
// c.Do("bopt", "not", dstk, kmiss)); err != nil {
// t.Fatal(err)
// } else if blen != 0 {
// t.Fatal(blen)
// }
// if v, _ := ledis.String(c.Do("bget", dstk)); v != "" {
// if v, _ := goredis.String(c.Do("bget", dstk)); v != "" {
// t.Fatal(v)
// }
// // case - 'and', 'or', 'xor' with inexisting key
// opts := []string{"and", "or", "xor"}
// for _, op := range opts {
// if blen, err := ledis.Int(
// if blen, err := goredis.Int(
// c.Do("bopt", op, dstk, kmiss, k0)); err != nil {
// t.Fatal(err)
// } else if blen != 0 {
@ -160,62 +160,62 @@ package server
// }
// // case - 'and'
// if blen, err := ledis.Int(
// if blen, err := goredis.Int(
// c.Do("bopt", "and", dstk, k0, k1)); err != nil {
// t.Fatal(err)
// } else if blen != 101 {
// t.Fatal(blen)
// }
// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 100)); v != 1 {
// if v, _ := goredis.Int(c.Do("bgetbit", dstk, 100)); v != 1 {
// t.Fatal(v)
// }
// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 20)); v != 0 {
// if v, _ := goredis.Int(c.Do("bgetbit", dstk, 20)); v != 0 {
// t.Fatal(v)
// }
// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 40)); v != 0 {
// if v, _ := goredis.Int(c.Do("bgetbit", dstk, 40)); v != 0 {
// t.Fatal(v)
// }
// // case - 'or'
// if blen, err := ledis.Int(
// if blen, err := goredis.Int(
// c.Do("bopt", "or", dstk, k0, k1)); err != nil {
// t.Fatal(err)
// } else if blen != 101 {
// t.Fatal(blen)
// }
// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 100)); v != 1 {
// if v, _ := goredis.Int(c.Do("bgetbit", dstk, 100)); v != 1 {
// t.Fatal(v)
// }
// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 20)); v != 1 {
// if v, _ := goredis.Int(c.Do("bgetbit", dstk, 20)); v != 1 {
// t.Fatal(v)
// }
// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 40)); v != 1 {
// if v, _ := goredis.Int(c.Do("bgetbit", dstk, 40)); v != 1 {
// t.Fatal(v)
// }
// // case - 'xor'
// if blen, err := ledis.Int(
// if blen, err := goredis.Int(
// c.Do("bopt", "xor", dstk, k0, k1)); err != nil {
// t.Fatal(err)
// } else if blen != 101 {
// t.Fatal(blen)
// }
// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 100)); v != 0 {
// if v, _ := goredis.Int(c.Do("bgetbit", dstk, 100)); v != 0 {
// t.Fatal(v)
// }
// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 20)); v != 1 {
// if v, _ := goredis.Int(c.Do("bgetbit", dstk, 20)); v != 1 {
// t.Fatal(v)
// }
// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 40)); v != 1 {
// if v, _ := goredis.Int(c.Do("bgetbit", dstk, 40)); v != 1 {
// t.Fatal(v)
// }

View File

@ -2,7 +2,7 @@ package server
import (
"fmt"
"github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"strconv"
"testing"
)
@ -12,54 +12,54 @@ func TestHash(t *testing.T) {
defer c.Close()
key := []byte("a")
if n, err := ledis.Int(c.Do("hkeyexists", key)); err != nil {
if n, err := goredis.Int(c.Do("hkeyexists", key)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("hset", key, 1, 0)); err != nil {
if n, err := goredis.Int(c.Do("hset", key, 1, 0)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("hkeyexists", key)); err != nil {
if n, err := goredis.Int(c.Do("hkeyexists", key)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("hexists", key, 1)); err != nil {
if n, err := goredis.Int(c.Do("hexists", key, 1)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("hexists", key, -1)); err != nil {
if n, err := goredis.Int(c.Do("hexists", key, -1)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("hget", key, 1)); err != nil {
if n, err := goredis.Int(c.Do("hget", key, 1)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("hset", key, 1, 1)); err != nil {
if n, err := goredis.Int(c.Do("hset", key, 1, 1)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("hget", key, 1)); err != nil {
if n, err := goredis.Int(c.Do("hget", key, 1)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("hlen", key)); err != nil {
if n, err := goredis.Int(c.Do("hlen", key)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
@ -95,19 +95,19 @@ func TestHashM(t *testing.T) {
defer c.Close()
key := []byte("b")
if ok, err := ledis.String(c.Do("hmset", key, 1, 1, 2, 2, 3, 3)); err != nil {
if ok, err := goredis.String(c.Do("hmset", key, 1, 1, 2, 2, 3, 3)); err != nil {
t.Fatal(err)
} else if ok != OK {
t.Fatal(ok)
}
if n, err := ledis.Int(c.Do("hlen", key)); err != nil {
if n, err := goredis.Int(c.Do("hlen", key)); err != nil {
t.Fatal(err)
} else if n != 3 {
t.Fatal(n)
}
if v, err := ledis.MultiBulk(c.Do("hmget", key, 1, 2, 3, 4)); err != nil {
if v, err := goredis.MultiBulk(c.Do("hmget", key, 1, 2, 3, 4)); err != nil {
t.Fatal(err)
} else {
if err := testHashArray(v, 1, 2, 3, 0); err != nil {
@ -115,19 +115,19 @@ func TestHashM(t *testing.T) {
}
}
if n, err := ledis.Int(c.Do("hdel", key, 1, 2, 3, 4)); err != nil {
if n, err := goredis.Int(c.Do("hdel", key, 1, 2, 3, 4)); err != nil {
t.Fatal(err)
} else if n != 3 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("hlen", key)); err != nil {
if n, err := goredis.Int(c.Do("hlen", key)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
}
if v, err := ledis.MultiBulk(c.Do("hmget", key, 1, 2, 3, 4)); err != nil {
if v, err := goredis.MultiBulk(c.Do("hmget", key, 1, 2, 3, 4)); err != nil {
t.Fatal(err)
} else {
if err := testHashArray(v, 0, 0, 0, 0); err != nil {
@ -135,7 +135,7 @@ func TestHashM(t *testing.T) {
}
}
if n, err := ledis.Int(c.Do("hlen", key)); err != nil {
if n, err := goredis.Int(c.Do("hlen", key)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
@ -147,31 +147,31 @@ func TestHashIncr(t *testing.T) {
defer c.Close()
key := []byte("c")
if n, err := ledis.Int(c.Do("hincrby", key, 1, 1)); err != nil {
if n, err := goredis.Int(c.Do("hincrby", key, 1, 1)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(err)
}
if n, err := ledis.Int(c.Do("hlen", key)); err != nil {
if n, err := goredis.Int(c.Do("hlen", key)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("hincrby", key, 1, 10)); err != nil {
if n, err := goredis.Int(c.Do("hincrby", key, 1, 10)); err != nil {
t.Fatal(err)
} else if n != 11 {
t.Fatal(err)
}
if n, err := ledis.Int(c.Do("hlen", key)); err != nil {
if n, err := goredis.Int(c.Do("hlen", key)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("hincrby", key, 1, -11)); err != nil {
if n, err := goredis.Int(c.Do("hincrby", key, 1, -11)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(err)
@ -185,13 +185,13 @@ func TestHashGetAll(t *testing.T) {
key := []byte("d")
if ok, err := ledis.String(c.Do("hmset", key, 1, 1, 2, 2, 3, 3)); err != nil {
if ok, err := goredis.String(c.Do("hmset", key, 1, 1, 2, 2, 3, 3)); err != nil {
t.Fatal(err)
} else if ok != OK {
t.Fatal(ok)
}
if v, err := ledis.MultiBulk(c.Do("hgetall", key)); err != nil {
if v, err := goredis.MultiBulk(c.Do("hgetall", key)); err != nil {
t.Fatal(err)
} else {
if err := testHashArray(v, 1, 1, 2, 2, 3, 3); err != nil {
@ -199,7 +199,7 @@ func TestHashGetAll(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("hkeys", key)); err != nil {
if v, err := goredis.MultiBulk(c.Do("hkeys", key)); err != nil {
t.Fatal(err)
} else {
if err := testHashArray(v, 1, 2, 3); err != nil {
@ -207,7 +207,7 @@ func TestHashGetAll(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("hvals", key)); err != nil {
if v, err := goredis.MultiBulk(c.Do("hvals", key)); err != nil {
t.Fatal(err)
} else {
if err := testHashArray(v, 1, 2, 3); err != nil {
@ -215,13 +215,13 @@ func TestHashGetAll(t *testing.T) {
}
}
if n, err := ledis.Int(c.Do("hclear", key)); err != nil {
if n, err := goredis.Int(c.Do("hclear", key)); err != nil {
t.Fatal(err)
} else if n != 3 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("hlen", key)); err != nil {
if n, err := goredis.Int(c.Do("hlen", key)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)

View File

@ -1,7 +1,7 @@
package server
import (
"github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"testing"
)
@ -9,121 +9,121 @@ func TestKV(t *testing.T) {
c := getTestConn()
defer c.Close()
if ok, err := ledis.String(c.Do("set", "a", "1234")); err != nil {
if ok, err := goredis.String(c.Do("set", "a", "1234")); err != nil {
t.Fatal(err)
} else if ok != OK {
t.Fatal(ok)
}
if n, err := ledis.Int(c.Do("setnx", "a", "123")); err != nil {
if n, err := goredis.Int(c.Do("setnx", "a", "123")); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("setnx", "b", "123")); err != nil {
if n, err := goredis.Int(c.Do("setnx", "b", "123")); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if ok, err := ledis.String(c.Do("setex", "xx", 10, "hello world")); err != nil {
if ok, err := goredis.String(c.Do("setex", "xx", 10, "hello world")); err != nil {
t.Fatal(err)
} else if ok != OK {
t.Fatal(ok)
}
if v, err := ledis.String(c.Do("get", "a")); err != nil {
if v, err := goredis.String(c.Do("get", "a")); err != nil {
t.Fatal(err)
} else if v != "1234" {
t.Fatal(v)
}
if v, err := ledis.String(c.Do("getset", "a", "123")); err != nil {
if v, err := goredis.String(c.Do("getset", "a", "123")); err != nil {
t.Fatal(err)
} else if v != "1234" {
t.Fatal(v)
}
if v, err := ledis.String(c.Do("get", "a")); err != nil {
if v, err := goredis.String(c.Do("get", "a")); err != nil {
t.Fatal(err)
} else if v != "123" {
t.Fatal(v)
}
if n, err := ledis.Int(c.Do("exists", "a")); err != nil {
if n, err := goredis.Int(c.Do("exists", "a")); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("exists", "empty_key_test")); err != nil {
if n, err := goredis.Int(c.Do("exists", "empty_key_test")); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
}
if _, err := ledis.Int(c.Do("del", "a", "b")); err != nil {
if _, err := goredis.Int(c.Do("del", "a", "b")); err != nil {
t.Fatal(err)
}
if n, err := ledis.Int(c.Do("exists", "a")); err != nil {
if n, err := goredis.Int(c.Do("exists", "a")); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("exists", "b")); err != nil {
if n, err := goredis.Int(c.Do("exists", "b")); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
}
rangeKey := "range_key"
if n, err := ledis.Int(c.Do("append", rangeKey, "Hello ")); err != nil {
if n, err := goredis.Int(c.Do("append", rangeKey, "Hello ")); err != nil {
t.Fatal(err)
} else if n != 6 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("setrange", rangeKey, 6, "Redis")); err != nil {
if n, err := goredis.Int(c.Do("setrange", rangeKey, 6, "Redis")); err != nil {
t.Fatal(err)
} else if n != 11 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("strlen", rangeKey)); err != nil {
if n, err := goredis.Int(c.Do("strlen", rangeKey)); err != nil {
t.Fatal(err)
} else if n != 11 {
t.Fatal(n)
}
if v, err := ledis.String(c.Do("getrange", rangeKey, 0, -1)); err != nil {
if v, err := goredis.String(c.Do("getrange", rangeKey, 0, -1)); err != nil {
t.Fatal(err)
} else if v != "Hello Redis" {
t.Fatal(v)
}
bitKey := "bit_key"
if n, err := ledis.Int(c.Do("setbit", bitKey, 7, 1)); err != nil {
if n, err := goredis.Int(c.Do("setbit", bitKey, 7, 1)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("getbit", bitKey, 7)); err != nil {
if n, err := goredis.Int(c.Do("getbit", bitKey, 7)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("bitcount", bitKey)); err != nil {
if n, err := goredis.Int(c.Do("bitcount", bitKey)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("bitpos", bitKey, 1)); err != nil {
if n, err := goredis.Int(c.Do("bitpos", bitKey, 1)); err != nil {
t.Fatal(err)
} else if n != 7 {
t.Fatal(n)
@ -132,13 +132,13 @@ func TestKV(t *testing.T) {
c.Do("set", "key1", "foobar")
c.Do("set", "key2", "abcdef")
if n, err := ledis.Int(c.Do("bitop", "and", "bit_dest_key", "key1", "key2")); err != nil {
if n, err := goredis.Int(c.Do("bitop", "and", "bit_dest_key", "key1", "key2")); err != nil {
t.Fatal(err)
} else if n != 6 {
t.Fatal(n)
}
if v, err := ledis.String(c.Do("get", "bit_dest_key")); err != nil {
if v, err := goredis.String(c.Do("get", "bit_dest_key")); err != nil {
t.Fatal(err)
} else if v != "`bc`ab" {
t.Fatal(v)
@ -150,13 +150,13 @@ func TestKVM(t *testing.T) {
c := getTestConn()
defer c.Close()
if ok, err := ledis.String(c.Do("mset", "a", "1", "b", "2")); err != nil {
if ok, err := goredis.String(c.Do("mset", "a", "1", "b", "2")); err != nil {
t.Fatal(err)
} else if ok != OK {
t.Fatal(ok)
}
if v, err := ledis.MultiBulk(c.Do("mget", "a", "b", "c")); err != nil {
if v, err := goredis.MultiBulk(c.Do("mget", "a", "b", "c")); err != nil {
t.Fatal(err)
} else if len(v) != 3 {
t.Fatal(len(v))
@ -179,31 +179,31 @@ func TestKVIncrDecr(t *testing.T) {
c := getTestConn()
defer c.Close()
if n, err := ledis.Int64(c.Do("incr", "n")); err != nil {
if n, err := goredis.Int64(c.Do("incr", "n")); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int64(c.Do("incr", "n")); err != nil {
if n, err := goredis.Int64(c.Do("incr", "n")); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int64(c.Do("decr", "n")); err != nil {
if n, err := goredis.Int64(c.Do("decr", "n")); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int64(c.Do("incrby", "n", 10)); err != nil {
if n, err := goredis.Int64(c.Do("incrby", "n", 10)); err != nil {
t.Fatal(err)
} else if n != 11 {
t.Fatal(n)
}
if n, err := ledis.Int64(c.Do("decrby", "n", 10)); err != nil {
if n, err := goredis.Int64(c.Do("decrby", "n", 10)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)

View File

@ -2,7 +2,7 @@ package server
import (
"fmt"
"github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"strconv"
"testing"
)
@ -11,10 +11,10 @@ func testListIndex(key []byte, index int64, v int) error {
c := getTestConn()
defer c.Close()
n, err := ledis.Int(c.Do("lindex", key, index))
if err == ledis.ErrNil && v != 0 {
n, err := goredis.Int(c.Do("lindex", key, index))
if err == goredis.ErrNil && v != 0 {
return fmt.Errorf("must nil")
} else if err != nil && err != ledis.ErrNil {
} else if err != nil && err != goredis.ErrNil {
return err
} else if n != v {
return fmt.Errorf("index err number %d != %d", n, v)
@ -27,7 +27,7 @@ func testListRange(key []byte, start int64, stop int64, checkValues ...int) erro
c := getTestConn()
defer c.Close()
vs, err := ledis.MultiBulk(c.Do("lrange", key, start, stop))
vs, err := goredis.MultiBulk(c.Do("lrange", key, start, stop))
if err != nil {
return err
}
@ -58,37 +58,37 @@ func TestList(t *testing.T) {
defer c.Close()
key := []byte("a")
if n, err := ledis.Int(c.Do("lkeyexists", key)); err != nil {
if n, err := goredis.Int(c.Do("lkeyexists", key)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("lpush", key, 1)); err != nil {
if n, err := goredis.Int(c.Do("lpush", key, 1)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("lkeyexists", key)); err != nil {
if n, err := goredis.Int(c.Do("lkeyexists", key)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(1)
}
if n, err := ledis.Int(c.Do("rpush", key, 2)); err != nil {
if n, err := goredis.Int(c.Do("rpush", key, 2)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("rpush", key, 3)); err != nil {
if n, err := goredis.Int(c.Do("rpush", key, 3)); err != nil {
t.Fatal(err)
} else if n != 3 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("llen", key)); err != nil {
if n, err := goredis.Int(c.Do("llen", key)); err != nil {
t.Fatal(err)
} else if n != 3 {
t.Fatal(n)
@ -212,7 +212,7 @@ func TestListMPush(t *testing.T) {
defer c.Close()
key := []byte("b")
if n, err := ledis.Int(c.Do("rpush", key, 1, 2, 3)); err != nil {
if n, err := goredis.Int(c.Do("rpush", key, 1, 2, 3)); err != nil {
t.Fatal(err)
} else if n != 3 {
t.Fatal(n)
@ -222,7 +222,7 @@ func TestListMPush(t *testing.T) {
t.Fatal(err)
}
if n, err := ledis.Int(c.Do("lpush", key, 1, 2, 3)); err != nil {
if n, err := goredis.Int(c.Do("lpush", key, 1, 2, 3)); err != nil {
t.Fatal(err)
} else if n != 6 {
t.Fatal(n)
@ -238,25 +238,25 @@ func TestPop(t *testing.T) {
defer c.Close()
key := []byte("c")
if n, err := ledis.Int(c.Do("rpush", key, 1, 2, 3, 4, 5, 6)); err != nil {
if n, err := goredis.Int(c.Do("rpush", key, 1, 2, 3, 4, 5, 6)); err != nil {
t.Fatal(err)
} else if n != 6 {
t.Fatal(n)
}
if v, err := ledis.Int(c.Do("lpop", key)); err != nil {
if v, err := goredis.Int(c.Do("lpop", key)); err != nil {
t.Fatal(err)
} else if v != 1 {
t.Fatal(v)
}
if v, err := ledis.Int(c.Do("rpop", key)); err != nil {
if v, err := goredis.Int(c.Do("rpop", key)); err != nil {
t.Fatal(err)
} else if v != 6 {
t.Fatal(v)
}
if n, err := ledis.Int(c.Do("lpush", key, 1)); err != nil {
if n, err := goredis.Int(c.Do("lpush", key, 1)); err != nil {
t.Fatal(err)
} else if n != 5 {
t.Fatal(n)
@ -267,14 +267,14 @@ func TestPop(t *testing.T) {
}
for i := 1; i <= 5; i++ {
if v, err := ledis.Int(c.Do("lpop", key)); err != nil {
if v, err := goredis.Int(c.Do("lpop", key)); err != nil {
t.Fatal(err)
} else if v != i {
t.Fatal(v)
}
}
if n, err := ledis.Int(c.Do("llen", key)); err != nil {
if n, err := goredis.Int(c.Do("llen", key)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
@ -282,13 +282,13 @@ func TestPop(t *testing.T) {
c.Do("rpush", key, 1, 2, 3, 4, 5)
if n, err := ledis.Int(c.Do("lclear", key)); err != nil {
if n, err := goredis.Int(c.Do("lclear", key)); err != nil {
t.Fatal(err)
} else if n != 5 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("llen", key)); err != nil {
if n, err := goredis.Int(c.Do("llen", key)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)

View File

@ -1,11 +1,16 @@
package server
import (
"errors"
"fmt"
goledis "github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/ledisdb/ledis"
"strings"
"sync"
"time"
"github.com/siddontang/go/hack"
"github.com/siddontang/go/log"
"github.com/siddontang/goredis"
"github.com/siddontang/ledisdb/ledis"
)
func dumpCommand(c *client) error {
@ -106,19 +111,42 @@ func restoreCommand(c *client) error {
return nil
}
// maybe only used in xcodis for redis data port
func xrestoreCommand(c *client) error {
args := c.args
if len(args) != 4 {
return ErrCmdParams
}
// tp := strings.ToUpper(string(args[2]))
key := args[1]
ttl, err := ledis.StrInt64(args[2], nil)
if err != nil {
return err
}
data := args[3]
if err = c.db.Restore(key, ttl, data); err != nil {
return err
} else {
c.resp.writeStatus(OK)
}
return nil
}
func xdump(db *ledis.DB, tp string, key []byte) ([]byte, error) {
var err error
var data []byte
switch strings.ToUpper(tp) {
case "KV":
case KVName:
data, err = db.Dump(key)
case "HASH":
case HashName:
data, err = db.HDump(key)
case "LIST":
case ListName:
data, err = db.LDump(key)
case "SET":
case SetName:
data, err = db.SDump(key)
case "ZSET":
case ZSetName:
data, err = db.ZDump(key)
default:
err = fmt.Errorf("invalid key type %s", tp)
@ -129,15 +157,15 @@ func xdump(db *ledis.DB, tp string, key []byte) ([]byte, error) {
func xdel(db *ledis.DB, tp string, key []byte) error {
var err error
switch strings.ToUpper(tp) {
case "KV":
case KVName:
_, err = db.Del(key)
case "HASH":
case HashName:
_, err = db.HClear(key)
case "LIST":
case ListName:
_, err = db.LClear(key)
case "SET":
case SetName:
_, err = db.SClear(key)
case "ZSET":
case ZSetName:
_, err = db.ZClear(key)
default:
err = fmt.Errorf("invalid key type %s", tp)
@ -147,15 +175,15 @@ func xdel(db *ledis.DB, tp string, key []byte) error {
func xttl(db *ledis.DB, tp string, key []byte) (int64, error) {
switch strings.ToUpper(tp) {
case "KV":
case KVName:
return db.TTL(key)
case "HASH":
case HashName:
return db.HTTL(key)
case "LIST":
case ListName:
return db.LTTL(key)
case "SET":
case SetName:
return db.STTL(key)
case "ZSET":
case ZSetName:
return db.ZTTL(key)
default:
return 0, fmt.Errorf("invalid key type %s", tp)
@ -164,15 +192,15 @@ func xttl(db *ledis.DB, tp string, key []byte) (int64, error) {
func xscan(db *ledis.DB, tp string, count int) ([][]byte, error) {
switch strings.ToUpper(tp) {
case "KV":
case KVName:
return db.Scan(KV, nil, count, false, "")
case "HASH":
case HashName:
return db.Scan(HASH, nil, count, false, "")
case "LIST":
case ListName:
return db.Scan(LIST, nil, count, false, "")
case "SET":
case SetName:
return db.Scan(SET, nil, count, false, "")
case "ZSET":
case ZSetName:
return db.Scan(ZSET, nil, count, false, "")
default:
return nil, fmt.Errorf("invalid key type %s", tp)
@ -185,7 +213,7 @@ func xdumpCommand(c *client) error {
return ErrCmdParams
}
tp := string(args[0])
tp := strings.ToUpper(string(args[0]))
key := args[1]
if data, err := xdump(c.db, tp, key); err != nil {
@ -196,14 +224,13 @@ func xdumpCommand(c *client) error {
return nil
}
func (app *App) getMigrateClient(addr string) *goledis.Client {
func (app *App) getMigrateClient(addr string) *goredis.Client {
app.migrateM.Lock()
mc, ok := app.migrateClients[addr]
if !ok {
mc = goledis.NewClient(&goledis.Config{addr, 4, 0, 0})
mc = goredis.NewClient(addr, "")
app.migrateClients[addr] = mc
}
app.migrateM.Unlock()
@ -211,6 +238,68 @@ func (app *App) getMigrateClient(addr string) *goledis.Client {
return mc
}
type migrateKeyLocker struct {
m sync.Mutex
locks map[string]struct{}
}
func (m *migrateKeyLocker) Lock(key []byte) bool {
m.m.Lock()
defer m.m.Unlock()
k := hack.String(key)
_, ok := m.locks[k]
if ok {
return false
}
m.locks[k] = struct{}{}
return true
}
func (m *migrateKeyLocker) Unlock(key []byte) {
m.m.Lock()
defer m.m.Unlock()
delete(m.locks, hack.String(key))
}
func newMigrateKeyLocker() *migrateKeyLocker {
m := new(migrateKeyLocker)
m.locks = make(map[string]struct{})
return m
}
func (a *App) newMigrateKeyLockers() {
a.migrateKeyLockers = make(map[string]*migrateKeyLocker)
a.migrateKeyLockers[KVName] = newMigrateKeyLocker()
a.migrateKeyLockers[HashName] = newMigrateKeyLocker()
a.migrateKeyLockers[ListName] = newMigrateKeyLocker()
a.migrateKeyLockers[SetName] = newMigrateKeyLocker()
a.migrateKeyLockers[ZSetName] = newMigrateKeyLocker()
}
func (a *App) migrateKeyLock(tp string, key []byte) bool {
l, ok := a.migrateKeyLockers[strings.ToUpper(tp)]
if !ok {
return false
}
return l.Lock(key)
}
func (a *App) migrateKeyUnlock(tp string, key []byte) {
l, ok := a.migrateKeyLockers[strings.ToUpper(tp)]
if !ok {
return
}
l.Unlock(key)
}
//XMIGRATEDB host port tp count db timeout
//select count tp type keys and migrate
//will block any other write operations
@ -227,7 +316,7 @@ func xmigratedbCommand(c *client) error {
return fmt.Errorf("migrate in same server is not allowed")
}
tp := string(args[2])
tp := strings.ToUpper(string(args[2]))
count, err := ledis.StrInt64(args[3], nil)
if err != nil {
@ -236,11 +325,9 @@ func xmigratedbCommand(c *client) error {
count = 10
}
db, err := ledis.StrUint64(args[4], nil)
db, err := parseMigrateDB(c, args[4])
if err != nil {
return err
} else if db >= uint64(c.app.cfg.Databases) {
return fmt.Errorf("invalid db index %d, must < %d", db, c.app.cfg.Databases)
}
timeout, err := ledis.StrInt64(args[5], nil)
@ -250,13 +337,7 @@ func xmigratedbCommand(c *client) error {
return fmt.Errorf("invalid timeout %d", timeout)
}
m, err := c.db.Multi()
if err != nil {
return err
}
defer m.Close()
keys, err := xscan(m.DB, tp, int(count))
keys, err := xscan(c.db, tp, int(count))
if err != nil {
return err
} else if len(keys) == 0 {
@ -264,49 +345,41 @@ func xmigratedbCommand(c *client) error {
return nil
}
mc := c.app.getMigrateClient(addr)
conn, err := mc.Get()
conn, err := getMigrateDBConn(c, addr, db)
if err != nil {
return err
}
defer conn.Close()
//timeout is milliseconds
t := time.Duration(timeout) * time.Millisecond
if _, err = conn.Do("select", db); err != nil {
return err
}
migrateNum := int64(0)
for _, key := range keys {
data, err := xdump(m.DB, tp, key)
err = migrateKey(c, conn, tp, key, timeout)
if err != nil {
return err
}
ttl, err := xttl(m.DB, tp, key)
if err != nil {
return err
}
conn.SetReadDeadline(time.Now().Add(t))
//ttl is second, but restore need millisecond
if _, err = conn.Do("restore", key, ttl*1e3, data); err != nil {
return err
}
if err = xdel(m.DB, tp, key); err != nil {
return err
if err == errNoKey || err == errKeyInMigrating {
continue
} else {
return err
}
}
migrateNum++
}
c.resp.writeInteger(int64(len(keys)))
c.resp.writeInteger(migrateNum)
return nil
}
func parseMigrateDB(c *client, arg []byte) (uint64, error) {
db, err := ledis.StrUint64(arg, nil)
if err != nil {
return 0, err
} else if db >= uint64(c.app.cfg.Databases) {
return 0, fmt.Errorf("invalid db index %d, must < %d", db, c.app.cfg.Databases)
}
return db, nil
}
//XMIGRATE host port type key destination-db timeout
//will block any other write operations
//maybe only for xcodis
@ -323,13 +396,11 @@ func xmigrateCommand(c *client) error {
return fmt.Errorf("migrate in same server is not allowed")
}
tp := string(args[2])
tp := strings.ToUpper(string(args[2]))
key := args[3]
db, err := ledis.StrUint64(args[4], nil)
db, err := parseMigrateDB(c, args[4])
if err != nil {
return err
} else if db >= uint64(c.app.cfg.Databases) {
return fmt.Errorf("invalid db index %d, must < %d", db, c.app.cfg.Databases)
}
timeout, err := ledis.StrInt64(args[5], nil)
@ -339,28 +410,80 @@ func xmigrateCommand(c *client) error {
return fmt.Errorf("invalid timeout %d", timeout)
}
m, err := c.db.Multi()
conn, err := getMigrateDBConn(c, addr, db)
if err != nil {
return err
}
defer m.Close()
defer conn.Close()
data, err := xdump(m.DB, tp, key)
if err != nil {
return err
} else if data == nil {
c.resp.writeStatus(NOKEY)
return nil
// if key is in migrating, we will wait 500ms and retry again
for i := 0; i < 10; i++ {
if tp == "ALL" {
// if tp is ALL, we will migrate the key in all types
// this feature is useful for xcodis RESTORE or other commands that we don't know the data type exactly
err = migrateAllTypeKeys(c, conn, key, timeout)
} else {
err = migrateKey(c, conn, tp, key, timeout)
}
if err != errKeyInMigrating {
break
} else {
log.Infof("%s key %s is in migrating, wait 500ms and retry", tp, key)
time.Sleep(500 * time.Millisecond)
}
}
ttl, err := xttl(m.DB, tp, key)
if err != nil {
return err
if err == errNoKey {
c.resp.writeStatus(NOKEY)
return nil
} else {
return err
}
}
c.resp.writeStatus(OK)
return nil
}
func getMigrateDBConn(c *client, addr string, db uint64) (*goredis.PoolConn, error) {
mc := c.app.getMigrateClient(addr)
conn, err := mc.Get()
if err != nil {
return nil, err
}
if _, err = conn.Do("select", db); err != nil {
conn.Close()
return nil, err
}
return conn, nil
}
var (
errNoKey = errors.New("migrate key is not exists")
errKeyInMigrating = errors.New("key is in migrating yet")
)
func migrateKey(c *client, conn *goredis.PoolConn, tp string, key []byte, timeout int64) error {
if !c.app.migrateKeyLock(tp, key) {
// other may also migrate this key, skip it
return errKeyInMigrating
}
defer c.app.migrateKeyUnlock(tp, key)
data, err := xdump(c.db, tp, key)
if err != nil {
return err
} else if data == nil {
return errNoKey
}
ttl, err := xttl(c.db, tp, key)
if err != nil {
return err
}
@ -368,10 +491,6 @@ func xmigrateCommand(c *client) error {
//timeout is milliseconds
t := time.Duration(timeout) * time.Millisecond
if _, err = conn.Do("select", db); err != nil {
return err
}
conn.SetReadDeadline(time.Now().Add(t))
//ttl is second, but restore need millisecond
@ -379,11 +498,25 @@ func xmigrateCommand(c *client) error {
return err
}
if err = xdel(m.DB, tp, key); err != nil {
if err = xdel(c.db, tp, key); err != nil {
return err
}
c.resp.writeStatus(OK)
return nil
}
func migrateAllTypeKeys(c *client, conn *goredis.PoolConn, key []byte, timeout int64) error {
for _, tp := range TypeNames {
err := migrateKey(c, conn, tp, key, timeout)
if err != nil {
if err == errNoKey {
continue
} else {
return err
}
}
}
return nil
}
@ -394,6 +527,7 @@ func init() {
register("sdump", sdumpCommand)
register("zdump", zdumpCommand)
register("restore", restoreCommand)
register("xrestore", xrestoreCommand)
register("xdump", xdumpCommand)
register("xmigrate", xmigrateCommand)
register("xmigratedb", xmigratedbCommand)

View File

@ -2,7 +2,7 @@ package server
import (
"fmt"
"github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"github.com/siddontang/ledisdb/config"
"os"
"testing"
@ -42,8 +42,8 @@ func TestDumpRestore(t *testing.T) {
testDumpRestore(c, "zdump", "mtest_za", t)
}
func testDumpRestore(c *ledis.Conn, dump string, key string, t *testing.T) {
if data, err := ledis.Bytes(c.Do(dump, key)); err != nil {
func testDumpRestore(c *goredis.PoolConn, dump string, key string, t *testing.T) {
if data, err := goredis.Bytes(c.Do(dump, key)); err != nil {
t.Fatal(err)
} else if _, err := c.Do("restore", key, 0, data); err != nil {
t.Fatal(err)
@ -80,10 +80,10 @@ func TestMigrate(t *testing.T) {
time.Sleep(1 * time.Second)
c1, _ := ledis.Connect(s1Cfg.Addr)
c1, _ := goredis.Connect(s1Cfg.Addr)
defer c1.Close()
c2, _ := ledis.Connect(s2Cfg.Addr)
c2, _ := goredis.Connect(s2Cfg.Addr)
defer c2.Close()
if _, err = c1.Do("set", "a", "1"); err != nil {
@ -95,34 +95,49 @@ func TestMigrate(t *testing.T) {
t.Fatal(err)
}
if s, err := ledis.String(c2.Do("get", "a")); err != nil {
if s, err := goredis.String(c2.Do("get", "a")); err != nil {
t.Fatal(err)
} else if s != "1" {
t.Fatal(s, "must 1")
}
if s, err := ledis.String(c1.Do("get", "a")); err != nil && err != ledis.ErrNil {
if s, err := goredis.String(c1.Do("get", "a")); err != nil && err != goredis.ErrNil {
t.Fatal(err)
} else if s != "" {
t.Fatal(s, "must empty")
}
if num, err := ledis.Int(c2.Do("xmigratedb", "127.0.0.1", 11185, "KV", 10, 0, timeout)); err != nil {
if num, err := goredis.Int(c2.Do("xmigratedb", "127.0.0.1", 11185, "KV", 10, 0, timeout)); err != nil {
t.Fatal(err)
} else if num != 1 {
t.Fatal(num, "must number 1")
}
if s, err := ledis.String(c1.Do("get", "a")); err != nil {
if s, err := goredis.String(c1.Do("get", "a")); err != nil {
t.Fatal(err)
} else if s != "1" {
t.Fatal(s, "must 1")
}
if s, err := ledis.String(c2.Do("get", "a")); err != nil && err != ledis.ErrNil {
if s, err := goredis.String(c2.Do("get", "a")); err != nil && err != goredis.ErrNil {
t.Fatal(err)
} else if s != "" {
t.Fatal(s, "must empty")
}
if _, err = c1.Do("xmigrate", "127.0.0.1", 11186, "ALL", "a", 0, timeout); err != nil {
t.Fatal(err)
}
if s, err := goredis.String(c2.Do("get", "a")); err != nil {
t.Fatal(err)
} else if s != "1" {
t.Fatal(s, "must 1")
}
if s, err := goredis.String(c1.Do("get", "a")); err != nil && err != goredis.ErrNil {
t.Fatal(err)
} else if s != "" {
t.Fatal(s, "must empty")
}
}

View File

@ -2,7 +2,7 @@ package server
import (
"fmt"
goledis "github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"github.com/siddontang/ledisdb/config"
"os"
"reflect"
@ -159,9 +159,9 @@ func TestReplication(t *testing.T) {
}
func checkTestRole(addr string, checkRoles []interface{}) error {
conn, _ := goledis.Connect(addr)
conn, _ := goredis.Connect(addr)
defer conn.Close()
roles, err := goledis.MultiBulk(conn.Do("ROLE"))
roles, err := goredis.MultiBulk(conn.Do("ROLE"))
if err != nil {
return err
} else if !reflect.DeepEqual(roles, checkRoles) {

View File

@ -9,13 +9,15 @@ import (
"strings"
)
func parseScanArgs(args [][]byte) (cursor []byte, match string, count int, err error) {
func parseScanArgs(args [][]byte) (cursor []byte, match string, count int, desc bool, err error) {
cursor = args[0]
args = args[1:]
count = 10
desc = false
for i := 0; i < len(args); {
switch strings.ToUpper(hack.String(args[i])) {
case "MATCH":
@ -25,7 +27,7 @@ func parseScanArgs(args [][]byte) (cursor []byte, match string, count int, err e
}
match = hack.String(args[i+1])
i = i + 2
i++
case "COUNT":
if i+1 >= len(args) {
err = ErrCmdParams
@ -37,17 +39,23 @@ func parseScanArgs(args [][]byte) (cursor []byte, match string, count int, err e
return
}
i = i + 2
i++
case "ASC":
desc = false
case "DESC":
desc = true
default:
err = fmt.Errorf("invalid argument %s", args[i])
return
}
i++
}
return
}
// XSCAN type cursor [MATCH match] [COUNT count]
// XSCAN type cursor [MATCH match] [COUNT count] [ASC|DESC]
func xscanCommand(c *client) error {
args := c.args
@ -71,13 +79,20 @@ func xscanCommand(c *client) error {
return fmt.Errorf("invalid key type %s", args[0])
}
cursor, match, count, err := parseScanArgs(args[1:])
cursor, match, count, desc, err := parseScanArgs(args[1:])
if err != nil {
return err
}
ay, err := c.db.Scan(dataType, cursor, count, false, match)
var ay [][]byte
if !desc {
ay, err = c.db.Scan(dataType, cursor, count, false, match)
} else {
ay, err = c.db.RevScan(dataType, cursor, count, false, match)
}
if err != nil {
return err
}
@ -93,7 +108,7 @@ func xscanCommand(c *client) error {
return nil
}
// XHSCAN key cursor [MATCH match] [COUNT count]
// XHSCAN key cursor [MATCH match] [COUNT count] [ASC|DESC]
func xhscanCommand(c *client) error {
args := c.args
@ -103,13 +118,20 @@ func xhscanCommand(c *client) error {
key := args[0]
cursor, match, count, err := parseScanArgs(args[1:])
cursor, match, count, desc, err := parseScanArgs(args[1:])
if err != nil {
return err
}
ay, err := c.db.HScan(key, cursor, count, false, match)
var ay []ledis.FVPair
if !desc {
ay, err = c.db.HScan(key, cursor, count, false, match)
} else {
ay, err = c.db.HRevScan(key, cursor, count, false, match)
}
if err != nil {
return err
}
@ -133,7 +155,7 @@ func xhscanCommand(c *client) error {
return nil
}
// XSSCAN key cursor [MATCH match] [COUNT count]
// XSSCAN key cursor [MATCH match] [COUNT count] [ASC|DESC]
func xsscanCommand(c *client) error {
args := c.args
@ -143,13 +165,20 @@ func xsscanCommand(c *client) error {
key := args[0]
cursor, match, count, err := parseScanArgs(args[1:])
cursor, match, count, desc, err := parseScanArgs(args[1:])
if err != nil {
return err
}
ay, err := c.db.SScan(key, cursor, count, false, match)
var ay [][]byte
if !desc {
ay, err = c.db.SScan(key, cursor, count, false, match)
} else {
ay, err = c.db.SRevScan(key, cursor, count, false, match)
}
if err != nil {
return err
}
@ -167,7 +196,7 @@ func xsscanCommand(c *client) error {
return nil
}
// XZSCAN key cursor [MATCH match] [COUNT count]
// XZSCAN key cursor [MATCH match] [COUNT count] [ASC|DESC]
func xzscanCommand(c *client) error {
args := c.args
@ -177,13 +206,20 @@ func xzscanCommand(c *client) error {
key := args[0]
cursor, match, count, err := parseScanArgs(args[1:])
cursor, match, count, desc, err := parseScanArgs(args[1:])
if err != nil {
return err
}
ay, err := c.db.ZScan(key, cursor, count, false, match)
var ay []ledis.ScorePair
if !desc {
ay, err = c.db.ZScan(key, cursor, count, false, match)
} else {
ay, err = c.db.ZRevScan(key, cursor, count, false, match)
}
if err != nil {
return err
}

View File

@ -2,7 +2,7 @@ package server
import (
"fmt"
"github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"github.com/siddontang/ledisdb/config"
"os"
"testing"
@ -22,10 +22,8 @@ func TestScan(t *testing.T) {
go s.Run()
defer s.Close()
cc := new(ledis.Config)
cc.Addr = cfg.Addr
cc.MaxIdleConns = 1
c := ledis.NewClient(cc)
c := goredis.NewClient(cfg.Addr, "")
c.SetMaxIdleConns(1)
defer c.Close()
testKVScan(t, c)
@ -37,7 +35,7 @@ func TestScan(t *testing.T) {
}
func checkScanValues(t *testing.T, ay interface{}, values ...interface{}) {
a, err := ledis.Strings(ay, nil)
a, err := goredis.Strings(ay, nil)
if err != nil {
t.Fatal(err)
}
@ -53,8 +51,8 @@ func checkScanValues(t *testing.T, ay interface{}, values ...interface{}) {
}
}
func checkScan(t *testing.T, c *ledis.Client, tp string) {
if ay, err := ledis.Values(c.Do("XSCAN", tp, "", "count", 5)); err != nil {
func checkScan(t *testing.T, c *goredis.Client, tp string) {
if ay, err := goredis.Values(c.Do("XSCAN", tp, "", "count", 5)); err != nil {
t.Fatal(err)
} else if len(ay) != 2 {
t.Fatal(len(ay))
@ -64,7 +62,7 @@ func checkScan(t *testing.T, c *ledis.Client, tp string) {
checkScanValues(t, ay[1], 0, 1, 2, 3, 4)
}
if ay, err := ledis.Values(c.Do("XSCAN", tp, "4", "count", 6)); err != nil {
if ay, err := goredis.Values(c.Do("XSCAN", tp, "4", "count", 6)); err != nil {
t.Fatal(err)
} else if len(ay) != 2 {
t.Fatal(len(ay))
@ -76,7 +74,7 @@ func checkScan(t *testing.T, c *ledis.Client, tp string) {
}
func testKVScan(t *testing.T, c *ledis.Client) {
func testKVScan(t *testing.T, c *goredis.Client) {
for i := 0; i < 10; i++ {
if _, err := c.Do("set", fmt.Sprintf("%d", i), []byte("value")); err != nil {
t.Fatal(err)
@ -86,7 +84,7 @@ func testKVScan(t *testing.T, c *ledis.Client) {
checkScan(t, c, "KV")
}
func testHashKeyScan(t *testing.T, c *ledis.Client) {
func testHashKeyScan(t *testing.T, c *goredis.Client) {
for i := 0; i < 10; i++ {
if _, err := c.Do("hset", fmt.Sprintf("%d", i), fmt.Sprintf("%d", i), []byte("value")); err != nil {
t.Fatal(err)
@ -96,7 +94,7 @@ func testHashKeyScan(t *testing.T, c *ledis.Client) {
checkScan(t, c, "HASH")
}
func testListKeyScan(t *testing.T, c *ledis.Client) {
func testListKeyScan(t *testing.T, c *goredis.Client) {
for i := 0; i < 10; i++ {
if _, err := c.Do("lpush", fmt.Sprintf("%d", i), fmt.Sprintf("%d", i)); err != nil {
t.Fatal(err)
@ -106,7 +104,7 @@ func testListKeyScan(t *testing.T, c *ledis.Client) {
checkScan(t, c, "LIST")
}
func testZSetKeyScan(t *testing.T, c *ledis.Client) {
func testZSetKeyScan(t *testing.T, c *goredis.Client) {
for i := 0; i < 10; i++ {
if _, err := c.Do("zadd", fmt.Sprintf("%d", i), i, []byte("value")); err != nil {
t.Fatal(err)
@ -116,7 +114,7 @@ func testZSetKeyScan(t *testing.T, c *ledis.Client) {
checkScan(t, c, "ZSET")
}
func testSetKeyScan(t *testing.T, c *ledis.Client) {
func testSetKeyScan(t *testing.T, c *goredis.Client) {
for i := 0; i < 10; i++ {
if _, err := c.Do("sadd", fmt.Sprintf("%d", i), fmt.Sprintf("%d", i)); err != nil {
t.Fatal(err)
@ -133,7 +131,7 @@ func TestHashScan(t *testing.T) {
key := "scan_hash"
c.Do("HMSET", key, "a", 1, "b", 2)
if ay, err := ledis.Values(c.Do("XHSCAN", key, "")); err != nil {
if ay, err := goredis.Values(c.Do("XHSCAN", key, "")); err != nil {
t.Fatal(err)
} else if len(ay) != 2 {
t.Fatal(len(ay))
@ -149,7 +147,7 @@ func TestSetScan(t *testing.T) {
key := "scan_set"
c.Do("SADD", key, "a", "b")
if ay, err := ledis.Values(c.Do("XSSCAN", key, "")); err != nil {
if ay, err := goredis.Values(c.Do("XSSCAN", key, "")); err != nil {
t.Fatal(err)
} else if len(ay) != 2 {
t.Fatal(len(ay))
@ -166,7 +164,7 @@ func TestZSetScan(t *testing.T) {
key := "scan_zset"
c.Do("ZADD", key, 1, "a", 2, "b")
if ay, err := ledis.Values(c.Do("XZSCAN", key, "")); err != nil {
if ay, err := goredis.Values(c.Do("XZSCAN", key, "")); err != nil {
t.Fatal(err)
} else if len(ay) != 2 {
t.Fatal(len(ay))

View File

@ -4,7 +4,7 @@ package server
import (
"fmt"
"github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"reflect"
"testing"
)
@ -13,13 +13,13 @@ func TestCmdEval(t *testing.T) {
c := getTestConn()
defer c.Close()
if v, err := ledis.Strings(c.Do("eval", "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "key1", "key2", "first", "second")); err != nil {
if v, err := goredis.Strings(c.Do("eval", "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "key1", "key2", "first", "second")); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(v, []string{"key1", "key2", "first", "second"}) {
t.Fatal(fmt.Sprintf("%v", v))
}
if v, err := ledis.Strings(c.Do("eval", "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "key1", "key2", "first", "second")); err != nil {
if v, err := goredis.Strings(c.Do("eval", "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "key1", "key2", "first", "second")); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(v, []string{"key1", "key2", "first", "second"}) {
t.Fatal(fmt.Sprintf("%v", v))
@ -27,31 +27,31 @@ func TestCmdEval(t *testing.T) {
var sha1 string
var err error
if sha1, err = ledis.String(c.Do("script", "load", "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}")); err != nil {
if sha1, err = goredis.String(c.Do("script", "load", "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}")); err != nil {
t.Fatal(err)
} else if len(sha1) != 40 {
t.Fatal(sha1)
}
if v, err := ledis.Strings(c.Do("evalsha", sha1, 2, "key1", "key2", "first", "second")); err != nil {
if v, err := goredis.Strings(c.Do("evalsha", sha1, 2, "key1", "key2", "first", "second")); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(v, []string{"key1", "key2", "first", "second"}) {
t.Fatal(fmt.Sprintf("%v", v))
}
if ay, err := ledis.Values(c.Do("script", "exists", sha1, "01234567890123456789")); err != nil {
if ay, err := goredis.Values(c.Do("script", "exists", sha1, "01234567890123456789")); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(ay, []interface{}{int64(1), int64(0)}) {
t.Fatal(fmt.Sprintf("%v", ay))
}
if ok, err := ledis.String(c.Do("script", "flush")); err != nil {
if ok, err := goredis.String(c.Do("script", "flush")); err != nil {
t.Fatal(err)
} else if ok != "OK" {
t.Fatal(ok)
}
if ay, err := ledis.Values(c.Do("script", "exists", sha1)); err != nil {
if ay, err := goredis.Values(c.Do("script", "exists", sha1)); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(ay, []interface{}{int64(0)}) {
t.Fatal(fmt.Sprintf("%v", ay))

View File

@ -1,7 +1,7 @@
package server
import (
"github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"testing"
)
@ -12,91 +12,91 @@ func TestSet(t *testing.T) {
key1 := "testdb_cmd_set_1"
key2 := "testdb_cmd_set_2"
if n, err := ledis.Int(c.Do("skeyexists", key1)); err != nil {
if n, err := goredis.Int(c.Do("skeyexists", key1)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("sadd", key1, 0, 1)); err != nil {
if n, err := goredis.Int(c.Do("sadd", key1, 0, 1)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("skeyexists", key1)); err != nil {
if n, err := goredis.Int(c.Do("skeyexists", key1)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("sadd", key2, 0, 1, 2, 3)); err != nil {
if n, err := goredis.Int(c.Do("sadd", key2, 0, 1, 2, 3)); err != nil {
t.Fatal(err)
} else if n != 4 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("scard", key1)); err != nil {
if n, err := goredis.Int(c.Do("scard", key1)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.MultiBulk(c.Do("sdiff", key2, key1)); err != nil {
if n, err := goredis.MultiBulk(c.Do("sdiff", key2, key1)); err != nil {
t.Fatal(err)
} else if len(n) != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("sdiffstore", []byte("cmd_set_em1"), key2, key1)); err != nil {
if n, err := goredis.Int(c.Do("sdiffstore", []byte("cmd_set_em1"), key2, key1)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.MultiBulk(c.Do("sunion", key1, key2)); err != nil {
if n, err := goredis.MultiBulk(c.Do("sunion", key1, key2)); err != nil {
t.Fatal(err)
} else if len(n) != 4 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("sunionstore", []byte("cmd_set_em2"), key1, key2)); err != nil {
if n, err := goredis.Int(c.Do("sunionstore", []byte("cmd_set_em2"), key1, key2)); err != nil {
t.Fatal(err)
} else if n != 4 {
t.Fatal(n)
}
if n, err := ledis.MultiBulk(c.Do("sinter", key1, key2)); err != nil {
if n, err := goredis.MultiBulk(c.Do("sinter", key1, key2)); err != nil {
t.Fatal(err)
} else if len(n) != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("sinterstore", []byte("cmd_set_em3"), key1, key2)); err != nil {
if n, err := goredis.Int(c.Do("sinterstore", []byte("cmd_set_em3"), key1, key2)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("srem", key1, 0, 1)); err != nil {
if n, err := goredis.Int(c.Do("srem", key1, 0, 1)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("sismember", key2, 0)); err != nil {
if n, err := goredis.Int(c.Do("sismember", key2, 0)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.MultiBulk(c.Do("smembers", key2)); err != nil {
if n, err := goredis.MultiBulk(c.Do("smembers", key2)); err != nil {
t.Fatal(err)
} else if len(n) != 4 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("sclear", key2)); err != nil {
if n, err := goredis.Int(c.Do("sclear", key2)); err != nil {
t.Fatal(err)
} else if n != 4 {
t.Fatal(n)
@ -104,7 +104,7 @@ func TestSet(t *testing.T) {
c.Do("sadd", key1, 0)
c.Do("sadd", key2, 1)
if n, err := ledis.Int(c.Do("smclear", key1, key2)); err != nil {
if n, err := goredis.Int(c.Do("smclear", key1, key2)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)

View File

@ -2,7 +2,7 @@ package server
import (
"fmt"
goledis "github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"testing"
)
@ -79,7 +79,7 @@ func TestSort(t *testing.T) {
t.Fatal(err)
}
if n, err := goledis.Int(c.Do("XLSORT", key, "STORE", storeKey)); err != nil {
if n, err := goredis.Int(c.Do("XLSORT", key, "STORE", storeKey)); err != nil {
t.Fatal(err)
} else if n != 3 {
t.Fatalf("invalid return store sort number, %d != 3", n)

View File

@ -2,7 +2,7 @@ package server
import (
"fmt"
"github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"testing"
"time"
)
@ -63,13 +63,13 @@ func TestExpire(t *testing.T) {
}
// expire + ttl
exp := int64(10)
if n, err := ledis.Int(c.Do(expire, key, exp)); err != nil {
if n, err := goredis.Int(c.Do(expire, key, exp)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if ttl, err := ledis.Int64(c.Do(ttl, key)); err != nil {
if ttl, err := goredis.Int64(c.Do(ttl, key)); err != nil {
t.Fatal(err)
} else if ttl == -1 {
t.Fatal("no ttl")
@ -77,13 +77,13 @@ func TestExpire(t *testing.T) {
// expireat + ttl
tm := now() + 3
if n, err := ledis.Int(c.Do(expireat, key, tm)); err != nil {
if n, err := goredis.Int(c.Do(expireat, key, tm)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if ttl, err := ledis.Int64(c.Do(ttl, key)); err != nil {
if ttl, err := goredis.Int64(c.Do(ttl, key)); err != nil {
t.Fatal(err)
} else if ttl == -1 {
t.Fatal("no ttl")
@ -92,31 +92,31 @@ func TestExpire(t *testing.T) {
kErr := "not_exist_ttl"
// err - expire, expireat
if n, err := ledis.Int(c.Do(expire, kErr, tm)); err != nil || n != 0 {
if n, err := goredis.Int(c.Do(expire, kErr, tm)); err != nil || n != 0 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do(expireat, kErr, tm)); err != nil || n != 0 {
if n, err := goredis.Int(c.Do(expireat, kErr, tm)); err != nil || n != 0 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do(ttl, kErr)); err != nil || n != -1 {
if n, err := goredis.Int(c.Do(ttl, kErr)); err != nil || n != -1 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do(persist, key)); err != nil {
if n, err := goredis.Int(c.Do(persist, key)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do(expire, key, 10)); err != nil {
if n, err := goredis.Int(c.Do(expire, key, 10)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do(persist, key)); err != nil {
if n, err := goredis.Int(c.Do(persist, key)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)

View File

@ -2,7 +2,7 @@ package server
import (
"fmt"
"github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"reflect"
"strconv"
"testing"
@ -14,103 +14,103 @@ func TestZSet(t *testing.T) {
key := []byte("myzset")
if n, err := ledis.Int(c.Do("zkeyexists", key)); err != nil {
if n, err := goredis.Int(c.Do("zkeyexists", key)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zadd", key, 3, "a", 4, "b")); err != nil {
if n, err := goredis.Int(c.Do("zadd", key, 3, "a", 4, "b")); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zkeyexists", key)); err != nil {
if n, err := goredis.Int(c.Do("zkeyexists", key)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
if n, err := goredis.Int(c.Do("zcard", key)); err != nil {
t.Fatal(n)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zadd", key, 1, "a", 2, "b")); err != nil {
if n, err := goredis.Int(c.Do("zadd", key, 1, "a", 2, "b")); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
if n, err := goredis.Int(c.Do("zcard", key)); err != nil {
t.Fatal(n)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zadd", key, 3, "c", 4, "d")); err != nil {
if n, err := goredis.Int(c.Do("zadd", key, 3, "c", 4, "d")); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
if n, err := goredis.Int(c.Do("zcard", key)); err != nil {
t.Fatal(err)
} else if n != 4 {
t.Fatal(n)
}
if s, err := ledis.Int(c.Do("zscore", key, "c")); err != nil {
if s, err := goredis.Int(c.Do("zscore", key, "c")); err != nil {
t.Fatal(err)
} else if s != 3 {
t.Fatal(s)
}
if n, err := ledis.Int(c.Do("zrem", key, "d", "e")); err != nil {
if n, err := goredis.Int(c.Do("zrem", key, "d", "e")); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
if n, err := goredis.Int(c.Do("zcard", key)); err != nil {
t.Fatal(err)
} else if n != 3 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zincrby", key, 4, "c")); err != nil {
if n, err := goredis.Int(c.Do("zincrby", key, 4, "c")); err != nil {
t.Fatal(err)
} else if n != 7 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zincrby", key, -4, "c")); err != nil {
if n, err := goredis.Int(c.Do("zincrby", key, -4, "c")); err != nil {
t.Fatal(err)
} else if n != 3 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zincrby", key, 4, "d")); err != nil {
if n, err := goredis.Int(c.Do("zincrby", key, 4, "d")); err != nil {
t.Fatal(err)
} else if n != 4 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
if n, err := goredis.Int(c.Do("zcard", key)); err != nil {
t.Fatal(err)
} else if n != 4 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zrem", key, "a", "b", "c", "d")); err != nil {
if n, err := goredis.Int(c.Do("zrem", key, "a", "b", "c", "d")); err != nil {
t.Fatal(err)
} else if n != 4 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
if n, err := goredis.Int(c.Do("zcard", key)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
@ -123,47 +123,47 @@ func TestZSetCount(t *testing.T) {
defer c.Close()
key := []byte("myzset")
if _, err := ledis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
if _, err := goredis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
t.Fatal(err)
}
if n, err := ledis.Int(c.Do("zcount", key, 2, 4)); err != nil {
if n, err := goredis.Int(c.Do("zcount", key, 2, 4)); err != nil {
t.Fatal(err)
} else if n != 3 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zcount", key, 4, 4)); err != nil {
if n, err := goredis.Int(c.Do("zcount", key, 4, 4)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zcount", key, 4, 3)); err != nil {
if n, err := goredis.Int(c.Do("zcount", key, 4, 3)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zcount", key, "(2", 4)); err != nil {
if n, err := goredis.Int(c.Do("zcount", key, "(2", 4)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zcount", key, "2", "(4")); err != nil {
if n, err := goredis.Int(c.Do("zcount", key, "2", "(4")); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zcount", key, "(2", "(4")); err != nil {
if n, err := goredis.Int(c.Do("zcount", key, "(2", "(4")); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zcount", key, "-inf", "+inf")); err != nil {
if n, err := goredis.Int(c.Do("zcount", key, "-inf", "+inf")); err != nil {
t.Fatal(err)
} else if n != 4 {
t.Fatal(n)
@ -171,7 +171,7 @@ func TestZSetCount(t *testing.T) {
c.Do("zadd", key, 3, "e")
if n, err := ledis.Int(c.Do("zcount", key, "(2", "(4")); err != nil {
if n, err := goredis.Int(c.Do("zcount", key, "(2", "(4")); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
@ -185,27 +185,27 @@ func TestZSetRank(t *testing.T) {
defer c.Close()
key := []byte("myzset")
if _, err := ledis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
if _, err := goredis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
t.Fatal(err)
}
if n, err := ledis.Int(c.Do("zrank", key, "c")); err != nil {
if n, err := goredis.Int(c.Do("zrank", key, "c")); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if _, err := ledis.Int(c.Do("zrank", key, "e")); err != ledis.ErrNil {
if _, err := goredis.Int(c.Do("zrank", key, "e")); err != goredis.ErrNil {
t.Fatal(err)
}
if n, err := ledis.Int(c.Do("zrevrank", key, "c")); err != nil {
if n, err := goredis.Int(c.Do("zrevrank", key, "c")); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if _, err := ledis.Int(c.Do("zrevrank", key, "e")); err != ledis.ErrNil {
if _, err := goredis.Int(c.Do("zrevrank", key, "e")); err != goredis.ErrNil {
t.Fatal(err)
}
}
@ -242,11 +242,11 @@ func TestZSetRangeScore(t *testing.T) {
defer c.Close()
key := []byte("myzset_range")
if _, err := ledis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
if _, err := goredis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
t.Fatal(err)
}
if v, err := ledis.MultiBulk(c.Do("zrangebyscore", key, 1, 4, "withscores")); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrangebyscore", key, 1, 4, "withscores")); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "a", 1, "b", 2, "c", 3, "d", 4); err != nil {
@ -254,7 +254,7 @@ func TestZSetRangeScore(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("zrangebyscore", key, 1, 4, "withscores", "limit", 1, 2)); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrangebyscore", key, 1, 4, "withscores", "limit", 1, 2)); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "b", 2, "c", 3); err != nil {
@ -262,7 +262,7 @@ func TestZSetRangeScore(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("zrangebyscore", key, "-inf", "+inf", "withscores")); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrangebyscore", key, "-inf", "+inf", "withscores")); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "a", 1, "b", 2, "c", 3, "d", 4); err != nil {
@ -270,7 +270,7 @@ func TestZSetRangeScore(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("zrangebyscore", key, "(1", "(4")); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrangebyscore", key, "(1", "(4")); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "b", "c"); err != nil {
@ -278,7 +278,7 @@ func TestZSetRangeScore(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("zrevrangebyscore", key, 4, 1, "withscores")); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrevrangebyscore", key, 4, 1, "withscores")); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "d", 4, "c", 3, "b", 2, "a", 1); err != nil {
@ -286,7 +286,7 @@ func TestZSetRangeScore(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("zrevrangebyscore", key, 4, 1, "withscores", "limit", 1, 2)); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrevrangebyscore", key, 4, 1, "withscores", "limit", 1, 2)); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "c", 3, "b", 2); err != nil {
@ -294,7 +294,7 @@ func TestZSetRangeScore(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("zrevrangebyscore", key, "+inf", "-inf", "withscores")); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrevrangebyscore", key, "+inf", "-inf", "withscores")); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "d", 4, "c", 3, "b", 2, "a", 1); err != nil {
@ -302,7 +302,7 @@ func TestZSetRangeScore(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("zrevrangebyscore", key, "(4", "(1")); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrevrangebyscore", key, "(4", "(1")); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "c", "b"); err != nil {
@ -310,19 +310,19 @@ func TestZSetRangeScore(t *testing.T) {
}
}
if n, err := ledis.Int(c.Do("zremrangebyscore", key, 2, 3)); err != nil {
if n, err := goredis.Int(c.Do("zremrangebyscore", key, 2, 3)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
if n, err := goredis.Int(c.Do("zcard", key)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if v, err := ledis.MultiBulk(c.Do("zrangebyscore", key, 1, 4)); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrangebyscore", key, 1, 4)); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "a", "d"); err != nil {
@ -336,11 +336,11 @@ func TestZSetRange(t *testing.T) {
defer c.Close()
key := []byte("myzset_range_rank")
if _, err := ledis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
if _, err := goredis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
t.Fatal(err)
}
if v, err := ledis.MultiBulk(c.Do("zrange", key, 0, 3, "withscores")); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrange", key, 0, 3, "withscores")); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "a", 1, "b", 2, "c", 3, "d", 4); err != nil {
@ -348,7 +348,7 @@ func TestZSetRange(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("zrange", key, 1, 4, "withscores")); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrange", key, 1, 4, "withscores")); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "b", 2, "c", 3, "d", 4); err != nil {
@ -356,7 +356,7 @@ func TestZSetRange(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("zrange", key, -2, -1, "withscores")); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrange", key, -2, -1, "withscores")); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "c", 3, "d", 4); err != nil {
@ -364,7 +364,7 @@ func TestZSetRange(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("zrange", key, 0, -1, "withscores")); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrange", key, 0, -1, "withscores")); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "a", 1, "b", 2, "c", 3, "d", 4); err != nil {
@ -372,13 +372,13 @@ func TestZSetRange(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("zrange", key, -1, -2, "withscores")); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrange", key, -1, -2, "withscores")); err != nil {
t.Fatal(err)
} else if len(v) != 0 {
t.Fatal(len(v))
}
if v, err := ledis.MultiBulk(c.Do("zrevrange", key, 0, 4, "withscores")); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrevrange", key, 0, 4, "withscores")); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "d", 4, "c", 3, "b", 2, "a", 1); err != nil {
@ -386,7 +386,7 @@ func TestZSetRange(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("zrevrange", key, 0, -1, "withscores")); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrevrange", key, 0, -1, "withscores")); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "d", 4, "c", 3, "b", 2, "a", 1); err != nil {
@ -394,7 +394,7 @@ func TestZSetRange(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("zrevrange", key, 2, 3, "withscores")); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrevrange", key, 2, 3, "withscores")); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "b", 2, "a", 1); err != nil {
@ -402,7 +402,7 @@ func TestZSetRange(t *testing.T) {
}
}
if v, err := ledis.MultiBulk(c.Do("zrevrange", key, -2, -1, "withscores")); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrevrange", key, -2, -1, "withscores")); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "b", 2, "a", 1); err != nil {
@ -410,19 +410,19 @@ func TestZSetRange(t *testing.T) {
}
}
if n, err := ledis.Int(c.Do("zremrangebyrank", key, 2, 3)); err != nil {
if n, err := goredis.Int(c.Do("zremrangebyrank", key, 2, 3)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
if n, err := goredis.Int(c.Do("zcard", key)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if v, err := ledis.MultiBulk(c.Do("zrange", key, 0, 4)); err != nil {
if v, err := goredis.MultiBulk(c.Do("zrange", key, 0, 4)); err != nil {
t.Fatal(err)
} else {
if err := testZSetRange(v, "a", "b"); err != nil {
@ -430,13 +430,13 @@ func TestZSetRange(t *testing.T) {
}
}
if n, err := ledis.Int(c.Do("zclear", key)); err != nil {
if n, err := goredis.Int(c.Do("zclear", key)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
if n, err := goredis.Int(c.Do("zcard", key)); err != nil {
t.Fatal(err)
} else if n != 0 {
t.Fatal(n)
@ -634,7 +634,7 @@ func TestZUnionStore(t *testing.T) {
t.Fatal(err.Error())
}
if n, err := ledis.Int64(c.Do("zunionstore", "out", "2", "k1", "k2", "weights", "1", "2")); err != nil {
if n, err := goredis.Int64(c.Do("zunionstore", "out", "2", "k1", "k2", "weights", "1", "2")); err != nil {
t.Fatal(err.Error())
} else {
if n != 3 {
@ -642,7 +642,7 @@ func TestZUnionStore(t *testing.T) {
}
}
if n, err := ledis.Int64(c.Do("zunionstore", "out", "2", "k1", "k2", "weights", "1", "2", "aggregate", "min")); err != nil {
if n, err := goredis.Int64(c.Do("zunionstore", "out", "2", "k1", "k2", "weights", "1", "2", "aggregate", "min")); err != nil {
t.Fatal(err.Error())
} else {
if n != 3 {
@ -650,7 +650,7 @@ func TestZUnionStore(t *testing.T) {
}
}
if n, err := ledis.Int64(c.Do("zunionstore", "out", "2", "k1", "k2", "aggregate", "max")); err != nil {
if n, err := goredis.Int64(c.Do("zunionstore", "out", "2", "k1", "k2", "aggregate", "max")); err != nil {
t.Fatal(err.Error())
} else {
if n != 3 {
@ -658,7 +658,7 @@ func TestZUnionStore(t *testing.T) {
}
}
if n, err := ledis.Int64(c.Do("zscore", "out", "two")); err != nil {
if n, err := goredis.Int64(c.Do("zscore", "out", "two")); err != nil {
t.Fatal(err.Error())
} else {
if n != 2 {
@ -687,7 +687,7 @@ func TestZInterStore(t *testing.T) {
t.Fatal(err.Error())
}
if n, err := ledis.Int64(c.Do("zinterstore", "out", "2", "k1", "k2", "weights", "1", "2")); err != nil {
if n, err := goredis.Int64(c.Do("zinterstore", "out", "2", "k1", "k2", "weights", "1", "2")); err != nil {
t.Fatal(err.Error())
} else {
if n != 1 {
@ -695,7 +695,7 @@ func TestZInterStore(t *testing.T) {
}
}
if n, err := ledis.Int64(c.Do("zinterstore", "out", "2", "k1", "k2", "aggregate", "min", "weights", "1", "2")); err != nil {
if n, err := goredis.Int64(c.Do("zinterstore", "out", "2", "k1", "k2", "aggregate", "min", "weights", "1", "2")); err != nil {
t.Fatal(err.Error())
} else {
if n != 1 {
@ -703,7 +703,7 @@ func TestZInterStore(t *testing.T) {
}
}
if n, err := ledis.Int64(c.Do("zinterstore", "out", "2", "k1", "k2", "aggregate", "sum")); err != nil {
if n, err := goredis.Int64(c.Do("zinterstore", "out", "2", "k1", "k2", "aggregate", "sum")); err != nil {
t.Fatal(err.Error())
} else {
if n != 1 {
@ -711,7 +711,7 @@ func TestZInterStore(t *testing.T) {
}
}
if n, err := ledis.Int64(c.Do("zscore", "out", "two")); err != nil {
if n, err := goredis.Int64(c.Do("zscore", "out", "two")); err != nil {
t.Fatal(err.Error())
} else {
if n != 3 {
@ -723,7 +723,7 @@ func TestZInterStore(t *testing.T) {
t.Fatal(err.Error())
}
if n, err := ledis.Int64(c.Do("zinterstore", "out", "3", "k1", "k2", "k3", "aggregate", "sum")); err != nil {
if n, err := goredis.Int64(c.Do("zinterstore", "out", "3", "k1", "k2", "k3", "aggregate", "sum")); err != nil {
t.Fatal(err.Error())
} else {
if n != 0 {
@ -735,7 +735,7 @@ func TestZInterStore(t *testing.T) {
t.Fatal(err.Error())
}
if n, err := ledis.Int64(c.Do("zinterstore", "out", "3", "k1", "k2", "k3", "aggregate", "sum", "weights", "3", "2", "2")); err != nil {
if n, err := goredis.Int64(c.Do("zinterstore", "out", "3", "k1", "k2", "k3", "aggregate", "sum", "weights", "3", "2", "2")); err != nil {
t.Fatal(err.Error())
} else {
if n != 1 {
@ -743,7 +743,7 @@ func TestZInterStore(t *testing.T) {
}
}
if n, err := ledis.Int64(c.Do("zscore", "out", "two")); err != nil {
if n, err := goredis.Int64(c.Do("zscore", "out", "two")); err != nil {
t.Fatal(err.Error())
} else {
if n != 14 {
@ -762,37 +762,37 @@ func TestZSetLex(t *testing.T) {
t.Fatal(err)
}
if ay, err := ledis.Strings(c.Do("zrangebylex", key, "-", "[c")); err != nil {
if ay, err := goredis.Strings(c.Do("zrangebylex", key, "-", "[c")); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(ay, []string{"a", "b", "c"}) {
t.Fatal("must equal")
}
if ay, err := ledis.Strings(c.Do("zrangebylex", key, "-", "(c")); err != nil {
if ay, err := goredis.Strings(c.Do("zrangebylex", key, "-", "(c")); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(ay, []string{"a", "b"}) {
t.Fatal("must equal")
}
if ay, err := ledis.Strings(c.Do("zrangebylex", key, "[aaa", "(g")); err != nil {
if ay, err := goredis.Strings(c.Do("zrangebylex", key, "[aaa", "(g")); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(ay, []string{"b", "c", "d", "e", "f"}) {
t.Fatal("must equal")
}
if n, err := ledis.Int64(c.Do("zlexcount", key, "-", "(c")); err != nil {
if n, err := goredis.Int64(c.Do("zlexcount", key, "-", "(c")); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int64(c.Do("zremrangebylex", key, "[aaa", "(g")); err != nil {
if n, err := goredis.Int64(c.Do("zremrangebylex", key, "[aaa", "(g")); err != nil {
t.Fatal(err)
} else if n != 5 {
t.Fatal(n)
}
if n, err := ledis.Int64(c.Do("zlexcount", key, "-", "+")); err != nil {
if n, err := goredis.Int64(c.Do("zlexcount", key, "-", "+")); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)

View File

@ -34,8 +34,18 @@ const (
ZSET = ledis.ZSET
)
const (
KVName = ledis.KVName
ListName = ledis.ListName
HashName = ledis.HashName
SetName = ledis.SetName
ZSetName = ledis.ZSetName
)
const (
GB uint64 = 1024 * 1024 * 1024
MB uint64 = 1024 * 1024
KB uint64 = 1024
)
var TypeNames = []string{KVName, ListName, HashName, SetName, ZSetName}

View File

@ -7,7 +7,7 @@ import (
"github.com/siddontang/go/log"
"github.com/siddontang/go/num"
"github.com/siddontang/go/sync2"
goledis "github.com/siddontang/ledisdb/client/goledis"
"github.com/siddontang/goredis"
"github.com/siddontang/ledisdb/ledis"
"github.com/siddontang/ledisdb/rpl"
"net"
@ -49,7 +49,7 @@ type master struct {
sync.Mutex
connLock sync.Mutex
conn *goledis.Conn
conn *goredis.Conn
app *App
@ -108,7 +108,7 @@ func (m *master) checkConn() error {
var err error
if m.conn == nil {
m.conn, err = goledis.Connect(m.addr)
m.conn, err = goredis.Connect(m.addr)
} else {
if _, err = m.conn.Do("PING"); err != nil {
m.conn.Close()
@ -227,7 +227,7 @@ func (m *master) replConf() error {
return err
}
if s, err := goledis.String(m.conn.Do("replconf", "listening-port", port)); err != nil {
if s, err := goredis.String(m.conn.Do("replconf", "listening-port", port)); err != nil {
return err
} else if strings.ToUpper(s) != "OK" {
return fmt.Errorf("not ok but %s", s)

View File

@ -1,4 +1,4 @@
// +build !windows
// +build lmdb
package mdb

View File

@ -1,4 +1,4 @@
// +build !windows
// +build lmdb
package mdb

View File

@ -1,4 +1,4 @@
// +build !windows
// +build lmdb
package mdb