From f34d3ac27ba41f6d7ad0baa8880ab12f8ed89886 Mon Sep 17 00:00:00 2001 From: siddontang Date: Mon, 2 Mar 2015 11:10:54 +0800 Subject: [PATCH] refactor scan --- ledis/const.go | 37 ++++++++ ledis/ledis_db.go | 4 +- ledis/scan.go | 73 +++++++++++----- ledis/scan_test.go | 102 ++++++---------------- ledis/t_bit.go | 10 --- ledis/t_hash.go | 8 -- ledis/t_hash_test.go | 4 +- ledis/t_kv.go | 10 --- ledis/t_kv_test.go | 4 +- ledis/t_list.go | 8 -- ledis/t_list_test.go | 4 +- ledis/t_set.go | 8 -- ledis/t_set_test.go | 4 +- ledis/t_zset.go | 8 -- ledis/t_zset_test.go | 4 +- server/cmd_bit.go | 12 --- server/cmd_hash.go | 12 --- server/cmd_kv.go | 80 ----------------- server/cmd_list.go | 12 --- server/cmd_migrate.go | 10 +-- server/cmd_replication_test.go | 4 +- server/cmd_scan.go | 101 +++++++++++++++++++++ server/{scan_test.go => cmd_scan_test.go} | 32 +++---- server/cmd_set.go | 12 --- server/cmd_zset.go | 12 --- server/const.go | 12 +-- 26 files changed, 257 insertions(+), 330 deletions(-) create mode 100644 server/cmd_scan.go rename server/{scan_test.go => cmd_scan_test.go} (81%) diff --git a/ledis/const.go b/ledis/const.go index 5d4b587..9bb0b70 100644 --- a/ledis/const.go +++ b/ledis/const.go @@ -6,6 +6,43 @@ import ( const Version = "0.4" +type DataType byte + +// for out use +const ( + KV DataType = iota + LIST + HASH + SET + ZSET +) + +func (d DataType) String() string { + switch d { + case KV: + return KVName + case LIST: + return ListName + case HASH: + return HashName + case SET: + return SetName + case ZSET: + return ZSetName + default: + return "unknown" + } +} + +const ( + KVName = "KV" + ListName = "LIST" + HashName = "HASH" + SetName = "SET" + ZSetName = "ZSET" +) + +// for backend store const ( NoneType byte = 0 KVType byte = 1 diff --git a/ledis/ledis_db.go b/ledis/ledis_db.go index ebde98e..4e2b490 100644 --- a/ledis/ledis_db.go +++ b/ledis/ledis_db.go @@ -128,7 +128,7 @@ func (db *DB) flushType(t *batch, dataType byte) (drop int64, err error) { } var keys [][]byte - keys, err = db.scan(metaDataType, nil, 1024, false, "") + keys, err = db.scanGeneric(metaDataType, nil, 1024, false, "", false) for len(keys) != 0 || err != nil { for _, key := range keys { deleteFunc(t, key) @@ -141,7 +141,7 @@ func (db *DB) flushType(t *batch, dataType byte) (drop int64, err error) { } else { drop += int64(len(keys)) } - keys, err = db.scan(metaDataType, nil, 1024, false, "") + keys, err = db.scanGeneric(metaDataType, nil, 1024, false, "", false) } return } diff --git a/ledis/scan.go b/ledis/scan.go index 9e8e235..5b32133 100644 --- a/ledis/scan.go +++ b/ledis/scan.go @@ -9,15 +9,46 @@ import ( var errDataType = errors.New("error data type") var errMetaKey = errors.New("error meta key") -func (db *DB) scan(dataType byte, key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.scanGeneric(dataType, key, count, inclusive, match, false) +//fif inclusive is true, scan range [cursor, inf) else (cursor, inf) +func (db *DB) Scan(dataType DataType, cursor []byte, count int, inclusive bool, match string) ([][]byte, error) { + storeDataType, err := getDataStoreType(dataType) + if err != nil { + return nil, err + } + + return db.scanGeneric(storeDataType, cursor, count, inclusive, match, false) } -func (db *DB) revscan(dataType byte, key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.scanGeneric(dataType, key, count, inclusive, match, true) +//if inclusive is true, revscan range (-inf, cursor] else (inf, cursor) +func (db *DB) RevScan(dataType DataType, cursor []byte, count int, inclusive bool, match string) ([][]byte, error) { + storeDataType, err := getDataStoreType(dataType) + if err != nil { + return nil, err + } + + return db.scanGeneric(storeDataType, cursor, count, inclusive, match, true) } -func (db *DB) scanGeneric(dataType byte, key []byte, count int, +func getDataStoreType(dataType DataType) (byte, error) { + var storeDataType byte + switch dataType { + case KV: + storeDataType = KVType + case LIST: + storeDataType = LMetaType + case HASH: + storeDataType = HSizeType + case SET: + storeDataType = SSizeType + case ZSET: + storeDataType = ZSizeType + default: + return 0, errDataType + } + return storeDataType, nil +} + +func (db *DB) scanGeneric(storeDataType byte, key []byte, count int, inclusive bool, match string, reverse bool) ([][]byte, error) { var minKey, maxKey []byte var err error @@ -32,10 +63,10 @@ func (db *DB) scanGeneric(dataType byte, key []byte, count int, tp := store.RangeOpen if !reverse { - if minKey, err = db.encodeScanMinKey(dataType, key); err != nil { + if minKey, err = db.encodeScanMinKey(storeDataType, key); err != nil { return nil, err } - if maxKey, err = db.encodeScanMaxKey(dataType, nil); err != nil { + if maxKey, err = db.encodeScanMaxKey(storeDataType, nil); err != nil { return nil, err } @@ -43,10 +74,10 @@ func (db *DB) scanGeneric(dataType byte, key []byte, count int, tp = store.RangeROpen } } else { - if minKey, err = db.encodeScanMinKey(dataType, nil); err != nil { + if minKey, err = db.encodeScanMinKey(storeDataType, nil); err != nil { return nil, err } - if maxKey, err = db.encodeScanMaxKey(dataType, key); err != nil { + if maxKey, err = db.encodeScanMaxKey(storeDataType, key); err != nil { return nil, err } @@ -69,7 +100,7 @@ func (db *DB) scanGeneric(dataType byte, key []byte, count int, v := make([][]byte, 0, count) for i := 0; it.Valid() && i < count; it.Next() { - if k, err := db.decodeScanKey(dataType, it.Key()); err != nil { + if k, err := db.decodeScanKey(storeDataType, it.Key()); err != nil { continue } else if r != nil && !r.Match(k) { continue @@ -82,36 +113,36 @@ func (db *DB) scanGeneric(dataType byte, key []byte, count int, return v, nil } -func (db *DB) encodeScanMinKey(dataType byte, key []byte) ([]byte, error) { +func (db *DB) encodeScanMinKey(storeDataType byte, key []byte) ([]byte, error) { if len(key) == 0 { - return db.encodeScanKey(dataType, nil) + return db.encodeScanKey(storeDataType, nil) } else { if err := checkKeySize(key); err != nil { return nil, err } - return db.encodeScanKey(dataType, key) + return db.encodeScanKey(storeDataType, key) } } -func (db *DB) encodeScanMaxKey(dataType byte, key []byte) ([]byte, error) { +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(dataType, key) + return db.encodeScanKey(storeDataType, key) } - k, err := db.encodeScanKey(dataType, nil) + k, err := db.encodeScanKey(storeDataType, nil) if err != nil { return nil, err } - k[len(k)-1] = dataType + 1 + k[len(k)-1] = storeDataType + 1 return k, nil } -func (db *DB) encodeScanKey(dataType byte, key []byte) ([]byte, error) { - switch dataType { +func (db *DB) encodeScanKey(storeDataType byte, key []byte) ([]byte, error) { + switch storeDataType { case KVType: return db.encodeKVKey(key), nil case LMetaType: @@ -128,8 +159,8 @@ func (db *DB) encodeScanKey(dataType byte, key []byte) ([]byte, error) { return nil, errDataType } } -func (db *DB) decodeScanKey(dataType byte, ek []byte) ([]byte, error) { - if len(ek) < 2 || ek[0] != db.index || ek[1] != dataType { +func (db *DB) decodeScanKey(storeDataType byte, ek []byte) ([]byte, error) { + if len(ek) < 2 || ek[0] != db.index || ek[1] != storeDataType { return nil, errMetaKey } return ek[2:], nil diff --git a/ledis/scan_test.go b/ledis/scan_test.go index b2a0970..ded982c 100644 --- a/ledis/scan_test.go +++ b/ledis/scan_test.go @@ -21,13 +21,13 @@ func TestDBScan(t *testing.T) { db.FlushAll() - if v, err := db.Scan(nil, 10, true, ""); err != nil { + if v, err := db.Scan(KV, nil, 10, true, ""); err != nil { t.Fatal(err) } else if len(v) != 0 { t.Fatal(len(v)) } - if v, err := db.RevScan(nil, 10, true, ""); err != nil { + if v, err := db.RevScan(KV, nil, 10, true, ""); err != nil { t.Fatal(err) } else if len(v) != 0 { t.Fatal(len(v)) @@ -37,73 +37,73 @@ func TestDBScan(t *testing.T) { db.Set([]byte("b"), []byte{}) db.Set([]byte("c"), []byte{}) - if v, err := db.Scan(nil, 1, true, ""); err != nil { + if v, err := db.Scan(KV, nil, 1, true, ""); err != nil { t.Fatal(err) } else { checkTestScan(t, v, "a") } - if v, err := db.Scan([]byte("a"), 2, false, ""); err != nil { + if v, err := db.Scan(KV, []byte("a"), 2, false, ""); err != nil { t.Fatal(err) } else { checkTestScan(t, v, "b", "c") } - if v, err := db.Scan(nil, 3, true, ""); err != nil { + if v, err := db.Scan(KV, nil, 3, true, ""); err != nil { t.Fatal(err) } else { checkTestScan(t, v, "a", "b", "c") } - if v, err := db.Scan(nil, 3, true, "b"); err != nil { + if v, err := db.Scan(KV, nil, 3, true, "b"); err != nil { t.Fatal(err) } else { checkTestScan(t, v, "b") } - if v, err := db.Scan(nil, 3, true, "."); err != nil { + if v, err := db.Scan(KV, nil, 3, true, "."); err != nil { t.Fatal(err) } else { checkTestScan(t, v, "a", "b", "c") } - if v, err := db.Scan(nil, 3, true, "a+"); err != nil { + if v, err := db.Scan(KV, nil, 3, true, "a+"); err != nil { t.Fatal(err) } else { checkTestScan(t, v, "a") } - if v, err := db.RevScan(nil, 1, true, ""); err != nil { + if v, err := db.RevScan(KV, nil, 1, true, ""); err != nil { t.Fatal(err) } else { checkTestScan(t, v, "c") } - if v, err := db.RevScan([]byte("c"), 2, false, ""); err != nil { + if v, err := db.RevScan(KV, []byte("c"), 2, false, ""); err != nil { t.Fatal(err) } else { checkTestScan(t, v, "b", "a") } - if v, err := db.RevScan(nil, 3, true, ""); err != nil { + if v, err := db.RevScan(KV, nil, 3, true, ""); err != nil { t.Fatal(err) } else { checkTestScan(t, v, "c", "b", "a") } - if v, err := db.RevScan(nil, 3, true, "b"); err != nil { + if v, err := db.RevScan(KV, nil, 3, true, "b"); err != nil { t.Fatal(err) } else { checkTestScan(t, v, "b") } - if v, err := db.RevScan(nil, 3, true, "."); err != nil { + if v, err := db.RevScan(KV, nil, 3, true, "."); err != nil { t.Fatal(err) } else { checkTestScan(t, v, "c", "b", "a") } - if v, err := db.RevScan(nil, 3, true, "c+"); err != nil { + if v, err := db.RevScan(KV, nil, 3, true, "c+"); err != nil { t.Fatal(err) } else { checkTestScan(t, v, "c") @@ -125,7 +125,7 @@ func TestDBHScan(t *testing.T) { k3 := []byte("k3") db.HSet(k3, []byte("3"), []byte{}) - if v, err := db.HScan(nil, 1, true, ""); err != nil { + if v, err := db.Scan(HASH, nil, 1, true, ""); err != nil { t.Fatal(err) } else if len(v) != 1 { t.Fatal("invalid length ", len(v)) @@ -133,7 +133,7 @@ func TestDBHScan(t *testing.T) { t.Fatal("invalid value ", string(v[0])) } - if v, err := db.HScan(k1, 2, true, ""); err != nil { + if v, err := db.Scan(HASH, k1, 2, true, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -143,7 +143,7 @@ func TestDBHScan(t *testing.T) { t.Fatal("invalid value ", string(v[1])) } - if v, err := db.HScan(k1, 2, false, ""); err != nil { + if v, err := db.Scan(HASH, k1, 2, false, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -169,7 +169,7 @@ func TestDBZScan(t *testing.T) { k3 := []byte("k3") db.ZAdd(k3, ScorePair{3, []byte("m")}) - if v, err := db.ZScan(nil, 1, true, ""); err != nil { + if v, err := db.Scan(ZSET, nil, 1, true, ""); err != nil { t.Fatal(err) } else if len(v) != 1 { t.Fatal("invalid length ", len(v)) @@ -177,7 +177,7 @@ func TestDBZScan(t *testing.T) { t.Fatal("invalid value ", string(v[0])) } - if v, err := db.ZScan(k1, 2, true, ""); err != nil { + if v, err := db.Scan(ZSET, k1, 2, true, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -187,7 +187,7 @@ func TestDBZScan(t *testing.T) { t.Fatal("invalid value ", string(v[1])) } - if v, err := db.ZScan(k1, 2, false, ""); err != nil { + if v, err := db.Scan(ZSET, k1, 2, false, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -219,7 +219,7 @@ func TestDBLScan(t *testing.T) { t.Fatal(err.Error()) } - if v, err := db.LScan(nil, 1, true, ""); err != nil { + if v, err := db.Scan(LIST, nil, 1, true, ""); err != nil { t.Fatal(err) } else if len(v) != 1 { t.Fatal("invalid length ", len(v)) @@ -227,7 +227,7 @@ func TestDBLScan(t *testing.T) { t.Fatal("invalid value ", string(v[0])) } - if v, err := db.LScan(k1, 2, true, ""); err != nil { + if v, err := db.Scan(LIST, k1, 2, true, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -237,7 +237,7 @@ func TestDBLScan(t *testing.T) { t.Fatal("invalid value ", string(v[1])) } - if v, err := db.LScan(k1, 2, false, ""); err != nil { + if v, err := db.Scan(LIST, k1, 2, false, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -249,56 +249,6 @@ func TestDBLScan(t *testing.T) { } -func TestDBBScan(t *testing.T) { - // db := getTestDB() - - // db.bFlush() - - // k1 := []byte("k1") - // if _, err := db.BSetBit(k1, 1, 1); err != nil { - // t.Fatal(err.Error()) - // } - - // k2 := []byte("k2") - // if _, err := db.BSetBit(k2, 1, 1); err != nil { - // t.Fatal(err.Error()) - // } - // k3 := []byte("k3") - - // if _, err := db.BSetBit(k3, 1, 0); err != nil { - // t.Fatal(err.Error()) - // } - - // if v, err := db.BScan(nil, 1, true, ""); err != nil { - // t.Fatal(err) - // } else if len(v) != 1 { - // t.Fatal("invalid length ", len(v)) - // } else if string(v[0]) != "k1" { - // t.Fatal("invalid value ", string(v[0])) - // } - - // if v, err := db.BScan(k1, 2, true, ""); err != nil { - // t.Fatal(err) - // } else if len(v) != 2 { - // t.Fatal("invalid length ", len(v)) - // } else if string(v[0]) != "k1" { - // t.Fatal("invalid value ", string(v[0])) - // } else if string(v[1]) != "k2" { - // t.Fatal("invalid value ", string(v[1])) - // } - - // if v, err := db.BScan(k1, 2, false, ""); err != nil { - // t.Fatal(err) - // } else if len(v) != 2 { - // t.Fatal("invalid length ", len(v)) - // } else if string(v[0]) != "k2" { - // t.Fatal("invalid value ", string(v[0])) - // } else if string(v[1]) != "k3" { - // t.Fatal("invalid value ", string(v[1])) - // } - -} - func TestDBSScan(t *testing.T) { db := getTestDB() @@ -319,7 +269,7 @@ func TestDBSScan(t *testing.T) { t.Fatal(err.Error()) } - if v, err := db.SScan(nil, 1, true, ""); err != nil { + if v, err := db.Scan(SET, nil, 1, true, ""); err != nil { t.Fatal(err) } else if len(v) != 1 { t.Fatal("invalid length ", len(v)) @@ -327,7 +277,7 @@ func TestDBSScan(t *testing.T) { t.Fatal("invalid value ", string(v[0])) } - if v, err := db.SScan(k1, 2, true, ""); err != nil { + if v, err := db.Scan(SET, k1, 2, true, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -337,7 +287,7 @@ func TestDBSScan(t *testing.T) { t.Fatal("invalid value ", string(v[1])) } - if v, err := db.SScan(k1, 2, false, ""); err != nil { + if v, err := db.Scan(SET, k1, 2, false, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) diff --git a/ledis/t_bit.go b/ledis/t_bit.go index cff51d6..d89f15b 100644 --- a/ledis/t_bit.go +++ b/ledis/t_bit.go @@ -922,16 +922,6 @@ func (db *DB) BPersist(key []byte) (int64, error) { return n, err } -func (db *DB) BScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - log.Error("bitmap type will be deprecated later, please use bit operations in kv type") - return db.scan(BitMetaType, key, count, inclusive, match) -} - -func (db *DB) BRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - log.Error("bitmap type will be deprecated later, please use bit operations in kv type") - return db.revscan(BitMetaType, key, count, inclusive, match) -} - func (db *DB) bFlush() (drop int64, err error) { t := db.binBatch t.Lock() diff --git a/ledis/t_hash.go b/ledis/t_hash.go index 6235acc..3a0133a 100644 --- a/ledis/t_hash.go +++ b/ledis/t_hash.go @@ -460,14 +460,6 @@ func (db *DB) hFlush() (drop int64, err error) { return db.flushType(t, HashType) } -func (db *DB) HScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.scan(HSizeType, key, count, inclusive, match) -} - -func (db *DB) HRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.revscan(HSizeType, key, count, inclusive, match) -} - func (db *DB) HExpire(key []byte, duration int64) (int64, error) { if duration <= 0 { return 0, errExpireValue diff --git a/ledis/t_hash_test.go b/ledis/t_hash_test.go index 7fac691..6afa2da 100644 --- a/ledis/t_hash_test.go +++ b/ledis/t_hash_test.go @@ -114,7 +114,7 @@ func TestHFlush(t *testing.T) { } } - if v, err := db.HScan(nil, 3000, true, ""); err != nil { + if v, err := db.Scan(HASH, nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 2000 { t.Fatal("invalid value ", len(v)) @@ -135,7 +135,7 @@ func TestHFlush(t *testing.T) { t.Fatal("invalid value ", n) } - if v, err := db.HScan(nil, 3000, true, ""); err != nil { + if v, err := db.Scan(HASH, nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 0 { t.Fatal("invalid value length ", len(v)) diff --git a/ledis/t_kv.go b/ledis/t_kv.go index 398d2a8..eb2f1fe 100644 --- a/ledis/t_kv.go +++ b/ledis/t_kv.go @@ -347,16 +347,6 @@ func (db *DB) flush() (drop int64, err error) { return db.flushType(t, KVType) } -//if inclusive is true, scan range [key, inf) else (key, inf) -func (db *DB) Scan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.scan(KVType, key, count, inclusive, match) -} - -//if inclusive is true, revscan range (-inf, key] else (inf, key) -func (db *DB) RevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.revscan(KVType, key, count, inclusive, match) -} - func (db *DB) Expire(key []byte, duration int64) (int64, error) { if duration <= 0 { return 0, errExpireValue diff --git a/ledis/t_kv_test.go b/ledis/t_kv_test.go index 63ea052..ffbac81 100644 --- a/ledis/t_kv_test.go +++ b/ledis/t_kv_test.go @@ -282,7 +282,7 @@ func TestKVFlush(t *testing.T) { } } - if v, err := db.Scan(nil, 3000, true, ""); err != nil { + if v, err := db.Scan(KV, nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 2000 { t.Fatal("invalid value ", len(v)) @@ -303,7 +303,7 @@ func TestKVFlush(t *testing.T) { t.Fatal("invalid value ", n) } - if v, err := db.Scan(nil, 3000, true, ""); err != nil { + if v, err := db.Scan(KV, nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 0 { t.Fatal("invalid value length ", len(v)) diff --git a/ledis/t_list.go b/ledis/t_list.go index 93c6ba2..c2cc5a9 100644 --- a/ledis/t_list.go +++ b/ledis/t_list.go @@ -480,14 +480,6 @@ func (db *DB) LPersist(key []byte) (int64, error) { return n, err } -func (db *DB) LScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.scan(LMetaType, key, count, inclusive, match) -} - -func (db *DB) LRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.revscan(LMetaType, key, count, inclusive, match) -} - func (db *DB) lEncodeMinKey() []byte { return db.lEncodeMetaKey(nil) } diff --git a/ledis/t_list_test.go b/ledis/t_list_test.go index b80af1d..58c1c58 100644 --- a/ledis/t_list_test.go +++ b/ledis/t_list_test.go @@ -145,7 +145,7 @@ func TestLFlush(t *testing.T) { } } - if v, err := db.LScan(nil, 3000, true, ""); err != nil { + if v, err := db.Scan(LIST, nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 2000 { t.Fatal("invalid value ", len(v)) @@ -157,7 +157,7 @@ func TestLFlush(t *testing.T) { t.Fatal("invalid value ", n) } - if v, err := db.LScan(nil, 3000, true, ""); err != nil { + if v, err := db.Scan(LIST, nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 0 { t.Fatal("invalid value length ", len(v)) diff --git a/ledis/t_set.go b/ledis/t_set.go index 126b407..200d66b 100644 --- a/ledis/t_set.go +++ b/ledis/t_set.go @@ -607,11 +607,3 @@ func (db *DB) SPersist(key []byte) (int64, error) { err = t.Commit() return n, err } - -func (db *DB) SScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.scan(SSizeType, key, count, inclusive, match) -} - -func (db *DB) SRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.revscan(SSizeType, key, count, inclusive, match) -} diff --git a/ledis/t_set_test.go b/ledis/t_set_test.go index eb24bdb..b6b1460 100644 --- a/ledis/t_set_test.go +++ b/ledis/t_set_test.go @@ -352,7 +352,7 @@ func TestSFlush(t *testing.T) { } } - if v, err := db.SScan(nil, 3000, true, ""); err != nil { + if v, err := db.Scan(SET, nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 2000 { t.Fatal("invalid value ", len(v)) @@ -364,7 +364,7 @@ func TestSFlush(t *testing.T) { t.Fatal("invalid value ", n) } - if v, err := db.SScan(nil, 3000, true, ""); err != nil { + if v, err := db.Scan(SET, nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 0 { t.Fatal("invalid value length ", len(v)) diff --git a/ledis/t_zset.go b/ledis/t_zset.go index 36f886b..0bd22a3 100644 --- a/ledis/t_zset.go +++ b/ledis/t_zset.go @@ -936,14 +936,6 @@ func (db *DB) ZInterStore(destKey []byte, srcKeys [][]byte, weights []int64, agg return n, nil } -func (db *DB) ZScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.scan(ZSizeType, key, count, inclusive, match) -} - -func (db *DB) ZRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.revscan(ZSizeType, key, count, inclusive, match) -} - func (db *DB) ZRangeByLex(key []byte, min []byte, max []byte, rangeType uint8, offset int, count int) ([][]byte, error) { if min == nil { min = db.zEncodeStartSetKey(key) diff --git a/ledis/t_zset_test.go b/ledis/t_zset_test.go index cdb35bc..6323091 100644 --- a/ledis/t_zset_test.go +++ b/ledis/t_zset_test.go @@ -391,7 +391,7 @@ func TestZScan(t *testing.T) { } } - if v, err := db.ZScan(nil, 3000, true, ""); err != nil { + if v, err := db.Scan(ZSET, nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 2000 { t.Fatal("invalid value ", len(v)) @@ -403,7 +403,7 @@ func TestZScan(t *testing.T) { t.Fatal("invalid value ", n) } - if v, err := db.ZScan(nil, 3000, true, ""); err != nil { + if v, err := db.Scan(ZSET, nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 0 { t.Fatal("invalid value length ", len(v)) diff --git a/server/cmd_bit.go b/server/cmd_bit.go index ac577ea..45d6ede 100644 --- a/server/cmd_bit.go +++ b/server/cmd_bit.go @@ -274,14 +274,6 @@ func bpersistCommand(c *client) error { return nil } -func bxscanCommand(c *client) error { - return xscanGeneric(c, c.db.BScan) -} - -func bxrevscanCommand(c *client) error { - return xscanGeneric(c, c.db.BRevScan) -} - func init() { register("bget", bgetCommand) register("bdelete", bdeleteCommand) @@ -294,8 +286,4 @@ func init() { register("bexpireat", bexpireAtCommand) register("bttl", bttlCommand) register("bpersist", bpersistCommand) - register("bxscan", bxscanCommand) - register("bxrevscan", bxrevscanCommand) - register("xbscan", bxscanCommand) - register("xbrevscan", bxrevscanCommand) } diff --git a/server/cmd_hash.go b/server/cmd_hash.go index 8c277ee..b9acc85 100644 --- a/server/cmd_hash.go +++ b/server/cmd_hash.go @@ -292,14 +292,6 @@ func hpersistCommand(c *client) error { return nil } -func hxscanCommand(c *client) error { - return xscanGeneric(c, c.db.HScan) -} - -func hxrevscanCommand(c *client) error { - return xscanGeneric(c, c.db.HRevScan) -} - func xhexistsCommand(c *client) error { args := c.args if len(args) != 1 { @@ -334,9 +326,5 @@ func init() { register("hexpireat", hexpireAtCommand) register("httl", httlCommand) register("hpersist", hpersistCommand) - register("hxscan", hxscanCommand) - register("hxrevscan", hxrevscanCommand) - register("xhscan", hxscanCommand) - register("xhrevscan", hxrevscanCommand) register("xhexists", xhexistsCommand) } diff --git a/server/cmd_kv.go b/server/cmd_kv.go index 45a4b7a..25d7bb9 100644 --- a/server/cmd_kv.go +++ b/server/cmd_kv.go @@ -1,10 +1,8 @@ package server import ( - "github.com/siddontang/go/hack" "github.com/siddontang/ledisdb/ledis" "strconv" - "strings" ) // func getCommand(c *client) error { @@ -315,82 +313,6 @@ func persistCommand(c *client) error { return nil } -func parseScanArgs(c *client) (key []byte, match string, count int, err error) { - args := c.args - count = 10 - - switch len(args) { - case 0: - key = nil - return - case 1, 3, 5: - key = args[0] - break - default: - err = ErrCmdParams - return - } - - if len(args) == 3 { - switch strings.ToLower(hack.String(args[1])) { - case "match": - match = hack.String(args[2]) - case "count": - count, err = strconv.Atoi(hack.String(args[2])) - default: - err = ErrCmdParams - return - } - } else if len(args) == 5 { - if strings.ToLower(hack.String(args[1])) != "match" { - err = ErrCmdParams - return - } else if strings.ToLower(hack.String(args[3])) != "count" { - err = ErrCmdParams - return - } - - match = hack.String(args[2]) - count, err = strconv.Atoi(hack.String(args[4])) - } - - if count <= 0 { - err = ErrCmdParams - } - - return -} - -func xscanGeneric(c *client, - f func(key []byte, count int, inclusive bool, match string) ([][]byte, error)) error { - key, match, count, err := parseScanArgs(c) - if err != nil { - return err - } - - if ay, err := f(key, count, false, match); err != nil { - return err - } else { - data := make([]interface{}, 2) - if len(ay) < count { - data[0] = []byte("") - } else { - data[0] = ay[len(ay)-1] - } - data[1] = ay - c.resp.writeArray(data) - } - return nil -} - -func xscanCommand(c *client) error { - return xscanGeneric(c, c.db.Scan) -} - -func xrevscanCommand(c *client) error { - return xscanGeneric(c, c.db.RevScan) -} - func appendCommand(c *client) error { args := c.args if len(args) != 2 { @@ -619,6 +541,4 @@ func init() { register("expireat", expireAtCommand) register("ttl", ttlCommand) register("persist", persistCommand) - register("xscan", xscanCommand) - register("xrevscan", xrevscanCommand) } diff --git a/server/cmd_list.go b/server/cmd_list.go index 7000a74..d3ac6e2 100644 --- a/server/cmd_list.go +++ b/server/cmd_list.go @@ -231,14 +231,6 @@ func lpersistCommand(c *client) error { return nil } -func lxscanCommand(c *client) error { - return xscanGeneric(c, c.db.LScan) -} - -func lxrevscanCommand(c *client) error { - return xscanGeneric(c, c.db.LRevScan) -} - func blpopCommand(c *client) error { keys, timeout, err := lParseBPopArgs(c) if err != nil { @@ -317,9 +309,5 @@ func init() { register("lexpireat", lexpireAtCommand) register("lttl", lttlCommand) register("lpersist", lpersistCommand) - register("lxscan", lxscanCommand) - register("lxrevscan", lxrevscanCommand) - register("xlscan", lxscanCommand) - register("xlrevscan", lxrevscanCommand) register("xlexists", xlexistsCommand) } diff --git a/server/cmd_migrate.go b/server/cmd_migrate.go index 3153821..a162328 100644 --- a/server/cmd_migrate.go +++ b/server/cmd_migrate.go @@ -165,15 +165,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": - return db.Scan(nil, count, false, "") + return db.Scan(KV, nil, count, false, "") case "HASH": - return db.HScan(nil, count, false, "") + return db.Scan(HASH, nil, count, false, "") case "LIST": - return db.LScan(nil, count, false, "") + return db.Scan(LIST, nil, count, false, "") case "SET": - return db.SScan(nil, count, false, "") + return db.Scan(SET, nil, count, false, "") case "ZSET": - return db.ZScan(nil, count, false, "") + return db.Scan(ZSET, nil, count, false, "") default: return nil, fmt.Errorf("invalid key type %s", tp) } diff --git a/server/cmd_replication_test.go b/server/cmd_replication_test.go index 530d7db..66b99c7 100644 --- a/server/cmd_replication_test.go +++ b/server/cmd_replication_test.go @@ -14,8 +14,8 @@ func checkDataEqual(master *App, slave *App) error { mdb, _ := master.ldb.Select(0) sdb, _ := slave.ldb.Select(0) - mkeys, _ := mdb.Scan(nil, 100, true, "") - skeys, _ := sdb.Scan(nil, 100, true, "") + mkeys, _ := mdb.Scan(KV, nil, 100, true, "") + skeys, _ := sdb.Scan(KV, nil, 100, true, "") if len(mkeys) != len(skeys) { return fmt.Errorf("keys number not equal %d != %d", len(mkeys), len(skeys)) diff --git a/server/cmd_scan.go b/server/cmd_scan.go new file mode 100644 index 0000000..e4f6f7f --- /dev/null +++ b/server/cmd_scan.go @@ -0,0 +1,101 @@ +package server + +import ( + "fmt" + "github.com/siddontang/go/hack" + "github.com/siddontang/ledisdb/ledis" + "strconv" + "strings" +) + +// XSCAN type cursor [MATCH match] [COUNT count] [ASC|DESC] +func xscanCommand(c *client) error { + args := c.args + + if len(args) < 2 { + return ErrCmdParams + } + + var dataType ledis.DataType + switch strings.ToUpper(hack.String(args[0])) { + case "KV": + dataType = ledis.KV + case "HASH": + dataType = ledis.HASH + case "LIST": + dataType = ledis.LIST + case "SET": + dataType = ledis.SET + case "ZSET": + dataType = ledis.ZSET + default: + return fmt.Errorf("invalid key type %s", args[0]) + } + + cursor := args[1] + + args = args[2:] + + match := "" + count := 10 + + desc := false + + var err error + + for i := 0; i < len(args); { + switch strings.ToUpper(hack.String(args[i])) { + case "MATCH": + if i+1 >= len(args) { + return ErrCmdParams + } + + match = hack.String(args[i+1]) + i = i + 2 + case "COUNT": + if i+1 >= len(args) { + return ErrCmdParams + } + + count, err = strconv.Atoi(hack.String(args[i+1])) + if err != nil { + return err + } + + i = i + 2 + case "ASC": + desc = false + i++ + case "DESC": + desc = true + i++ + default: + return fmt.Errorf("invalid argument %s", args[i]) + } + } + + 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 + } + + data := make([]interface{}, 2) + if len(ay) < count { + data[0] = []byte("") + } else { + data[0] = ay[len(ay)-1] + } + data[1] = ay + c.resp.writeArray(data) + return nil +} + +func init() { + register("xscan", xscanCommand) +} diff --git a/server/scan_test.go b/server/cmd_scan_test.go similarity index 81% rename from server/scan_test.go rename to server/cmd_scan_test.go index 1778416..66d34d3 100644 --- a/server/scan_test.go +++ b/server/cmd_scan_test.go @@ -53,8 +53,8 @@ func checkScanValues(t *testing.T, ay interface{}, values ...int) { } } -func checkScan(t *testing.T, c *ledis.Client, cmd string) { - if ay, err := ledis.Values(c.Do(cmd, "", "count", 5)); err != nil { +func checkScan(t *testing.T, c *ledis.Client, tp string) { + if ay, err := ledis.Values(c.Do("XSCAN", tp, "", "count", 5)); err != nil { t.Fatal(err) } else if len(ay) != 2 { t.Fatal(len(ay)) @@ -64,7 +64,7 @@ func checkScan(t *testing.T, c *ledis.Client, cmd string) { checkScanValues(t, ay[1], 0, 1, 2, 3, 4) } - if ay, err := ledis.Values(c.Do(cmd, "4", "count", 6)); err != nil { + if ay, err := ledis.Values(c.Do("XSCAN", tp, "4", "count", 6)); err != nil { t.Fatal(err) } else if len(ay) != 2 { t.Fatal(len(ay)) @@ -76,8 +76,8 @@ func checkScan(t *testing.T, c *ledis.Client, cmd string) { } -func checkRevScan(t *testing.T, c *ledis.Client, cmd string) { - if ay, err := ledis.Values(c.Do(cmd, "", "count", 5)); err != nil { +func checkRevScan(t *testing.T, c *ledis.Client, tp string) { + if ay, err := ledis.Values(c.Do("XSCAN", tp, "", "count", 5, "DESC")); err != nil { t.Fatal(err) } else if len(ay) != 2 { t.Fatal(len(ay)) @@ -87,7 +87,7 @@ func checkRevScan(t *testing.T, c *ledis.Client, cmd string) { checkScanValues(t, ay[1], 9, 8, 7, 6, 5) } - if ay, err := ledis.Values(c.Do(cmd, "5", "count", 6)); err != nil { + if ay, err := ledis.Values(c.Do("XSCAN", tp, "5", "count", 6, "DESC")); err != nil { t.Fatal(err) } else if len(ay) != 2 { t.Fatal(len(ay)) @@ -106,8 +106,8 @@ func testKVScan(t *testing.T, c *ledis.Client) { } } - checkScan(t, c, "xscan") - checkRevScan(t, c, "xrevscan") + checkScan(t, c, "KV") + checkRevScan(t, c, "KV") } func testHashScan(t *testing.T, c *ledis.Client) { @@ -117,8 +117,8 @@ func testHashScan(t *testing.T, c *ledis.Client) { } } - checkScan(t, c, "xhscan") - checkRevScan(t, c, "xhrevscan") + checkScan(t, c, "HASH") + checkRevScan(t, c, "HASH") } func testListScan(t *testing.T, c *ledis.Client) { @@ -128,8 +128,8 @@ func testListScan(t *testing.T, c *ledis.Client) { } } - checkScan(t, c, "xlscan") - checkRevScan(t, c, "xlrevscan") + checkScan(t, c, "LIST") + checkRevScan(t, c, "LIST") } func testZSetScan(t *testing.T, c *ledis.Client) { @@ -139,8 +139,8 @@ func testZSetScan(t *testing.T, c *ledis.Client) { } } - checkScan(t, c, "zxscan") - checkRevScan(t, c, "zxrevscan") + checkScan(t, c, "ZSET") + checkRevScan(t, c, "ZSET") } func testSetScan(t *testing.T, c *ledis.Client) { @@ -150,6 +150,6 @@ func testSetScan(t *testing.T, c *ledis.Client) { } } - checkScan(t, c, "xsscan") - checkRevScan(t, c, "xsrevscan") + checkScan(t, c, "SET") + checkRevScan(t, c, "SET") } diff --git a/server/cmd_set.go b/server/cmd_set.go index ae22bc5..b2ee2dc 100644 --- a/server/cmd_set.go +++ b/server/cmd_set.go @@ -262,14 +262,6 @@ func spersistCommand(c *client) error { return nil } -func sxscanCommand(c *client) error { - return xscanGeneric(c, c.db.SScan) -} - -func sxrevscanCommand(c *client) error { - return xscanGeneric(c, c.db.SRevScan) -} - func xsexistsCommand(c *client) error { args := c.args if len(args) != 1 { @@ -301,10 +293,6 @@ func init() { register("sexpireat", sexpireAtCommand) register("sttl", sttlCommand) register("spersist", spersistCommand) - register("sxscan", sxscanCommand) - register("sxrevscan", sxrevscanCommand) - register("xsscan", sxscanCommand) - register("xsrevscan", sxrevscanCommand) register("xsexists", xsexistsCommand) } diff --git a/server/cmd_zset.go b/server/cmd_zset.go index ddeefbd..77d2db3 100644 --- a/server/cmd_zset.go +++ b/server/cmd_zset.go @@ -641,14 +641,6 @@ func zinterstoreCommand(c *client) error { return err } -func zxscanCommand(c *client) error { - return xscanGeneric(c, c.db.ZScan) -} - -func zxrevscanCommand(c *client) error { - return xscanGeneric(c, c.db.ZRevScan) -} - func zparseMemberRange(minBuf []byte, maxBuf []byte) (min []byte, max []byte, rangeType uint8, err error) { rangeType = store.RangeClose if strings.ToLower(hack.String(minBuf)) == "-" { @@ -815,9 +807,5 @@ func init() { register("zexpireat", zexpireAtCommand) register("zttl", zttlCommand) register("zpersist", zpersistCommand) - register("zxscan", zxscanCommand) - register("zxrevscan", zxrevscanCommand) - register("xzscan", zxscanCommand) - register("xzrevscan", zxrevscanCommand) register("xzexists", xzexistsCommand) } diff --git a/server/const.go b/server/const.go index dc55e24..9804ad7 100644 --- a/server/const.go +++ b/server/const.go @@ -2,6 +2,7 @@ package server import ( "errors" + "github.com/siddontang/ledisdb/ledis" ) var ( @@ -26,12 +27,11 @@ var ( ) const ( - KV = iota - LIST - HASH - SET - ZSET - BIT + KV ledis.DataType = ledis.KV + LIST = ledis.LIST + HASH = ledis.HASH + SET = ledis.SET + ZSET = ledis.ZSET ) const (