From 65ddeb8506ff654b5d68b3b046dc592c22e6fd92 Mon Sep 17 00:00:00 2001 From: karthick Date: Sun, 11 Aug 2024 21:37:34 +0530 Subject: [PATCH] added support for RANDOMKEY --- core/commands.go | 7 +++++ core/eval.go | 11 ++++++++ core/store.go | 16 +++++++++++ tests/commands_test.go | 60 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+) create mode 100644 tests/commands_test.go diff --git a/core/commands.go b/core/commands.go index 4c90734a06..ec1afaf4f7 100644 --- a/core/commands.go +++ b/core/commands.go @@ -510,6 +510,12 @@ var ( Arity: 3, KeySpecs: KeySpecs{BeginIndex: 1}, } + randomKeyCmdMeta = DiceCmdMeta{ + Name: "RANDOMKEY", + Info: "Return a random key", + Eval: evalRandomKey, + Arity: 1, + } ) func init() { @@ -570,4 +576,5 @@ func init() { diceCmds["GETDEL"] = getDelCmdMeta diceCmds["DECRBY"] = decrByCmdMeta diceCmds["RENAME"] = renameCmdMeta + diceCmds["RANDOMKEY"] = randomKeyCmdMeta } diff --git a/core/eval.go b/core/eval.go index bfa177b09a..6d7e567be8 100644 --- a/core/eval.go +++ b/core/eval.go @@ -1688,6 +1688,17 @@ func evalEXISTS(args []string) []byte { return Encode(count, false) } +func evalRandomKey(args []string) []byte { + if len(args) != 0 { + return Encode(errors.New("ERR wrong number of arguments for RANDOMKEY command"), false) + } + + key := RandomKey() + if key == "" { + return RESP_NIL + } + return Encode(key, false) +} func executeCommand(cmd *RedisCmd, c *Client) []byte { diceCmd, ok := diceCmds[cmd.Cmd] diff --git a/core/store.go b/core/store.go index bfbe4f1ae0..6efa62637f 100644 --- a/core/store.go +++ b/core/store.go @@ -1,6 +1,7 @@ package core import ( + "math/rand" "path" "sync" "time" @@ -328,3 +329,18 @@ func delByPtr(ptr unsafe.Pointer) bool { } return false } + +func RandomKey() string { + if len(keypool) == 0 { + return "" + } + index := rand.Intn(len(keypool)) + currentIndex := 0 + for key := range keypool { + if currentIndex == index { + return key + } + currentIndex++ + } + return "" +} diff --git a/tests/commands_test.go b/tests/commands_test.go new file mode 100644 index 0000000000..d7c3d64c7f --- /dev/null +++ b/tests/commands_test.go @@ -0,0 +1,60 @@ +package tests + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestCommandRandomKey(t *testing.T) { + var randomKeyTestCases = []struct { + name string + inCmd []string + expected []interface{} + }{ + { + name: "Try to get RandomKey with 0 keys in map", + inCmd: []string{"RANDOMKEY"}, + expected: []interface{}{"(nil)"}, + }, + { + name: "Set a key and get a RandomKey", + inCmd: []string{"set Key hello", "RANDOMKEY"}, + expected: []interface{}{"OK", "Key"}, + }, + { + name: "Set another two keys and check the RandomKey", + inCmd: []string{"set Key2 hello2", "set Key3 hello3", "RANDOMKEY"}, + expected: []interface{}{"OK", "OK"}, + }, + } + + conn := getLocalConnection() + defer conn.Close() + + for _, tc := range randomKeyTestCases { + t.Run(tc.name, func(t *testing.T) { + var actual []interface{} + for _, cmd := range tc.inCmd { + result := fireCommand(conn, cmd) + actual = append(actual, result) + } + + if tc.name == "Set another two keys and check the RandomKey" { + // Manually check if the last result is one of the expected keys + lastResult := actual[len(actual)-1] + expectedKeys := []string{"Key", "Key2", "Key3"} + found := false + for _, key := range expectedKeys { + if lastResult == key { + found = true + break + } + } + assert.Assert(t, found, "Expected one of %v, but got %v", expectedKeys, lastResult) + } else { + assert.DeepEqual(t, tc.expected, actual) + } + }) + } +}