Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GO Implement Dump, Restore and ObjectEncoding Command #2781

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d4f398e
GO Implement Dump and ObjectEncoding command
EdricCua Dec 3, 2024
7b4710b
Add restore command
EdricCua Dec 5, 2024
2762a43
update branch from main
EdricCua Dec 27, 2024
cfcb0e8
Added RestoreWithOptions
EdricCua Dec 27, 2024
f1413cb
Merge branch 'valkey-io:main' into Go-Implement-Dump-ObjectEncoding-c…
EdricCua Dec 30, 2024
ce6d506
Fix Docs, Test and base_client.go
EdricCua Dec 30, 2024
7aedc52
Get update from main, fix conflict
EdricCua Jan 3, 2025
c37233a
Added info Docs to the Eviction struct
EdricCua Jan 3, 2025
66fef28
Modify test and command_options
EdricCua Jan 6, 2025
45226a1
Merge branch 'valkey-io:main' into Go-Implement-Dump-ObjectEncoding-c…
EdricCua Jan 6, 2025
7fe1cee
Set RestoreOptions fields private and fix lint
EdricCua Jan 7, 2025
54689ab
Fix merge confict
EdricCua Jan 7, 2025
1b3888e
Fix lint
EdricCua Jan 7, 2025
7f17c40
Merge branch 'valkey-io:main' into Go-Implement-Dump-ObjectEncoding-c…
EdricCua Jan 8, 2025
51e6294
Fix lint comment
EdricCua Jan 8, 2025
9e5d2e5
Fix merge conflict
EdricCua Jan 8, 2025
d2dd403
Fix lint
EdricCua Jan 8, 2025
48f2bcc
Fix lint
EdricCua Jan 8, 2025
459002e
Rename test
EdricCua Jan 9, 2025
cc855fa
Merge branch 'valkey-io:main' into Go-Implement-Dump-ObjectEncoding-c…
EdricCua Jan 9, 2025
82345bb
Fix merge conflict
EdricCua Jan 9, 2025
c9ed87f
Moved Dump, ObjEncoding, Restore and RestoreWithOption
EdricCua Jan 10, 2025
d0c67c3
Merge branch 'valkey-io:main' into Go-Implement-Dump-ObjectEncoding-c…
EdricCua Jan 10, 2025
a1ead6f
Corrected expected result in test ObjectEncoding
EdricCua Jan 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions go/api/base_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1483,3 +1483,40 @@ func (client *baseClient) Persist(key string) (Result[bool], error) {
}
return handleBooleanResponse(result)
}

func (client *baseClient) Restore(key string, ttl int64, value string) (Result[string], error) {
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
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 handleStringOrNullResponse(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 handleStringOrNullResponse(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 handleStringOrNullResponse(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 @@ -447,4 +447,89 @@ type GenericBaseCommands interface {
//
// [valkey.io]: https://valkey.io/commands/persist/
Persist(key string) (Result[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 @@ -4484,3 +4484,111 @@ func (suite *GlideTestSuite) TestPersist() {
assert.False(t, resultInvalidKey.Value())
})
}

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 for quicklist
list := []string{"value1", "value2", "value3"}
key1 := "{keyName}" + uuid.NewString()
res1, err := client.LPush(key1, list)
assert.Nil(t, err)
assert.Equal(t, int64(3), res1.Value())
resultListPack, err := client.ObjectEncoding(key1)
assert.Nil(t, err)
assert.Equal(t, "quicklist", resultListPack.Value(), "The result should be quicklist")

// Test 3: Check object encoding command for non existing key
key3 := "{keyName}" + uuid.NewString()
resultDumpNull, err := client.ObjectEncoding(key3)
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.Value())
suite.verifyOK(client.Restore(key, int64(0), resultDump.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.Value())
optsReplace := api.NewRestoreOptionsBuilder().SetReplace()
suite.verifyOK(client.RestoreWithOptions(key, int64(0), resultDump.Value(), optsReplace))
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.Value())
opts_test2 := api.NewRestoreOptionsBuilder().SetABSTTL()
suite.verifyOK(client.RestoreWithOptions(key, int64(0), resultDump.Value(), opts_test2))
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.Value())
opts_test3 := api.NewRestoreOptionsBuilder().SetEviction(api.FREQ, 10)
suite.verifyOK(client.RestoreWithOptions(key, int64(0), resultDump.Value(), opts_test3))
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.Value())
opts_test4 := api.NewRestoreOptionsBuilder().SetEviction(api.IDLETIME, 10)
suite.verifyOK(client.RestoreWithOptions(key, int64(0), resultDump.Value(), opts_test4))
resultGet_test4, err := client.Get(key)
assert.Nil(t, err)
assert.Equal(t, value, resultGet_test4.Value())
})
}
Loading