Skip to content

Commit

Permalink
GO Implement Dump, Restore and ObjectEncoding Command (valkey-io#2781)
Browse files Browse the repository at this point in the history
* GO Implement Dump and ObjectEncoding command

Signed-off-by: EdricCua <[email protected]>
  • Loading branch information
EdricCua authored Jan 17, 2025
1 parent 9995c23 commit b1a2c1f
Show file tree
Hide file tree
Showing 4 changed files with 307 additions and 0 deletions.
37 changes: 37 additions & 0 deletions go/api/base_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1995,3 +1995,40 @@ func (client *baseClient) XPendingWithOptions(
}
return handleXPendingDetailResponse(result)
}

func (client *baseClient) Restore(key string, ttl int64, value string) (Result[string], error) {
return client.RestoreWithOptions(key, ttl, value, NewRestoreOptionsBuilder())
}

func (client *baseClient) RestoreWithOptions(key string, ttl int64,
value string, options *RestoreOptions,
) (Result[string], error) {
optionArgs, err := options.toArgs()
if err != nil {
return CreateNilStringResult(), err
}
result, err := client.executeCommand(C.Restore, append([]string{
key,
utils.IntToString(ttl), value,
}, optionArgs...))
if err != nil {
return CreateNilStringResult(), err
}
return handleStringOrNilResponse(result)
}

func (client *baseClient) Dump(key string) (Result[string], error) {
result, err := client.executeCommand(C.Dump, []string{key})
if err != nil {
return CreateNilStringResult(), err
}
return handleStringOrNilResponse(result)
}

func (client *baseClient) ObjectEncoding(key string) (Result[string], error) {
result, err := client.executeCommand(C.ObjectEncoding, []string{key})
if err != nil {
return CreateNilStringResult(), err
}
return handleStringOrNilResponse(result)
}
77 changes: 77 additions & 0 deletions go/api/command_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,3 +278,80 @@ func (listDirection ListDirection) toString() (string, error) {
return "", &RequestError{"Invalid list direction"}
}
}

// Optional arguments to Restore(key string, ttl int64, value string, option *RestoreOptions)
//
// Note IDLETIME and FREQ modifiers cannot be set at the same time.
//
// [valkey.io]: https://valkey.io/commands/restore/
type RestoreOptions struct {
// Subcommand string to replace existing key.
replace string
// Subcommand string to represent absolute timestamp (in milliseconds) for TTL.
absTTL string
// It represents the idletime/frequency of object.
eviction Eviction
}

func NewRestoreOptionsBuilder() *RestoreOptions {
return &RestoreOptions{}
}

const (
// Subcommand string to replace existing key.
Replace_keyword = "REPLACE"

// Subcommand string to represent absolute timestamp (in milliseconds) for TTL.
ABSTTL_keyword string = "ABSTTL"
)

// Custom setter methods to replace existing key.
func (restoreOption *RestoreOptions) SetReplace() *RestoreOptions {
restoreOption.replace = Replace_keyword
return restoreOption
}

// Custom setter methods to represent absolute timestamp (in milliseconds) for TTL.
func (restoreOption *RestoreOptions) SetABSTTL() *RestoreOptions {
restoreOption.absTTL = ABSTTL_keyword
return restoreOption
}

// For eviction purpose, you may use IDLETIME or FREQ modifiers.
type Eviction struct {
// It represent IDLETIME or FREQ.
Type EvictionType
// It represents count(int) of the idletime/frequency of object.
Count int64
}

type EvictionType string

const (
// It represents the idletime of object
IDLETIME EvictionType = "IDLETIME"
// It represents the frequency of object
FREQ EvictionType = "FREQ"
)

// Custom setter methods set the idletime/frequency of object.
func (restoreOption *RestoreOptions) SetEviction(evictionType EvictionType, count int64) *RestoreOptions {
restoreOption.eviction.Type = evictionType
restoreOption.eviction.Count = count
return restoreOption
}

