add scan command

This commit is contained in:
lhpqaq
2024-07-17 10:31:49 +08:00
committed by finley
parent ff67ac3bb0
commit c1dd65d84f
8 changed files with 271 additions and 11 deletions

View File

@@ -1,6 +1,7 @@
package dict
import (
"github.com/hdt3213/godis/lib/wildcard"
"math"
"math/rand"
"sort"
@@ -435,3 +436,47 @@ func (dict *ConcurrentDict) RWUnLocks(writeKeys []string, readKeys []string) {
}
}
}
func stringsToBytes(strSlice []string) [][]byte {
byteSlice := make([][]byte, len(strSlice))
for i, str := range strSlice {
byteSlice[i] = []byte(str)
}
return byteSlice
}
func (dict *ConcurrentDict) DictScan(cursor int, count int, pattern string) ([][]byte, int) {
size := dict.Len()
result := make([][]byte, 0)
if pattern == "*" && count >= size {
return stringsToBytes(dict.Keys()), 0
}
matchKey, err := wildcard.CompilePattern(pattern)
if err != nil {
return result, -1
}
shardCount := len(dict.table)
shardIndex := cursor
for shardIndex < shardCount {
shard := dict.table[shardIndex]
shard.mutex.RLock()
if len(result)+len(shard.m) > count && shardIndex > cursor {
shard.mutex.RUnlock()
return result, shardIndex
}
for key := range shard.m {
if pattern == "*" || matchKey.IsMatch(key) {
result = append(result, []byte(key))
}
}
shard.mutex.RUnlock()
shardIndex++
}
return result, 0
}

View File

@@ -465,7 +465,7 @@ func TestConcurrentRemoveWithLock(t *testing.T) {
}
}
//change t.Error remove->forEach
// change t.Error remove->forEach
func TestConcurrentForEach(t *testing.T) {
d := MakeConcurrent(0)
size := 100
@@ -524,3 +524,51 @@ func TestConcurrentDict_Keys(t *testing.T) {
t.Errorf("expect %d keys, actual: %d", size, len(d.Keys()))
}
}
func TestDictScan(t *testing.T) {
d := MakeConcurrent(0)
count := 100
for i := 0; i < count; i++ {
key := "kkk" + strconv.Itoa(i)
d.Put(key, i)
}
for i := 0; i < count; i++ {
key := "key" + strconv.Itoa(i)
d.Put(key, i)
}
cursor := 0
matchKey := "*"
c := 20
result := make([][]byte, 0)
var returnKeys [][]byte
for {
returnKeys, cursor = d.DictScan(cursor, c, matchKey)
result = append(result, returnKeys...)
if cursor == 0 {
break
}
}
result = utils.RemoveDuplicates(result)
if len(result) != count*2 {
t.Errorf("scan command result number error: %d, should be %d ", len(result), count*2)
}
matchKey = "key*"
cursor = 0
mresult := make([][]byte, 0)
for {
returnKeys, cursor = d.DictScan(cursor, c, matchKey)
mresult = append(mresult, returnKeys...)
if cursor == 0 {
break
}
}
mresult = utils.RemoveDuplicates(mresult)
if len(mresult) != count {
t.Errorf("scan command result number error: %d, should be %d ", len(mresult), count)
}
matchKey = "no*"
returnKeys, _ = d.DictScan(cursor, c, matchKey)
if len(returnKeys) != 0 {
t.Errorf("returnKeys should be empty")
}
}

View File

@@ -16,4 +16,5 @@ type Dict interface {
RandomKeys(limit int) []string
RandomDistinctKeys(limit int) []string
Clear()
DictScan(cursor int, count int, pattern string) ([][]byte, int)
}

View File

@@ -120,3 +120,7 @@ func (dict *SimpleDict) RandomDistinctKeys(limit int) []string {
func (dict *SimpleDict) Clear() {
*dict = *MakeSimple()
}
func (dict *SimpleDict) DictScan(cursor int, count int, pattern string) ([][]byte, int) {
return stringsToBytes(dict.Keys()), 0
}