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 LastSave command #3086

Merged
merged 33 commits into from
Mar 27, 2025
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
cb23720
Implement LastSave command
EdricCua Feb 5, 2025
8c31d56
Fix review comment
EdricCua Feb 5, 2025
108f875
Merge branch 'main' into Go-Implement-LastSave
Yury-Fridlyand Feb 7, 2025
3c8a874
Update from main
EdricCua Feb 10, 2025
167e176
Fix docs comment
EdricCua Feb 10, 2025
ede6c11
Merge branch 'main' into Go-Implement-LastSave
Yury-Fridlyand Feb 10, 2025
0b1f5d6
Implement LastSave
EdricCua Feb 13, 2025
b486213
Update from main
EdricCua Feb 13, 2025
ef98d2a
update from main
EdricCua Mar 12, 2025
728bbad
add example doc
EdricCua Mar 12, 2025
9523341
add example doc
EdricCua Mar 12, 2025
5292cc3
add example doc
EdricCua Mar 12, 2025
f3855b5
Updates from main
EdricCua Mar 18, 2025
e910dea
Fix review comment
EdricCua Mar 18, 2025
fd4521b
Fix review comment
EdricCua Mar 18, 2025
d414cf5
Fix review comment
EdricCua Mar 18, 2025
ee1d155
Fix review comment
EdricCua Mar 18, 2025
a3ec505
fix review comment
EdricCua Mar 18, 2025
cc4f266
fix example
EdricCua Mar 18, 2025
8799409
update from main
EdricCua Mar 18, 2025
e274dd7
fix examples
EdricCua Mar 19, 2025
c369184
fix examples
EdricCua Mar 19, 2025
919f231
fix examples
EdricCua Mar 19, 2025
8bcf3e9
fix examples
EdricCua Mar 19, 2025
bbf62b4
fix examples
EdricCua Mar 19, 2025
2f5f5c2
update from main
EdricCua Mar 20, 2025
3f5d8d7
update from main
EdricCua Mar 20, 2025
a4b7c44
Merge branch 'main' into Go-Implement-LastSave
Yury-Fridlyand Mar 20, 2025
137808f
update from main
EdricCua Mar 20, 2025
80baefe
update from main
EdricCua Mar 24, 2025
79330d8
update from main
EdricCua Mar 24, 2025
a0246e5
update from main
EdricCua Mar 26, 2025
42a9eeb
update from main
EdricCua Mar 27, 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
15 changes: 15 additions & 0 deletions go/api/glide_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,3 +238,18 @@ func (client *GlideClient) PingWithOptions(pingOptions options.PingOptions) (str
}
return handleStringResponse(result)
}

// Returns UNIX TIME of the last DB save timestamp or startup timestamp if no save was made since then.
//
// Return value:
//
// UNIX TIME of the last DB save executed with success.
//
// [valkey.io]: https://valkey.io/commands/lastsave/
func (client *GlideClient) LastSave() (int64, error) {
response, err := client.executeCommand(C.LastSave, []string{})
if err != nil {
return defaultIntResponse, err
}
return handleIntResponse(response)
}
53 changes: 53 additions & 0 deletions go/api/glide_cluster_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,3 +452,56 @@ func (client *GlideClusterClient) ScanWithOptions(
nextCursor, keys, err := handleScanResponse(response)
return *options.NewClusterScanCursorWithId(nextCursor), keys, err
}

// Returns UNIX TIME of the last DB save timestamp or startup timestamp if no save was made since then.
// The command is routed to a random node by default, which is safe for read-only commands.
//
// Return value:
//
// UNIX TIME of the last DB save executed with success.
//
// [valkey.io]: https://valkey.io/commands/lastsave/
func (client *GlideClusterClient) LastSave() (ClusterValue[int64], error) {
response, err := client.executeCommand(C.LastSave, []string{})
if err != nil {
return createEmptyClusterValue[int64](), err
}
data, err := handleIntResponse(response)
if err != nil {
return createEmptyClusterValue[int64](), err
}
return createClusterSingleValue[int64](data), nil
}

// Returns UNIX TIME of the last DB save timestamp or startup timestamp if no save was made since then.
// The command is routed to a random node by default, which is safe for read-only commands.
//
// Parameters:
//
// route - Specifies the routing configuration for the command. The client will route the
// command to the nodes defined by route.
//
// Return value:
//
// UNIX TIME of the last DB save executed with success.
//
// [valkey.io]: https://valkey.io/commands/lastsave/
func (client *GlideClusterClient) LastSaveWithOptions(opts options.RouteOption) (ClusterValue[int64], error) {
response, err := client.executeCommandWithRoute(C.LastSave, []string{}, opts.Route)
if err != nil {
return createEmptyClusterValue[int64](), err
}
if opts.Route != nil &&
(opts.Route).IsMultiNode() {
data, err := handleStringIntMapResponse(response)
if err != nil {
return createEmptyClusterValue[int64](), err
}
return createClusterMultiValue[int64](data), nil
}
data, err := handleIntResponse(response)
if err != nil {
return createEmptyClusterValue[int64](), err
}
return createClusterSingleValue[int64](data), nil
}
27 changes: 27 additions & 0 deletions go/api/response_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -1334,3 +1334,30 @@ func handleTimeClusterResponse(response *C.struct_CommandResponse) (ClusterValue
}
return createClusterSingleValue(data), nil
}