func (opts *RestoreOptions) toArgs() ([]string, error) {
args := []string{}
var err error
if opts.replace != "" {
args = append(args, string(opts.replace))
}
if opts.absTTL != "" {
args = append(args, string(opts.absTTL))
}
if (opts.eviction != Eviction{}) {
args = append(args, string(opts.eviction.Type), utils.IntToString(opts.eviction.Count))
}
return args, err
}
85 changes: 85 additions & 0 deletions go/api/generic_base_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,4 +442,89 @@ type GenericBaseCommands interface {
//
// [valkey.io]: https://valkey.io/commands/persist/
Persist(key string) (bool, error)

// Create a key associated with a value that is obtained by
// deserializing the provided serialized value (obtained via [valkey.io]: Https://valkey.io/commands/dump/).
//
// Parameters:
// key - The key to create.
// ttl - The expiry time (in milliseconds). If 0, the key will persist.
// value - The serialized value to deserialize and assign to key.
//
// Return value:
// Return OK if successfully create a key with a value </code>.
//
// Example:
// result, err := client.Restore("key",ttl, value)
// if err != nil {
// // handle error
// }
// fmt.Println(result.Value()) // Output: OK
//
// [valkey.io]: https://valkey.io/commands/restore/
Restore(key string, ttl int64, value string) (Result[string], error)

// Create a key associated with a value that is obtained by
// deserializing the provided serialized value (obtained via [valkey.io]: Https://valkey.io/commands/dump/).
//
// Parameters:
// key - The key to create.
// ttl - The expiry time (in milliseconds). If 0, the key will persist.
// value - The serialized value to deserialize and assign to key.
// restoreOptions - Set restore options with replace and absolute TTL modifiers, object idletime and frequency
//
// Return value:
// Return OK if successfully create a key with a value.
//
// Example:
// restoreOptions := api.NewRestoreOptionsBuilder().SetReplace().SetABSTTL().SetEviction(api.FREQ, 10)
// resultRestoreOpt, err := client.RestoreWithOptions(key, ttl, value, restoreOptions)
// if err != nil {
// // handle error
// }
// fmt.Println(result.Value()) // Output: OK
//
// [valkey.io]: https://valkey.io/commands/restore/
RestoreWithOptions(key string, ttl int64, value string, option *RestoreOptions) (Result[string], error)

// Returns the internal encoding for the Valkey object stored at key.
//
// Note:
// When in cluster mode, both key and newkey must map to the same hash slot.
//
// Parameters:
// The key of the object to get the internal encoding of.
//
// Return value:
// If key exists, returns the internal encoding of the object stored at
// key as a String. Otherwise, returns null.
//
// Example:
// result, err := client.ObjectEncoding("mykeyRenamenx")
// if err != nil {
// // handle error
// }
// fmt.Println(result.Value()) // Output: embstr
//
// [valkey.io]: https://valkey.io/commands/object-encoding/
ObjectEncoding(key string) (Result[string], error)

// Serialize the value stored at key in a Valkey-specific format and return it to the user.
//
// Parameters:
// The key to serialize.
//
// Return value:
// The serialized value of the data stored at key
// If key does not exist, null will be returned.
//
// Example:
// result, err := client.Dump([]string{"key"})
// if err != nil {
// // handle error
// }
// fmt.Println(result.Value()) // Output: (Serialized Value)
//
// [valkey.io]: https://valkey.io/commands/dump/
Dump(key string) (Result[string], error)
}
108 changes: 108 additions & 0 deletions go/integTest/shared_commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5716,3 +5716,111 @@ func (suite *GlideTestSuite) TestXPendingFailures() {
}
})
}

func (suite *GlideTestSuite) TestObjectEncoding() {
suite.runWithDefaultClients(func(client api.BaseClient) {
// Test 1: Check object encoding for embstr
key := "{keyName}" + uuid.NewString()
value1 := "Hello"
t := suite.T()
suite.verifyOK(client.Set(key, value1))
resultObjectEncoding, err := client.ObjectEncoding(key)
assert.Nil(t, err)
assert.Equal(t, "embstr", resultObjectEncoding.Value(), "The result should be embstr")

// Test 2: Check object encoding command for non existing key
key2 := "{keyName}" + uuid.NewString()
resultDumpNull, err := client.ObjectEncoding(key2)
assert.Nil(t, err)
assert.Equal(t, "", resultDumpNull.Value())
})
}

