Skip to content

Commit fe35eeb

Browse files
authored
[Backport 2.1] Go: Add Multi-Database Support for Cluster Mode Valkey 9.0 (#4696)
Go: Add Multi-Database Support for Cluster Mode Valkey 9.0 (#4660) * - Implement database selection for cluster clients when database_id != 0 - Send SELECT command to all cluster nodes using MultiNode routing - Add comprehensive error handling with clear error messages - Include test for database selection error handling in cluster mode - Support backward compatibility with servers that don't support multi-db cluster mode - Enable cluster-databases=16 for Valkey 9.0+ in cluster manager This enables cluster clients to work with non-default databases on Valkey 9.0+ servers configured with cluster-databases support, while gracefully handling older servers that don't support this feature. * update valkey9 multi db tests * fix lint * fixing valkey 9 cluster tests * add to route to all nodes in the core * rust lint fix * implement database selection in cluster mode via connection parameters - Add database_id field to ClusterParams and BuilderParams structs - Add database() method to ClusterClientBuilder for setting database ID - Pass database_id through connection info instead of post-connection SELECT - Remove SELECT command from cluster routing - Remove post-connection SELECT command logic from create_cluster_client - Add comprehensive tests for database isolation and reconnection behavior - Verify database ID persistence across node reconnections - Test multi-database cluster support with proper isolation verification This change moves database selection from a post-connection SELECT command to a connection parameter, which is more reliable for cluster mode and handles reconnections automatically. The approach works with servers that support multi-database cluster configurations (like Valkey 9.0+). * add valkey9 constraint in the core tests * fix core test not skipping test when version was lower than valkey 9 * Fix version check * refactored test test_set_database_id_after_reconnection to be similar from standalone removed is_valkey_9_or_higher * removed tests and cleanup * renamed builder.database to builder.database_id * go: Add multi-database support for cluster mode clients - Add DatabaseId field to baseClientConfiguration for both standalone and cluster clients - Implement WithDatabaseId() method for ClusterClientConfiguration - Add database ID validation with proper error handling for negative values - Refactor standalone client to use shared database ID logic from base configuration - Add Select() and SelectWithOptions() methods for cluster clients with routing support - Include comprehensive test coverage for database isolation, reconnection persistence, and error handling - Add backward compatibility support - clients default to database 0 when no database_id specified - Add server version compatibility checks (cluster multi-database requires Valkey 9.0+) - Update documentation with warnings about SELECT command limitations and recommended configuration approach This enables cluster mode clients to connect to specific databases at initialization time, with the database selection persisting across reconnections, unlike the SELECT command which resets on reconnection. * fixed go tests - batch tests - database id tests * removed selectWithOptions * go: Remove Select command from cluster clients - Remove Select method from ClusterClient and related interfaces - Delete comprehensive database_id integration tests - Remove Select command from batch operations - Remove Select examples and tests - Add example for cluster client with database_id configuration The Select command is being removed in favor of using the database_id configuration parameter, which provides persistent database selection across reconnections. * remove config validation simplified tests * added changelog * removing batch test, and readding standalone select and removing batch select --------- Signed-off-by: affonsov <67347924+affonsov@users.noreply.github.com>
1 parent d2731ec commit fe35eeb

File tree

5 files changed

+102
-5
lines changed

5 files changed

+102
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* JAVA: Valkey 9 new commands HASH items expiration ([#4556](https://github.com/valkey-io/valkey-glide/pull/4556))
77
* NODE: Valkey 9 support - Add Hash Field Expiration Commands Support ([#4598](https://github.com/valkey-io/valkey-glide/pull/4598))
88
* Python: Valkey 9 new hash field expiration commands ([#4610](https://github.com/valkey-io/valkey-glide/pull/4610))
9+
* Go: Add Multi-Database Support for Cluster Mode Valkey 9.0 ([#4660](https://github.com/valkey-io/valkey-glide/pull/4660))
910
* Python: Add Multi-Database Support for Cluster Mode Valkey 9.0 ([#4659](https://github.com/valkey-io/valkey-glide/pull/4659))
1011

1112
#### Fixes

go/config/config.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ type baseClientConfiguration struct {
102102
clientAZ string
103103
reconnectStrategy *BackoffStrategy
104104
lazyConnect bool
105+
DatabaseId *int `json:"database_id,omitempty"`
105106
}
106107

107108
func (config *baseClientConfiguration) toProtobuf() (*protobuf.ConnectionRequest, error) {
@@ -152,6 +153,10 @@ func (config *baseClientConfiguration) toProtobuf() (*protobuf.ConnectionRequest
152153
request.LazyConnect = config.lazyConnect
153154
}
154155

156+
if config.DatabaseId != nil {
157+
request.DatabaseId = uint32(*config.DatabaseId)
158+
}
159+
155160
return &request, nil
156161
}
157162

@@ -214,7 +219,6 @@ func (strategy *BackoffStrategy) toProtobuf() *protobuf.ConnectionRetryStrategy
214219
// ClientConfiguration represents the configuration settings for a Standalone client.
215220
type ClientConfiguration struct {
216221
baseClientConfiguration
217-
databaseId int
218222
subscriptionConfig *StandaloneSubscriptionConfig
219223
AdvancedClientConfiguration
220224
}
@@ -232,9 +236,6 @@ func (config *ClientConfiguration) ToProtobuf() (*protobuf.ConnectionRequest, er
232236
}
233237
request.ClusterModeEnabled = false
234238

235-
if config.databaseId != 0 {
236-
request.DatabaseId = uint32(config.databaseId)
237-
}
238239
if config.subscriptionConfig != nil && len(config.subscriptionConfig.subscriptions) > 0 {
239240
request.PubsubSubscriptions = config.subscriptionConfig.toProtobuf()
240241
}
@@ -330,7 +331,7 @@ func (config *ClientConfiguration) WithReconnectStrategy(strategy *BackoffStrate
330331

331332
// WithDatabaseId sets the index of the logical database to connect to.
332333
func (config *ClientConfiguration) WithDatabaseId(id int) *ClientConfiguration {
333-
config.databaseId = id
334+
config.DatabaseId = &id
334335
return config
335336
}
336337

@@ -481,6 +482,12 @@ func (config *ClusterClientConfiguration) WithReconnectStrategy(
481482
return config
482483
}
483484

485+
// WithDatabaseId sets the index of the logical database to connect to.
486+
func (config *ClusterClientConfiguration) WithDatabaseId(id int) *ClusterClientConfiguration {
487+
config.DatabaseId = &id
488+
return config
489+
}
490+
484491
// WithAdvancedConfiguration sets the advanced configuration settings for the client.
485492
func (config *ClusterClientConfiguration) WithAdvancedConfiguration(
486493
advancedConfig *AdvancedClusterClientConfiguration,

go/config/config_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,3 +340,54 @@ func TestConfig_LazyConnect(t *testing.T) {
340340

341341
assert.False(t, defaultClusterResult.LazyConnect)
342342
}
343+
344+
func TestConfig_DatabaseId(t *testing.T) {
345+
// Test standalone client with database ID
346+
standaloneConfig := NewClientConfiguration().WithDatabaseId(5)
347+
standaloneResult, err := standaloneConfig.ToProtobuf()
348+
if err != nil {
349+
t.Fatalf("Failed to convert standalone config to protobuf: %v", err)
350+
}
351+
assert.Equal(t, uint32(5), standaloneResult.DatabaseId)
352+
353+
// Test cluster client with database ID
354+
clusterConfig := NewClusterClientConfiguration().WithDatabaseId(3)
355+
clusterResult, err := clusterConfig.ToProtobuf()
356+
if err != nil {
357+
t.Fatalf("Failed to convert cluster config to protobuf: %v", err)
358+
}
359+
assert.Equal(t, uint32(3), clusterResult.DatabaseId)
360+
361+
// Test default behavior (no database ID set)
362+
defaultStandaloneConfig := NewClientConfiguration()
363+
defaultStandaloneResult, err := defaultStandaloneConfig.ToProtobuf()
364+
if err != nil {
365+
t.Fatalf("Failed to convert default standalone config to protobuf: %v", err)
366+
}
367+
assert.Equal(t, uint32(0), defaultStandaloneResult.DatabaseId)
368+
369+
defaultClusterConfig := NewClusterClientConfiguration()
370+
defaultClusterResult, err := defaultClusterConfig.ToProtobuf()
371+
if err != nil {
372+
t.Fatalf("Failed to convert default cluster config to protobuf: %v", err)
373+
}
374+
assert.Equal(t, uint32(0), defaultClusterResult.DatabaseId)
375+
}
376+
377+
func TestConfig_DatabaseId_BaseConfiguration(t *testing.T) {
378+
// Test that database_id is properly handled in base configuration for both client types
379+
380+
// Test standalone client inherits database_id from base configuration
381+
standaloneConfig := NewClientConfiguration().WithDatabaseId(5)
382+
standaloneResult, err := standaloneConfig.ToProtobuf()
383+
assert.NoError(t, err)
384+
assert.Equal(t, uint32(5), standaloneResult.DatabaseId)
385+
assert.False(t, standaloneResult.ClusterModeEnabled)
386+
387+
// Test cluster client inherits database_id from base configuration
388+
clusterConfig := NewClusterClientConfiguration().WithDatabaseId(3)
389+
clusterResult, err := clusterConfig.ToProtobuf()
390+
assert.NoError(t, err)
391+
assert.Equal(t, uint32(3), clusterResult.DatabaseId)
392+
assert.True(t, clusterResult.ClusterModeEnabled)
393+
}

go/create_client_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,27 @@ func ExampleNewClusterClient() {
5454
// Output:
5555
// Client created and connected: *glide.ClusterClient
5656
}
57+
58+
func ExampleNewClusterClient_withDatabaseId() {
59+
// This WithDatabaseId for cluster requires Valkey 9.0+
60+
clientConf := config.NewClusterClientConfiguration().
61+
WithAddress(&getClusterAddresses()[0]).
62+
WithRequestTimeout(5 * time.Second).
63+
WithUseTLS(false).
64+
WithDatabaseId(1).
65+
WithSubscriptionConfig(
66+
config.NewClusterSubscriptionConfig().
67+
WithSubscription(config.PatternClusterChannelMode, "news.*").
68+
WithCallback(func(message *models.PubSubMessage, ctx any) {
69+
fmt.Printf("Received message on '%s': %s", message.Channel, message.Message)
70+
}, nil),
71+
)
72+
client, err := NewClusterClient(clientConf)
73+
if err != nil {
74+
fmt.Println("Failed to create a client and connect: ", err)
75+
}
76+
fmt.Printf("Client created and connected: %T", client)
77+
78+
// Output:
79+
// Client created and connected: *glide.ClusterClient
80+
}

go/glide_client.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,20 @@ func (client *Client) ConfigGet(ctx context.Context, args []string) (map[string]
210210

211211
// Select changes the currently selected database.
212212
//
213+
// WARNING: This command is NOT RECOMMENDED for production use.
214+
// Upon reconnection, the client will revert to the database_id specified
215+
// in the client configuration (default: 0), NOT the database selected
216+
// via this command.
217+
//
218+
// RECOMMENDED APPROACH: Use the database_id parameter in client
219+
// configuration instead:
220+
//
221+
// config := &config.ClientConfiguration{
222+
// Addresses: []config.NodeAddress{{Host: "localhost", Port: 6379}},
223+
// DatabaseId: &databaseId, // Recommended: persists across reconnections
224+
// }
225+
// client, err := NewClient(config)
226+
//
213227
// See [valkey.io] for details.
214228
//
215229
// Parameters:

0 commit comments

Comments
 (0)