func handleStringIntMapResponse(response *C.struct_CommandResponse) (map[string]int64, error) {
defer C.free_command_response(response)

typeErr := checkResponseType(response, C.Map, false)
if typeErr != nil {
return nil, typeErr
}

data, err := parseMap(response)
if err != nil {
return nil, err
}
aMap := data.(map[string]interface{})

converted, err := mapConverter[int64]{
nil, false,
}.convert(aMap)
if err != nil {
return nil, err
}
result, ok := converted.(map[string]int64)
if !ok {
return nil, &errors.RequestError{Msg: fmt.Sprintf("unexpected type of map: %T", converted)}
}
return result, nil
}
4 changes: 4 additions & 0 deletions go/api/server_management_cluster_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ type ServerManagementClusterCommands interface {
TimeWithOptions(routeOption options.RouteOption) (ClusterValue[[]string], error)

DBSizeWithOptions(routeOption options.RouteOption) (int64, error)

LastSave() (ClusterValue[int64], error)

LastSaveWithOptions(routeOption options.RouteOption) (ClusterValue[int64], error)
}
23 changes: 23 additions & 0 deletions go/api/server_management_cluster_commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,26 @@ func ExampleGlideClusterClient_DBSizeWithOptions() {

// Output: 0
}

func ExampleGlideClusterClient_LastSave() {
var client *GlideClusterClient = getExampleGlideClusterClient() // example helper function
result, err := client.LastSave()
if err != nil {
fmt.Println("Glide example failed with an error: ", err)
}
fmt.Println(result.IsSingleValue())

// Output: true
}

func ExampleGlideClusterClient_LastSaveWithOptions() {
var client *GlideClusterClient = getExampleGlideClusterClient() // example helper function
opts := options.RouteOption{Route: nil}
result, err := client.LastSaveWithOptions(opts)
if err != nil {
fmt.Println("Glide example failed with an error: ", err)
}
fmt.Println(result.IsSingleValue())

// Output: true
}
2 changes: 2 additions & 0 deletions go/api/server_management_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ type ServerManagementCommands interface {
DBSize() (int64, error)

Time() ([]string, error)

LastSave() (int64, error)
}
12 changes: 12 additions & 0 deletions go/api/server_management_commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,15 @@ func ExampleGlideClient_InfoWithOptions() {

// Output: response is of type string
}

func ExampleGlideClient_LastSave() {
var client *GlideClient = getExampleGlideClient() // example helper function
response, err := client.LastSave()
if err != nil {
fmt.Println("Glide example failed with an error: ", err)
}
fmt.Println(response)

// Output:
// 1742239667
}
33 changes: 33 additions & 0 deletions go/integTest/cluster_commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -729,3 +729,36 @@ func (suite *GlideTestSuite) TestClusterScanWithDifferentTypes() {
assert.NotContains(t, allKeys, elem)
}
}

func (suite *GlideTestSuite) TestLastSaveCluster() {
client := suite.defaultClusterClient()
t := suite.T()
response, err := client.LastSave()
assert.NoError(t, err)
assert.True(t, response.IsSingleValue())
}

func (suite *GlideTestSuite) TestLastSaveWithOptionCluster() {
client := suite.defaultClusterClient()
t := suite.T()

// LastSave with option or with multiple options without route
opts := options.RouteOption{Route: nil}
response, err := client.LastSaveWithOptions(opts)
assert.NoError(t, err)
assert.True(t, response.IsSingleValue())

// same sections with random route
route := config.Route(config.RandomRoute)
opts = options.RouteOption{Route: route}
response, err = client.LastSaveWithOptions(opts)
assert.NoError(t, err)
assert.True(t, response.IsSingleValue())

// default sections, multi node route
route = config.Route(config.AllPrimaries)
opts = options.RouteOption{Route: route}
response, err = client.LastSaveWithOptions(opts)
assert.NoError(t, err)
assert.True(t, response.IsMultiValue())
}
8 changes: 8 additions & 0 deletions go/integTest/standalone_commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -516,3 +516,11 @@ func (suite *GlideTestSuite) TestTime_Error() {
assert.Nil(suite.T(), results)
assert.IsType(suite.T(), &errors.ClosingError{}, err)
}

func (suite *GlideTestSuite) TestLastSave() {
client := suite.defaultClient()
t := suite.T()
result, err := client.LastSave()
assert.Nil(t, err)
assert.Greater(t, result, int64(0))
}
Loading