func (suite *GlideTestSuite) TestDumpRestore() {
suite.runWithDefaultClients(func(client api.BaseClient) {
// Test 1: Check restore command for deleted key and check value
key := "testKey1_" + uuid.New().String()
value := "hello"
t := suite.T()
suite.verifyOK(client.Set(key, value))
resultDump, err := client.Dump(key)
assert.Nil(t, err)
assert.NotNil(t, resultDump)
deletedCount, err := client.Del([]string{key})
assert.Nil(t, err)
assert.Equal(t, int64(1), deletedCount)
result_test1, err := client.Restore(key, int64(0), resultDump.Value())
assert.Nil(suite.T(), err)
assert.Equal(suite.T(), "OK", result_test1.Value())
resultGetRestoreKey, err := client.Get(key)
assert.Nil(t, err)
assert.Equal(t, value, resultGetRestoreKey.Value())

// Test 2: Check dump command for non existing key
key1 := "{keyName}" + uuid.NewString()
resultDumpNull, err := client.Dump(key1)
assert.Nil(t, err)
assert.Equal(t, "", resultDumpNull.Value())
})
}

func (suite *GlideTestSuite) TestRestoreWithOptions() {
suite.runWithDefaultClients(func(client api.BaseClient) {
key := "testKey1_" + uuid.New().String()
value := "hello"
t := suite.T()
suite.verifyOK(client.Set(key, value))

resultDump, err := client.Dump(key)
assert.Nil(t, err)
assert.NotNil(t, resultDump)

// Test 1: Check restore command with restoreOptions REPLACE modifier
deletedCount, err := client.Del([]string{key})
assert.Nil(t, err)
assert.Equal(t, int64(1), deletedCount)
optsReplace := api.NewRestoreOptionsBuilder().SetReplace()
result_test1, err := client.RestoreWithOptions(key, int64(0), resultDump.Value(), optsReplace)
assert.Nil(suite.T(), err)
assert.Equal(suite.T(), "OK", result_test1.Value())
resultGetRestoreKey, err := client.Get(key)
assert.Nil(t, err)
assert.Equal(t, value, resultGetRestoreKey.Value())

// Test 2: Check restore command with restoreOptions ABSTTL modifier
delete_test2, err := client.Del([]string{key})
assert.Nil(t, err)
assert.Equal(t, int64(1), delete_test2)
opts_test2 := api.NewRestoreOptionsBuilder().SetABSTTL()
result_test2, err := client.RestoreWithOptions(key, int64(0), resultDump.Value(), opts_test2)
assert.Nil(suite.T(), err)
assert.Equal(suite.T(), "OK", result_test2.Value())
resultGet_test2, err := client.Get(key)
assert.Nil(t, err)
assert.Equal(t, value, resultGet_test2.Value())

// Test 3: Check restore command with restoreOptions FREQ modifier
delete_test3, err := client.Del([]string{key})
assert.Nil(t, err)
assert.Equal(t, int64(1), delete_test3)
opts_test3 := api.NewRestoreOptionsBuilder().SetEviction(api.FREQ, 10)
result_test3, err := client.RestoreWithOptions(key, int64(0), resultDump.Value(), opts_test3)
assert.Nil(suite.T(), err)
assert.Equal(suite.T(), "OK", result_test3.Value())
resultGet_test3, err := client.Get(key)
assert.Nil(t, err)
assert.Equal(t, value, resultGet_test3.Value())

// Test 4: Check restore command with restoreOptions IDLETIME modifier
delete_test4, err := client.Del([]string{key})
assert.Nil(t, err)
assert.Equal(t, int64(1), delete_test4)
opts_test4 := api.NewRestoreOptionsBuilder().SetEviction(api.IDLETIME, 10)
result_test4, err := client.RestoreWithOptions(key, int64(0), resultDump.Value(), opts_test4)
assert.Nil(suite.T(), err)
assert.Equal(suite.T(), "OK", result_test4.Value())
resultGet_test4, err := client.Get(key)
assert.Nil(t, err)
assert.Equal(t, value, resultGet_test4.Value())
})
}

0 comments on commit b1a2c1f

Please sign in to comment.