From 919a6706b7b50d0138b640316ac4ffb4197e1bec Mon Sep 17 00:00:00 2001 From: Niharika Bhavaraju Date: Wed, 30 Apr 2025 14:19:24 +0000 Subject: [PATCH 01/13] Valkey Glide Go client transaction blog Signed-off-by: Niharika Bhavaraju --- .../blog/2025-04-30-go-client-transaction.md | 306 ++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 content/blog/2025-04-30-go-client-transaction.md diff --git a/content/blog/2025-04-30-go-client-transaction.md b/content/blog/2025-04-30-go-client-transaction.md new file mode 100644 index 00000000..04be60f4 --- /dev/null +++ b/content/blog/2025-04-30-go-client-transaction.md @@ -0,0 +1,306 @@ ++++ +title= "Introducing the Transaction in Valkey Glide Go Client" +description = "Valkey Glide transaction in Valkey Glide Go Client. Read to learn more about how transaction is designed for Go client" +date= 2025-04-30 01:01:01 +authors= [ "niharikabhavaraju"] ++++ + +Introducing Transaction for Valkey GLIDE Go client + + Valkey GLIDE Go client provides a comprehensive transaction support, enabling developers to execute multiple commands atomically across standalone and cluster environments. +Transactions provide a mechanism for executing multiple commands atomically, ensuring data integrity and consistency in distributed systems. + +## Key Concepts + +### What are transactions? + +Transactions in Valkey allow the execution of a group of commands in a single step. They ensure all operations are executed sequentially, with no other client requests served in the middle of the transaction, guaranteeing the operations are executed as a single isolated unit. + +### Transaction Lifecycle + +**Create Transaction:** + + Initialize a transaction object. +Use `api.NewTransaction(client)` to create a new transaction with established client as an argument +`cmd := tx.GlideClient` grants access to a transaction-scoped commands interface, allowing you to construct a sequence of operations to be executed. We can access transaction commands via the embedded client. + +**Queue Commands:** + + Queue commands in the order they should be executed. +Each queued command is added to the transaction but not immediately executed.. +`cmd.CommandName` allows you to queue multiple transactions , +**Example:** + +```go +cmd.Set("key1","val1") +cmd.Get("key1") +``` + +**Execute Transaction:** + Run all queued commands atomically + After queueing the commands, transaction can be executed with `tx.Exec()` + All the queued commands will be executed and returns an response array which consists of the commands result. + +## Standalone Mode Transaction + +```go +// Standalone Client Transaction +package main + +import ( + "fmt" + "log" + "github.com/valkey-io/valkey-glide/go/api" +) + +func main() { + config := api.NewGlideClientConfiguration(). + WithAddress(&api.NodeAddress{Host: "localhost", Port: 6379}) + + client, err := api.NewGlideClient(config) + if err != nil { + log.Fatal("error connecting to database: ", err) + } + defer client.Close() + + tx := api.NewTransaction(client) + cmd := tx.GlideClient + + // Queue multiple commands + cmd.Set("user:profile", "John Doe") + cmd.Set("user:email", "john@example.com") + cmd.SAdd("users", "john") + + // Execute transaction + results, err := tx.Exec() + if err != nil { + log.Printf("Transaction failed: %v", err) + return + } + + // Process results + for _, result := range results { + fmt.Println(result) + } +} +``` + +## Cluster mode Transaction + +To create a cluster transaction, initialize the cluster transaction object using `api.NewClusterTransaction(clusterClient)` to create a new cluster transaction with an established cluster client as an argument. +`cmd := tx.GlideClusterClient` grants access to a transaction-scoped cluster commands interface, allowing you to construct a sequence of operations to be executed. + +```go +// Cluster Client Transaction +package main + +import ( + "fmt" + "log" + "github.com/valkey-io/valkey-glide/go/api" + "github.com/valkey-io/valkey-glide/go/api/options" +) + +func main() { + clusterConfig := api.NewGlideClusterClientConfiguration(). + WithAddress(&api.NodeAddress{Host: "cluster1", Port: 7007}). + WithAddress(&api.NodeAddress{Host: "cluster2", Port: 7008}). + WithAddress(&api.NodeAddress{Host: "cluster3", Port: 7009}) + + clusterClient, err := api.NewGlideClusterClient(clusterConfig) + if err != nil { + log.Fatal("error connecting to cluster: ", err) + } + defer clusterClient.Close() + + clusterTx := api.NewClusterTransaction(clusterClient) + cmd := clusterTx.GlideClusterClient + + // Define routing option + var simpleRoute config.Route = config.RandomRoute + + // Create ping options with specific routing + pingOpts := options.ClusterPingOptions{ + PingOptions: &options.PingOptions{ + Message: "Hello Valkey Cluster", + }, + RouteOption: &options.RouteOption{ + Route: simpleRoute, + }, + } + + // Queue commands with routing + cmd.PingWithOptions(pingOpts) + // Execute transaction + results, err := clusterTx.Exec() + if err != nil { + log.Printf("Cluster transaction failed: %v", err) + return + } + + // Process results + for _, result := range results { + fmt.Println(result) + } +} +``` + +In cluster transactions, all commands are executed on a single node, regardless of the routing configuration. The routing option does not enable multi-node transaction execution. In the below example, it routes to a random single node even when the route is not provided. + +```go +clusterTx := api.NewClusterTransaction(clusterClient) +cmd := clusterTx.GlideClusterClient + +// Create ping options with specific routing +pingOpts := options.ClusterPingOptions{ + PingOptions: &options.PingOptions{ + Message: "Hello Valkey Cluster", + }, + // route is not provided here +} + +// Queue commands with routing +cmd.PingWithOptions(pingOpts) +// Execute transaction +results, err := clusterTx.Exec() +if err != nil { + log.Printf("Cluster transaction failed: %v", err) + return +} + +// Process results +for _, result := range results { + fmt.Println(result) +} +``` + +## Transaction design + +The transaction implementation in Valkey GLIDE was designed with several key goals, each achieved through specific implementation approaches: + +### Idiomatic Go API + +The design provides a natural, Go-like interface for transactions through: + +- Clean command calling syntax (`cmd.Set(...)`, `cmd.Get(...)`) +- Use of Go's embedding to inherit behavior from `baseClient` +- Explicit error handling that follows Go conventions (`result, err := tx.Exec()`) + +### Command Transparency + +A key design followed is that commands can be used the same way in both regular and transaction contexts: + +```go +// In regular client context - executes immediately +client.Set("key1", "value1") + +// In transaction context - identical syntax, but queued instead +tx := NewTransaction(client) +cmd := tx.GlideClient +cmd.Set("key1", "value1") // Queued for later execution +``` + +This transparency is implemented in code like this + +```go +// Inside baseClient command methods like Get, Set, etc. +func (client *baseClient) Set(key string, value string) (string, error) { + // Use the executor to send the command + result, err := client.executor.sendCommand(C.Set, []string{key, value}) + if err != nil { + return DefaultStringResponse, err + } + + // Check if we're in a transaction + if _, isTransaction := client.executor.(*Transaction); isTransaction && result == nil { + return DefaultStringResponse, err + } + + return handleOkResponse(result) +} +``` + +The key aspects: + +- Regular clients and transactions both implement a common interface +- When creating a transaction, it sets itself as the command executor +- In transaction mode, commands are queued instead of executed +- The code detects transaction context and handles results appropriately + +This enables patterns like: + +```go +cmd.Watch([]string{"key1"}) +// Check current value +val, _ := cmd.Get("key1") +// Only update if no one else modified since Watch +cmd.Set("key1", "new-value") +// Will fail if key1 changed between Watch and Exec +result, err := tx.Exec() +``` + +### Optimistic Concurrency Support + +Valkey's optimistic locking mechanism is supported through: + +- Implementation of `Watch()` that monitors keys for changes +- Implementation of `Unwatch()` that releases monitoring +- Proper handling of transaction failure when watched keys change +- Passing watch state through to the Valkey server during execution + +This enables patterns like: + +```go +cmd.Watch([]string{"key1"}) +// Check current value +val, _ := cmd.Get("key1") +// Only update if no one else modified since Watch +cmd.Set("key1", "new-value") +// Will fail if key1 changed between Watch and Exec +result, err := tx.Exec() +``` + +### Memory Management + +Memory management across language boundaries is a critical design consideration: + +- Go side uses explicit pinning to prevent garbage collection of memory shared with Rust +- Reference counting in the Rust layer ensures proper cleanup +- Explicit tracking of pending requests allows graceful handling of client closure + +### Discard Operation + + The `tx.Discard()` method can be used to explicitly cancel a transaction before execution. +This mechanism serves to: + +- Release server-side resources associated with the transaction when the client decides not to proceed. - Clean up client-side state, such as the command queue within the `TransactionExecutor`. +- Prevent the execution of unintended commands. + This functionality is achieved by sending the `DISCARD` command to the Valkey server, which then clears the server-side command queue for the transaction. The `tx.Discard()` method will return an error if communication with the server fails during this process. + +### Composition over inheritance + +The `transaction.go` defines the core transaction functionality: + +```go +type Transaction struct { + *baseClient // Embed baseClient to inherit methods + *GlideClient // Embed client for command chaining + *GlideClusterClient // Support for cluster environments + commands []Cmder // Queue for transaction commands +} +``` + +The `Transaction` struct uses composition by embedding both `baseClient` and the client types (`GlideClient` ,`GlideClusterClient`). This approach allows the transaction to inherit all methods from the client &cCommand implementations are shared between regular execution and transactions. +This composition approach allows the transaction to reuse most of the client code while overriding only the behavior that needs to change for transactions. + +## Conclusion + +The transaction in Valkey GLIDE go client provides a robust, idiomatic way to execute atomic operations in a Valkey database. Its design emphasizes developer experience, safety, and compatibility with Valkey semantics while providing a solid foundation for future enhancements. + +## Contributors + +The following contributors dedicated their efforts to implementing transactions in the Valkey GLIDE Go client: + +- [Omkar Mestry](https://github.com/omangesg) (Google Cloud Platform) +- [Edric Cuartero](https://github.com/EdricCua) (Google Cloud Platform) +- [Niharika Bhavaraju](https://github.com/niharikabhavaraju) (Google Cloud Platform) From f4c337bd30230a76ece3d8dccbcb68529215a10b Mon Sep 17 00:00:00 2001 From: Niharika Bhavaraju Date: Thu, 8 May 2025 11:02:01 +0000 Subject: [PATCH 02/13] Added GA release blog content Signed-off-by: Niharika Bhavaraju --- .../blog/2025-04-30-go-client-transaction.md | 306 ----------- content/blog/2025-05-08-go-client-GA.md | 501 ++++++++++++++++++ 2 files changed, 501 insertions(+), 306 deletions(-) delete mode 100644 content/blog/2025-04-30-go-client-transaction.md create mode 100644 content/blog/2025-05-08-go-client-GA.md diff --git a/content/blog/2025-04-30-go-client-transaction.md b/content/blog/2025-04-30-go-client-transaction.md deleted file mode 100644 index 04be60f4..00000000 --- a/content/blog/2025-04-30-go-client-transaction.md +++ /dev/null @@ -1,306 +0,0 @@ -+++ -title= "Introducing the Transaction in Valkey Glide Go Client" -description = "Valkey Glide transaction in Valkey Glide Go Client. Read to learn more about how transaction is designed for Go client" -date= 2025-04-30 01:01:01 -authors= [ "niharikabhavaraju"] -+++ - -Introducing Transaction for Valkey GLIDE Go client - - Valkey GLIDE Go client provides a comprehensive transaction support, enabling developers to execute multiple commands atomically across standalone and cluster environments. -Transactions provide a mechanism for executing multiple commands atomically, ensuring data integrity and consistency in distributed systems. - -## Key Concepts - -### What are transactions? - -Transactions in Valkey allow the execution of a group of commands in a single step. They ensure all operations are executed sequentially, with no other client requests served in the middle of the transaction, guaranteeing the operations are executed as a single isolated unit. - -### Transaction Lifecycle - -**Create Transaction:** - - Initialize a transaction object. -Use `api.NewTransaction(client)` to create a new transaction with established client as an argument -`cmd := tx.GlideClient` grants access to a transaction-scoped commands interface, allowing you to construct a sequence of operations to be executed. We can access transaction commands via the embedded client. - -**Queue Commands:** - - Queue commands in the order they should be executed. -Each queued command is added to the transaction but not immediately executed.. -`cmd.CommandName` allows you to queue multiple transactions , -**Example:** - -```go -cmd.Set("key1","val1") -cmd.Get("key1") -``` - -**Execute Transaction:** - Run all queued commands atomically - After queueing the commands, transaction can be executed with `tx.Exec()` - All the queued commands will be executed and returns an response array which consists of the commands result. - -## Standalone Mode Transaction - -```go -// Standalone Client Transaction -package main - -import ( - "fmt" - "log" - "github.com/valkey-io/valkey-glide/go/api" -) - -func main() { - config := api.NewGlideClientConfiguration(). - WithAddress(&api.NodeAddress{Host: "localhost", Port: 6379}) - - client, err := api.NewGlideClient(config) - if err != nil { - log.Fatal("error connecting to database: ", err) - } - defer client.Close() - - tx := api.NewTransaction(client) - cmd := tx.GlideClient - - // Queue multiple commands - cmd.Set("user:profile", "John Doe") - cmd.Set("user:email", "john@example.com") - cmd.SAdd("users", "john") - - // Execute transaction - results, err := tx.Exec() - if err != nil { - log.Printf("Transaction failed: %v", err) - return - } - - // Process results - for _, result := range results { - fmt.Println(result) - } -} -``` - -## Cluster mode Transaction - -To create a cluster transaction, initialize the cluster transaction object using `api.NewClusterTransaction(clusterClient)` to create a new cluster transaction with an established cluster client as an argument. -`cmd := tx.GlideClusterClient` grants access to a transaction-scoped cluster commands interface, allowing you to construct a sequence of operations to be executed. - -```go -// Cluster Client Transaction -package main - -import ( - "fmt" - "log" - "github.com/valkey-io/valkey-glide/go/api" - "github.com/valkey-io/valkey-glide/go/api/options" -) - -func main() { - clusterConfig := api.NewGlideClusterClientConfiguration(). - WithAddress(&api.NodeAddress{Host: "cluster1", Port: 7007}). - WithAddress(&api.NodeAddress{Host: "cluster2", Port: 7008}). - WithAddress(&api.NodeAddress{Host: "cluster3", Port: 7009}) - - clusterClient, err := api.NewGlideClusterClient(clusterConfig) - if err != nil { - log.Fatal("error connecting to cluster: ", err) - } - defer clusterClient.Close() - - clusterTx := api.NewClusterTransaction(clusterClient) - cmd := clusterTx.GlideClusterClient - - // Define routing option - var simpleRoute config.Route = config.RandomRoute - - // Create ping options with specific routing - pingOpts := options.ClusterPingOptions{ - PingOptions: &options.PingOptions{ - Message: "Hello Valkey Cluster", - }, - RouteOption: &options.RouteOption{ - Route: simpleRoute, - }, - } - - // Queue commands with routing - cmd.PingWithOptions(pingOpts) - // Execute transaction - results, err := clusterTx.Exec() - if err != nil { - log.Printf("Cluster transaction failed: %v", err) - return - } - - // Process results - for _, result := range results { - fmt.Println(result) - } -} -``` - -In cluster transactions, all commands are executed on a single node, regardless of the routing configuration. The routing option does not enable multi-node transaction execution. In the below example, it routes to a random single node even when the route is not provided. - -```go -clusterTx := api.NewClusterTransaction(clusterClient) -cmd := clusterTx.GlideClusterClient - -// Create ping options with specific routing -pingOpts := options.ClusterPingOptions{ - PingOptions: &options.PingOptions{ - Message: "Hello Valkey Cluster", - }, - // route is not provided here -} - -// Queue commands with routing -cmd.PingWithOptions(pingOpts) -// Execute transaction -results, err := clusterTx.Exec() -if err != nil { - log.Printf("Cluster transaction failed: %v", err) - return -} - -// Process results -for _, result := range results { - fmt.Println(result) -} -``` - -## Transaction design - -The transaction implementation in Valkey GLIDE was designed with several key goals, each achieved through specific implementation approaches: - -### Idiomatic Go API - -The design provides a natural, Go-like interface for transactions through: - -- Clean command calling syntax (`cmd.Set(...)`, `cmd.Get(...)`) -- Use of Go's embedding to inherit behavior from `baseClient` -- Explicit error handling that follows Go conventions (`result, err := tx.Exec()`) - -### Command Transparency - -A key design followed is that commands can be used the same way in both regular and transaction contexts: - -```go -// In regular client context - executes immediately -client.Set("key1", "value1") - -// In transaction context - identical syntax, but queued instead -tx := NewTransaction(client) -cmd := tx.GlideClient -cmd.Set("key1", "value1") // Queued for later execution -``` - -This transparency is implemented in code like this - -```go -// Inside baseClient command methods like Get, Set, etc. -func (client *baseClient) Set(key string, value string) (string, error) { - // Use the executor to send the command - result, err := client.executor.sendCommand(C.Set, []string{key, value}) - if err != nil { - return DefaultStringResponse, err - } - - // Check if we're in a transaction - if _, isTransaction := client.executor.(*Transaction); isTransaction && result == nil { - return DefaultStringResponse, err - } - - return handleOkResponse(result) -} -``` - -The key aspects: - -- Regular clients and transactions both implement a common interface -- When creating a transaction, it sets itself as the command executor -- In transaction mode, commands are queued instead of executed -- The code detects transaction context and handles results appropriately - -This enables patterns like: - -```go -cmd.Watch([]string{"key1"}) -// Check current value -val, _ := cmd.Get("key1") -// Only update if no one else modified since Watch -cmd.Set("key1", "new-value") -// Will fail if key1 changed between Watch and Exec -result, err := tx.Exec() -``` - -### Optimistic Concurrency Support - -Valkey's optimistic locking mechanism is supported through: - -- Implementation of `Watch()` that monitors keys for changes -- Implementation of `Unwatch()` that releases monitoring -- Proper handling of transaction failure when watched keys change -- Passing watch state through to the Valkey server during execution - -This enables patterns like: - -```go -cmd.Watch([]string{"key1"}) -// Check current value -val, _ := cmd.Get("key1") -// Only update if no one else modified since Watch -cmd.Set("key1", "new-value") -// Will fail if key1 changed between Watch and Exec -result, err := tx.Exec() -``` - -### Memory Management - -Memory management across language boundaries is a critical design consideration: - -- Go side uses explicit pinning to prevent garbage collection of memory shared with Rust -- Reference counting in the Rust layer ensures proper cleanup -- Explicit tracking of pending requests allows graceful handling of client closure - -### Discard Operation - - The `tx.Discard()` method can be used to explicitly cancel a transaction before execution. -This mechanism serves to: - -- Release server-side resources associated with the transaction when the client decides not to proceed. - Clean up client-side state, such as the command queue within the `TransactionExecutor`. -- Prevent the execution of unintended commands. - This functionality is achieved by sending the `DISCARD` command to the Valkey server, which then clears the server-side command queue for the transaction. The `tx.Discard()` method will return an error if communication with the server fails during this process. - -### Composition over inheritance - -The `transaction.go` defines the core transaction functionality: - -```go -type Transaction struct { - *baseClient // Embed baseClient to inherit methods - *GlideClient // Embed client for command chaining - *GlideClusterClient // Support for cluster environments - commands []Cmder // Queue for transaction commands -} -``` - -The `Transaction` struct uses composition by embedding both `baseClient` and the client types (`GlideClient` ,`GlideClusterClient`). This approach allows the transaction to inherit all methods from the client &cCommand implementations are shared between regular execution and transactions. -This composition approach allows the transaction to reuse most of the client code while overriding only the behavior that needs to change for transactions. - -## Conclusion - -The transaction in Valkey GLIDE go client provides a robust, idiomatic way to execute atomic operations in a Valkey database. Its design emphasizes developer experience, safety, and compatibility with Valkey semantics while providing a solid foundation for future enhancements. - -## Contributors - -The following contributors dedicated their efforts to implementing transactions in the Valkey GLIDE Go client: - -- [Omkar Mestry](https://github.com/omangesg) (Google Cloud Platform) -- [Edric Cuartero](https://github.com/EdricCua) (Google Cloud Platform) -- [Niharika Bhavaraju](https://github.com/niharikabhavaraju) (Google Cloud Platform) diff --git a/content/blog/2025-05-08-go-client-GA.md b/content/blog/2025-05-08-go-client-GA.md new file mode 100644 index 00000000..2f24466b --- /dev/null +++ b/content/blog/2025-05-08-go-client-GA.md @@ -0,0 +1,501 @@ ++++ +title= "Valkey Glide Go Client: Now Generally Available!" +description = "Valkey Glide Go client reaches general availability. Read to learn more about the go client designed for performance and developer productivity" +date= 2025-05-08 01:01:01 +authors= [ "niharikabhavaraju"] ++++ + +Valkey-Glide is pleased to announce the the general availability (GA) release of the GLIDE(General Language Independent Driver for the Enterprise) Go client. This release brings the power and reliability of Valkey to Go developers with an API designed for performance and developer productivity. + +Valkey GLIDE is a multi-language client for Valkey, designed for operational excellence and incorporating best practices refined through years of experience. GLIDE ensures a consistent and unified client experience across applications, regardless of the programming language. + +With support for Java, Node.js, Python, and now Go moving from public preview to general availability, this announcement introduces the Valkey GLIDE support for Go, expanding support to Go developers and providing new connectivity to Valkey servers, including both standalone and cluster deployments. + +## Why You Should Be Excited + +The Go client extends Valkey GLIDE to the Go community, offering a robust, client that's built on the battle-tested Rust core. This client library is a thoughtfully designed experience for Go developers who need reliable, high-performance data access. + +## What's new in GA? + +- The major addition in the GA release is comprehensive [transaction support](#transaction-support). +- Complete support for all Valkey commands encompassing scripting, functions, pubsub, server management, and all other operations in both standalone and cluster. + +## Key Features + +### Advanced Cluster Topology Management + +Connect to your Valkey cluster with minimal configuration. The client automatically detects the entire cluster topology and configures connection management based on industry best practices. + +```go +config := api.NewGlideClusterClientConfiguration(). + WithAddress(&api.NodeAddress{Host: "localhost", Port: 6379}) + +client, err := api.NewGlideClusterClient(config) +``` + +The Go client provides advanced topology managements features such as: + +#### Automatic Topology Discovery + +GLIDE automatically discovers all cluster nodes from a single seed node, eliminating the need to manually configure every node address. The NodeAddress can be an IP address, hostname, or fully qualified domain name (FQDN). + +#### Dynamic Topology Maintenance + +Cluster topology can change over time as nodes are added, removed, or when slot ownership changes. GLIDE implements several mechanisms to maintain an accurate view of the cluster: + +- **Proactive Topology Monitoring**: GLIDE performs periodic background checks for cluster topology changes. This approach ensures a comprehensive and up-to-date view of the cluster, improving availability and reducing tail latency. +- **Consensus-Based Resolution**: GLIDE queries multiple nodes for their topology view and selects the one with the highest agreement, reducing the risk of stale or incorrect mappings and ensuring a more accurate and up-to-date cluster view, improving the overall availability of the cluster. +- **Efficient Resource Management**: GLIDE employs an efficient algorithm to compare node views and dynamically throttles client-management requests to prevent overloading Valkey servers, ensuring a balance between maintaining an up-to-date topology map and optimizing resource utilization. + +### Enhanced Connection Management + +Connection management in distributed systems presents unique challenges that impact performance, reliability, and resource utilization. The Go client addresses these challenges with reliable solutions: + +#### Proactive Reconnection + +GLIDE implements a background monitoring system for connection states. By detecting disconnections and initiating reconnections preemptively, the client eliminates the reconnection latency typically experienced when a request discovers a broken connection. + +#### Connection Storm Prevention + +When network events occur, connection storms can overwhelm servers with simultaneous reconnection attempts. GLIDE mitigates this risk through backoff algorithm with jitter that distributes reconnection attempts over time, protecting servers from sudden connection surges. + +Robust connection handling with automatic reconnection strategies ensures your application remains resilient even during network instability: + +```go +// Configure a custom reconnection strategy with exponential backoff +config := api.NewGlideClientConfiguration(). + WithAddress(&api.NodeAddress{Host: "localhost", Port: 6379}). + WithReconnectStrategy(api.NewBackoffStrategy( + 5, // Initial delay in milliseconds + 10, // Maximum attempts + 50 // Maximum delay in milliseconds + )) +``` + +#### Multiplexed Connection + +Rather than maintaining connection pools, GLIDE establishes a single multiplexed connection per cluster node. This architectural choice: + +- Minimizes the total number of TCP connections to servers +- Reduces system call overhead +- Maintains high throughput through efficient connection pipelining +- Decreases server-side connection management burden + +### Built for Performance + +The Go client is designed from the ground up with performance in mind while still being simple to use. +The Go client provides a synchronous API for simplicity and compatibility with existing Go key-value store clients. While each individual command is blocking (following the familiar patterns in the ecosystem), the client is fully thread-safe and designed for concurrent usage: + +```go +// Example of concurrent execution using goroutines +func performConcurrentOperations(client *api.GlideClient) { + var wg sync.WaitGroup + + // Launch 10 concurrent operations + for i := 0; i < 10; i++ { + wg.Add(1) + go func(idx int) { + defer wg.Done() + key := fmt.Sprintf("key:%d", idx) + value := fmt.Sprintf("value:%d", idx) + + // Each command blocks within its goroutine, but all 10 run concurrently + _, err := client.Set(key, value) + if err != nil { + fmt.Printf("Error setting %s: %v\n", key, err) + return + } + + result, err := client.Get(key) + if err != nil { + fmt.Printf("Error getting %s: %v\n", key, err) + return + } + + fmt.Printf("Result for %s: %s\n", key, result) + }(i) + } + + wg.Wait() +} +``` + +Under the hood, the client efficiently handles these concurrent requests by: + +1. Using a single multiplexed connection per node to pipeline concurrent commands, minimizing socket overhead and system resources +2. Implementing thread-safe command execution +3. Efficiently routing concurrent commands to the appropriate server nodes + +While the current API is synchronous, the implementation is specifically optimized for concurrent usage through Go's native goroutines. We would love feedback about whether to add async/channel-based APIs in future releases. + +## Getting Started + +You can add Valkey GLIDE to your project with the following two commands: + +```bash +go get github.com/valkey-io/valkey-glide/go +go mod tidy +``` + +Then, you can get started connecting to a Valkey standalone server, running locally on port 6379, with the following sample applications: + +```go +package main + +import ( + "fmt" + "github.com/valkey-io/valkey-glide/go/api" +) + +func main() { + // Connect to a standalone Valkey server + config := api.NewGlideClientConfiguration(). + WithAddress(&api.NodeAddress{Host: "localhost", Port: 6379}) + + client, err := api.NewGlideClient(config) + if err != nil { + fmt.Println("Error:", err) + return + } + defer client.Close() + + // Test the connection + result, err := client.Ping() + if err != nil { + fmt.Println("Error:", err) + return + } + fmt.Println(result) // PONG + + // Store and retrieve a value + client.Set("hello", "valkey") + value, _ := client.Get("hello") + fmt.Println(value) // valkey +} +``` + +### Cluster Mode Connection Setup + +Need to work with a Valkey cluster? + +Just as easy! The Go client automatically discovers your entire cluster topology from a single seed node. The following sample shows how to connect to a Valkey cluster through a node running locally on port 7001: + +```go +package main + +import ( + "fmt" + "github.com/valkey-io/valkey-glide/go/api" +) + +func main() { + // Specify the address of any single node in your cluster + // This example connects to a local cluster node on port 7001 + host := "localhost" + port := 7001 + + // Connect to a Valkey cluster through any node + config := api.NewGlideClusterClientConfiguration(). + WithAddress(&api.NodeAddress{Host: host, Port: port}) + + client, err := api.NewGlideClusterClient(config) + if err != nil { + fmt.Println("There was an error: ", err) + return + } + + res, err := client.Ping() + if err != nil { + fmt.Println("There was an error: ", err) + return + } + fmt.Println(res) // PONG + client.Close() +} +``` + +## Advanced Configuration Options + +### Read Strategies for Optimized Performance + +Balance consistency and throughput with flexible read strategies: + +```go +// Configure to prefer replicas for read operations +config := api.NewGlideClusterClientConfiguration(). + WithAddress(&api.NodeAddress{Host: "cluster.example.com", Port: 6379}). + WithReadFrom(api.PreferReplica) + +client, err := api.NewGlideClusterClient(config) + +// Write to primary +client.Set("key1", "value1") + +// Automatically reads from a replica (round-robin) +result, err := client.Get("key1") +``` + +Available strategies: + +- **PRIMARY**: Always read from primary nodes for the freshest data +- **PREFER_REPLICA**: Distribute reads across replicas in round-robin fashion, falling back to primary when needed + +Planned for future release: + +- **AZ_AFFINITY**: (Coming soon) Prefer replicas in the same availability zone as the client + +### Authentication and TLS + +Secure your connections with built-in authentication and TLS support: + +```go +// Configure with authentication +config := api.NewGlideClientConfiguration(). + WithAddress(&api.NodeAddress{Host: "localhost", Port: 6379}). + WithCredentials(api.NewServerCredentials("username", "password")). + WithUseTLS(true) // Enable TLS for encrypted connections +``` + +### Request Timeout and Handling + +Fine-tune timeout settings for different workloads: + +```go +// Set a longer timeout for operations that may take more time +config := api.NewGlideClientConfiguration(). + WithAddress(&api.NodeAddress{Host: "localhost", Port: 6379}). + WithRequestTimeout(500) // 500ms timeout +``` + +## Behind the Scenes: Technical Architecture + +The Valkey GLIDE Go client is built on top of the Valkey GLIDE core. The core framework is written in Rust (lib.rs), which exposes public functions. These functions are converted to a C header file using Cbindgen. The Go client then uses CGO to call these C functions, providing Go developers with an idiomatic interface while leveraging Rust's performance advantages. This architecture ensures consistent behavior across all Valkey GLIDE language implementations (Java, Python, Node.js, and Go) while maintaining performance and reliability. + +### Component details + +```text ++------------+ +------+ +------------+ +------------+ +------------+ +| | | | | | | | | | +| Go |----->| |----->| C Header |----->| Rust |----->| Valkey | +| Client | | CGO | | cbindgen | | Core | | Server | +| |<-----| |<-----| |<-----| |<-----| | +| | | | | | | | | | ++------------+ +------+ +------------+ +------------+ +------------+ +``` + +- **Go Client**: The language-specific interface for Go developers +- **CGO**: Allows Go code to call C functions +- **Cbindgen**: Automates the generation of C header files from Rust public APIs +- **Rust Core**: High-performance framework that connects to and communicates with Valkey servers +- **Rust FFI Library**: Enables cross-language function calls between Rust and other languages + +## Transaction Support + +The Go client now includes full transaction support, enabling atomic operations across multiple commands. + +### Transaction Lifecycle + +**Create Transaction:** + + Initialize a transaction object. +Use `api.NewTransaction(client)` to create a new transaction with established client as an argument +`cmd := tx.GlideClient` grants access to a transaction-scoped commands interface, allowing you to construct a sequence of operations to be executed. We can access transaction commands via the embedded client. + +**Queue Commands:** + + Queue commands in the order they should be executed. +Each queued command is added to the transaction but not immediately executed.. +`cmd.CommandName` allows you to queue multiple transactions , +**Example:** + +```go +cmd.Set("key1","val1") +cmd.Get("key1") +``` + +**Execute Transaction:** + Run all queued commands atomically + After queueing the commands, transaction can be executed with `tx.Exec()` + All the queued commands will be executed and returns an response array which consists of the commands result. + +## Standalone Mode Transaction + +```go + // Standalone Client Transaction + config := api.NewGlideClientConfiguration(). + WithAddress(&api.NodeAddress{Host: "localhost", Port: 6379}) + + client, err := api.NewGlideClient(config) + if err != nil { + log.Fatal("error connecting to database: ", err) + } + defer client.Close() + + tx := api.NewTransaction(client) + cmd := tx.GlideClient + + // Queue multiple commands + cmd.Set("user:profile", "John Doe") + cmd.Set("user:email", "john@example.com") + cmd.SAdd("users", "john") + + // Execute transaction + results, err := tx.Exec() + if err != nil { + log.Printf("Transaction failed: %v", err) + return + } + + // Process results + for _, result := range results { + fmt.Println(result) + } +``` + +## Cluster mode Transaction + +To create a cluster transaction, initialize the cluster transaction object using `api.NewClusterTransaction(clusterClient)` to create a new cluster transaction with an established cluster client as an argument. +`cmd := tx.GlideClusterClient` grants access to a transaction-scoped cluster commands interface, allowing you to construct a sequence of operations to be executed. + +```go + // Cluster Client Transaction + clusterConfig := api.NewGlideClusterClientConfiguration(). + WithAddress(&api.NodeAddress{Host: "cluster1", Port: 7007}). + WithAddress(&api.NodeAddress{Host: "cluster2", Port: 7008}). + WithAddress(&api.NodeAddress{Host: "cluster3", Port: 7009}) + + clusterClient, err := api.NewGlideClusterClient(clusterConfig) + if err != nil { + log.Fatal("error connecting to cluster: ", err) + } + defer clusterClient.Close() + + clusterTx := api.NewClusterTransaction(clusterClient) + cmd := clusterTx.GlideClusterClient + + // Define routing option + var simpleRoute config.Route = config.RandomRoute + + // Create ping options with specific routing + pingOpts := options.ClusterPingOptions{ + PingOptions: &options.PingOptions{ + Message: "Hello Valkey Cluster", + }, + RouteOption: &options.RouteOption{ + Route: simpleRoute, + }, + } + + // Queue commands with routing + cmd.PingWithOptions(pingOpts) + // Execute transaction + results, err := clusterTx.Exec() + if err != nil { + log.Printf("Cluster transaction failed: %v", err) + return + } + + // Process results + for _, result := range results { + fmt.Println(result) + } +``` + +In cluster transactions, the routing option does not enable multi-node transaction execution. + +## Transaction design + +The transaction implementation in Valkey GLIDE was designed with several key goals, each achieved through specific implementation approaches: + +### Idiomatic Go API + +The design provides a natural, Go-like interface for transactions through: + +- Clean command calling syntax (`cmd.Set(...)`, `cmd.Get(...)`) +- Use of Go's embedding to inherit behavior from `baseClient` +- Explicit error handling that follows Go conventions (`result, err := tx.Exec()`) + +### Command Transparency + +A key design followed is that commands can be used the same way in both regular and transaction contexts: + +```go +// In regular client context - executes immediately +client.Set("key1", "value1") + +// In transaction context - identical syntax, but queued instead +tx := NewTransaction(client) +cmd := tx.GlideClient +cmd.Set("key1", "value1") // Queued for later execution +``` + +- Regular clients and transactions both implement a common interface +- When creating a transaction, it sets itself as the command executor +- In transaction mode, commands are queued instead of executed +- The code detects transaction context and handles results appropriately + +### Optimistic Concurrency Support + +Valkey's optimistic locking mechanism is supported through: + +- Implementation of `Watch()` that monitors keys for changes +- Implementation of `Unwatch()` that releases monitoring +- Proper handling of transaction failure when watched keys change +- Passing watch state through to the Valkey server during execution + +This enables patterns like: + +```go +cmd.Watch([]string{"key1"}) +// Check current value +val, _ := cmd.Get("key1") +// Only update if no one else modified since Watch +cmd.Set("key1", "new-value") +// Will fail if key1 changed between Watch and Exec +result, err := tx.Exec() +``` + +### Discard Operation + + The `tx.Discard()` method can be used to explicitly cancel a transaction before execution. + +This mechanism serves to: + +- Release server-side resources associated with the transaction when the client decides not to proceed. - Clean up client-side state, such as the command queue within the `TransactionExecutor`. +- Prevent the execution of unintended commands. + This functionality is achieved by sending the `DISCARD` command to the Valkey server, which then clears the server-side command queue for the transaction. The `tx.Discard()` method will return an error if communication with the server fails during this process. + +## Join the Journey + +We're actively developing and enhancing the Go client, and we'd love your feedback and contributions. Try it out in your projects, share your experiences, and help us make it even better! +You can join our development journey by: + +- Submitting issues or feature requests on our [GitHub Issues page](https://github.com/valkey-io/valkey-glide/issues) +- Joining discussions in our [GitHub Discussions forum](https://github.com/valkey-io/valkey-glide/discussions) + +## Looking Forward + +As we move toward general availability, we'll be expanding command support, enhancing performance, and adding even more features to make the Valkey GLIDE Go client a great choice for Go developers. + +Checkout our [Valkey GLIDE go client](https://github.com/valkey-io/valkey-glide/tree/main/go) for the source code. +For implementation examples, please refer to the [README of the Go examples](https://github.com/valkey-io/valkey-glide/blob/main/go/README.md) for instructions on running the Standalone and Cluster examples. + +For a complete reference of all available commands and their parameters, explore the [Go API documentation on pkg.go.dev](https://pkg.go.dev/github.com/valkey-io/valkey-glide/go/api), which provides detailed information on method signatures, parameters, and return types. + +## Contributors + +A huge thank you to all the contributors who have made this possible - your dedication and expertise have created something truly special for the Go community. + +[Janhavi Gupta](https://github.com/janhavigupta007) (Google Cloud Platform) + +[Niharika Bhavaraju](https://github.com/niharikabhavaraju) (Google Cloud Platform) + +[Edric Cuartero](https://github.com/EdricCua) (Google Cloud Platform) + +[Omkar Mestry](https://github.com/omangesg) (Google Cloud Platform) + +[Yury Fridlyand](https://github.com/Yury-Fridlyand) (Improving) + +[Prateek Kumar](https://github.com/prateek-kumar-improving) (Improving) + +Kudos to [Aaron Congo](https://github.com/aaron-congo) who created the backbone of the client 🚀 and to [Umit Unal](https://github.com/umit), [Michael](https://github.com/MikeMwita) for their contributions! From 18c0715bd48566ec69710e804b0983ba19aba564 Mon Sep 17 00:00:00 2001 From: Niharika Bhavaraju Date: Thu, 8 May 2025 11:11:42 +0000 Subject: [PATCH 03/13] minor change Signed-off-by: Niharika Bhavaraju --- content/blog/2025-05-08-go-client-GA.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/content/blog/2025-05-08-go-client-GA.md b/content/blog/2025-05-08-go-client-GA.md index 2f24466b..a06d9fa2 100644 --- a/content/blog/2025-05-08-go-client-GA.md +++ b/content/blog/2025-05-08-go-client-GA.md @@ -305,7 +305,8 @@ Use `api.NewTransaction(client)` to create a new transaction with established cl Queue commands in the order they should be executed. Each queued command is added to the transaction but not immediately executed.. -`cmd.CommandName` allows you to queue multiple transactions , +`cmd.CommandName` allows you to queue multiple transactions. + **Example:** ```go @@ -314,6 +315,7 @@ cmd.Get("key1") ``` **Execute Transaction:** + Run all queued commands atomically After queueing the commands, transaction can be executed with `tx.Exec()` All the queued commands will be executed and returns an response array which consists of the commands result. From 7e11c784992bd3599431ba842b4eaf3baaff2192 Mon Sep 17 00:00:00 2001 From: jbrinkman Date: Tue, 17 Jun 2025 15:58:31 -0400 Subject: [PATCH 04/13] docs: update Go client docs for v2 release with batch support and OpenTelemetry tracing Signed-off-by: jbrinkman --- content/blog/2025-05-08-go-client-GA.md | 385 ++++++++++++------------ 1 file changed, 198 insertions(+), 187 deletions(-) diff --git a/content/blog/2025-05-08-go-client-GA.md b/content/blog/2025-05-08-go-client-GA.md index a06d9fa2..ba4e6d33 100644 --- a/content/blog/2025-05-08-go-client-GA.md +++ b/content/blog/2025-05-08-go-client-GA.md @@ -1,24 +1,25 @@ +++ -title= "Valkey Glide Go Client: Now Generally Available!" -description = "Valkey Glide Go client reaches general availability. Read to learn more about the go client designed for performance and developer productivity" +title= "Valkey GLIDE Go Client: Now Generally Available!" +description = "Valkey GLIDE Go client reaches general availability. Read to learn more about the go client designed for performance and developer productivity" date= 2025-05-08 01:01:01 -authors= [ "niharikabhavaraju"] +authors= [ "niharikabhavaraju", "jbrinkman"] +++ -Valkey-Glide is pleased to announce the the general availability (GA) release of the GLIDE(General Language Independent Driver for the Enterprise) Go client. This release brings the power and reliability of Valkey to Go developers with an API designed for performance and developer productivity. +Valkey GLIDE is pleased to announce the general availability (GA) release of the GLIDE(General Language Independent Driver for the Enterprise) Go client. This release brings the power and reliability of Valkey to Go developers with an API designed for performance and developer productivity. Valkey GLIDE is a multi-language client for Valkey, designed for operational excellence and incorporating best practices refined through years of experience. GLIDE ensures a consistent and unified client experience across applications, regardless of the programming language. -With support for Java, Node.js, Python, and now Go moving from public preview to general availability, this announcement introduces the Valkey GLIDE support for Go, expanding support to Go developers and providing new connectivity to Valkey servers, including both standalone and cluster deployments. +With support for Java, Node.js, Python, and now Golang moving from public preview to general availability, this announcement introduces the Valkey GLIDE support for Go, expanding support to Golang developers and providing new connectivity to Valkey servers, including both standalone and cluster deployments. ## Why You Should Be Excited -The Go client extends Valkey GLIDE to the Go community, offering a robust, client that's built on the battle-tested Rust core. This client library is a thoughtfully designed experience for Go developers who need reliable, high-performance data access. +The Go client extends Valkey GLIDE to the Go community, offering a robust client that's built on the battle-tested Rust core. This client library is a thoughtfully designed experience for Go developers who need reliable, high-performance data access. ## What's new in GA? -- The major addition in the GA release is comprehensive [transaction support](#transaction-support). +- The major addition in the GA release is comprehensive [batch support](#batch-support) that includes both transactions and pipelines.. - Complete support for all Valkey commands encompassing scripting, functions, pubsub, server management, and all other operations in both standalone and cluster. +- Support for OpenTelemetry tracing which has been added to all language clients, including Go. ## Key Features @@ -27,10 +28,10 @@ The Go client extends Valkey GLIDE to the Go community, offering a robust, clien Connect to your Valkey cluster with minimal configuration. The client automatically detects the entire cluster topology and configures connection management based on industry best practices. ```go -config := api.NewGlideClusterClientConfiguration(). - WithAddress(&api.NodeAddress{Host: "localhost", Port: 6379}) +cfg := config.NewClusterClientConfiguration(). + WithAddress(&config.NodeAddress{Host: "localhost", Port: 6379}) -client, err := api.NewGlideClusterClient(config) +client, err := glide.NewClusterClient(cfg) ``` The Go client provides advanced topology managements features such as: @@ -41,7 +42,7 @@ GLIDE automatically discovers all cluster nodes from a single seed node, elimina #### Dynamic Topology Maintenance -Cluster topology can change over time as nodes are added, removed, or when slot ownership changes. GLIDE implements several mechanisms to maintain an accurate view of the cluster: +Cluster topology can change over time as nodes are added, removed, or when slot ownership changes. GLIDE implements several features to maintain an accurate view of the cluster: - **Proactive Topology Monitoring**: GLIDE performs periodic background checks for cluster topology changes. This approach ensures a comprehensive and up-to-date view of the cluster, improving availability and reducing tail latency. - **Consensus-Based Resolution**: GLIDE queries multiple nodes for their topology view and selects the one with the highest agreement, reducing the risk of stale or incorrect mappings and ensuring a more accurate and up-to-date cluster view, improving the overall availability of the cluster. @@ -57,18 +58,18 @@ GLIDE implements a background monitoring system for connection states. By detect #### Connection Storm Prevention -When network events occur, connection storms can overwhelm servers with simultaneous reconnection attempts. GLIDE mitigates this risk through backoff algorithm with jitter that distributes reconnection attempts over time, protecting servers from sudden connection surges. +When network events occur, connection storms can overwhelm servers with simultaneous reconnection attempts. GLIDE mitigates this risk through a backoff algorithm with jitter that distributes reconnection attempts over time, protecting servers from sudden connection surges. Robust connection handling with automatic reconnection strategies ensures your application remains resilient even during network instability: ```go // Configure a custom reconnection strategy with exponential backoff -config := api.NewGlideClientConfiguration(). - WithAddress(&api.NodeAddress{Host: "localhost", Port: 6379}). - WithReconnectStrategy(api.NewBackoffStrategy( - 5, // Initial delay in milliseconds - 10, // Maximum attempts - 50 // Maximum delay in milliseconds +cfg := config.NewClientConfiguration(). + WithAddress(&config.NodeAddress{Host: "localhost", Port: 6379}). + WithReconnectStrategy(config.NewBackoffStrategy( + 5, // Number of retry attempts, with increasing backoff between attempts. + 10, // Multiplier that will be applied to the delay between attempts. + 2 // Exponent base configured for the backoff strategy. )) ``` @@ -84,11 +85,11 @@ Rather than maintaining connection pools, GLIDE establishes a single multiplexed ### Built for Performance The Go client is designed from the ground up with performance in mind while still being simple to use. -The Go client provides a synchronous API for simplicity and compatibility with existing Go key-value store clients. While each individual command is blocking (following the familiar patterns in the ecosystem), the client is fully thread-safe and designed for concurrent usage: +The Go client provides a synchronous API for simplicity and compatibility with existing Golang key-value store clients. While each individual command is blocking (following the familiar patterns in the ecosystem), the client is fully thread-safe and designed for concurrent usage: ```go // Example of concurrent execution using goroutines -func performConcurrentOperations(client *api.GlideClient) { +func performConcurrentOperations(client *glide.Client) { var wg sync.WaitGroup // Launch 10 concurrent operations @@ -100,13 +101,13 @@ func performConcurrentOperations(client *api.GlideClient) { value := fmt.Sprintf("value:%d", idx) // Each command blocks within its goroutine, but all 10 run concurrently - _, err := client.Set(key, value) + _, err := client.Set(context.Background(), key, value) if err != nil { fmt.Printf("Error setting %s: %v\n", key, err) return } - result, err := client.Get(key) + result, err := client.Get(context.Background(), key) if err != nil { fmt.Printf("Error getting %s: %v\n", key, err) return @@ -133,26 +134,27 @@ While the current API is synchronous, the implementation is specifically optimiz You can add Valkey GLIDE to your project with the following two commands: ```bash -go get github.com/valkey-io/valkey-glide/go +go get github.com/valkey-io/valkey-glide/go/v2 go mod tidy ``` -Then, you can get started connecting to a Valkey standalone server, running locally on port 6379, with the following sample applications: +Then, you can get started connecting to a Valkey standalone server, running locally on port 6379, with the following sample code: ```go package main import ( "fmt" - "github.com/valkey-io/valkey-glide/go/api" + glide "github.com/valkey-io/valkey-glide/go/v2" + "github.com/valkey-io/valkey-glide/go/v2/config" ) func main() { // Connect to a standalone Valkey server - config := api.NewGlideClientConfiguration(). - WithAddress(&api.NodeAddress{Host: "localhost", Port: 6379}) + cfg := config.NewClientConfiguration(). + WithAddress(&config.NodeAddress{Host: "localhost", Port: 6379}) - client, err := api.NewGlideClient(config) + client, err := glide.NewClient(cfg) if err != nil { fmt.Println("Error:", err) return @@ -160,7 +162,7 @@ func main() { defer client.Close() // Test the connection - result, err := client.Ping() + result, err := client.Ping(context.Background()) if err != nil { fmt.Println("Error:", err) return @@ -168,8 +170,8 @@ func main() { fmt.Println(result) // PONG // Store and retrieve a value - client.Set("hello", "valkey") - value, _ := client.Get("hello") + client.Set(context.Background(), "hello", "valkey") + value, _ := client.Get(context.Background(), "hello") fmt.Println(value) // valkey } ``` @@ -185,7 +187,8 @@ package main import ( "fmt" - "github.com/valkey-io/valkey-glide/go/api" + glide "github.com/valkey-io/valkey-glide/go/v2" + "github.com/valkey-io/valkey-glide/go/v2/config" ) func main() { @@ -195,16 +198,16 @@ func main() { port := 7001 // Connect to a Valkey cluster through any node - config := api.NewGlideClusterClientConfiguration(). - WithAddress(&api.NodeAddress{Host: host, Port: port}) + cfg := config.NewClusterClientConfiguration(). + WithAddress(&config.NodeAddress{Host: host, Port: port}) - client, err := api.NewGlideClusterClient(config) + client, err := glide.NewClusterClient(cfg) if err != nil { fmt.Println("There was an error: ", err) return } - res, err := client.Ping() + res, err := client.Ping(context.Background()) if err != nil { fmt.Println("There was an error: ", err) return @@ -222,27 +225,25 @@ Balance consistency and throughput with flexible read strategies: ```go // Configure to prefer replicas for read operations -config := api.NewGlideClusterClientConfiguration(). - WithAddress(&api.NodeAddress{Host: "cluster.example.com", Port: 6379}). - WithReadFrom(api.PreferReplica) +cfg := config.NewClusterClientConfiguration(). + WithAddress(&config.NodeAddress{Host: "cluster.example.com", Port: 6379}). + WithReadFrom(config.PreferReplica) -client, err := api.NewGlideClusterClient(config) +client, err := glide.NewClusterClient(cfg) // Write to primary -client.Set("key1", "value1") +client.Set(context.Background(), "key1", "value1") // Automatically reads from a replica (round-robin) -result, err := client.Get("key1") +result, err := client.Get(context.Background(), "key1") ``` Available strategies: -- **PRIMARY**: Always read from primary nodes for the freshest data -- **PREFER_REPLICA**: Distribute reads across replicas in round-robin fashion, falling back to primary when needed - -Planned for future release: - -- **AZ_AFFINITY**: (Coming soon) Prefer replicas in the same availability zone as the client +- **Primary**: Always read from primary nodes for the freshest data +- **PreferReplica**: Distribute reads across replicas in round-robin fashion, falling back to primary when needed +- **AzAffinity**: Prefer replicas in the same availability zone as the client +- **AzAffinityReplicaAndPrimary**: Spread the read requests among nodes within the client's availability zone in a round-robin manner ### Authentication and TLS @@ -250,9 +251,9 @@ Secure your connections with built-in authentication and TLS support: ```go // Configure with authentication -config := api.NewGlideClientConfiguration(). - WithAddress(&api.NodeAddress{Host: "localhost", Port: 6379}). - WithCredentials(api.NewServerCredentials("username", "password")). +cfg := config.NewClientConfiguration(). + WithAddress(&config.NodeAddress{Host: "localhost", Port: 6379}). + WithCredentials(config.NewServerCredentials("username", "password")). WithUseTLS(true) // Enable TLS for encrypted connections ``` @@ -262,8 +263,8 @@ Fine-tune timeout settings for different workloads: ```go // Set a longer timeout for operations that may take more time -config := api.NewGlideClientConfiguration(). - WithAddress(&api.NodeAddress{Host: "localhost", Port: 6379}). +cfg := config.NewClientConfiguration(). + WithAddress(&config.NodeAddress{Host: "localhost", Port: 6379}). WithRequestTimeout(500) // 500ms timeout ``` @@ -274,136 +275,135 @@ The Valkey GLIDE Go client is built on top of the Valkey GLIDE core. The core fr ### Component details ```text -+------------+ +------+ +------------+ +------------+ +------------+ -| | | | | | | | | | -| Go |----->| |----->| C Header |----->| Rust |----->| Valkey | -| Client | | CGO | | cbindgen | | Core | | Server | -| |<-----| |<-----| |<-----| |<-----| | -| | | | | | | | | | -+------------+ +------+ +------------+ +------------+ +------------+ ++------------+ +------+ +------------+ +------------+ +------------+ +------------+ +| | | | | | | | | | | | +| Go |----->| |----->| C Header |----->| FFI |----->| Rust |----->| Valkey | +| Client | | CGO | | cbindgen | | Library | | Core | | Server | +| |<-----| |<-----| |<-----| |<-----| |<-----| | +| | | | | | | | | | | | ++------------+ +------+ +------------+ +------------+ +------------+ +------------+ ``` - **Go Client**: The language-specific interface for Go developers - **CGO**: Allows Go code to call C functions -- **Cbindgen**: Automates the generation of C header files from Rust public APIs -- **Rust Core**: High-performance framework that connects to and communicates with Valkey servers +- **Cbindgen**: Automates the generation of C header files from Rust FFI Library - **Rust FFI Library**: Enables cross-language function calls between Rust and other languages +- **Rust Core**: High-performance framework that connects to and communicates with Valkey servers -## Transaction Support - -The Go client now includes full transaction support, enabling atomic operations across multiple commands. -### Transaction Lifecycle +## Batch Support -**Create Transaction:** +The Go client now includes full batch support, including transactions and pipelines, enabling atomic and non-atomic operations across multiple commands. - Initialize a transaction object. -Use `api.NewTransaction(client)` to create a new transaction with established client as an argument -`cmd := tx.GlideClient` grants access to a transaction-scoped commands interface, allowing you to construct a sequence of operations to be executed. We can access transaction commands via the embedded client. +### Batch Lifecycle -**Queue Commands:** +**Create Batch:** - Queue commands in the order they should be executed. -Each queued command is added to the transaction but not immediately executed.. -`cmd.CommandName` allows you to queue multiple transactions. - -**Example:** +Define a new batch using `pipeline.NewStandaloneBatch(isAtomic)` or `pipeline.NewClusterBatch(isAtomic)` to begin the process of creating a batch. `isAtomic` is a boolean that determines whether the batch should be executed atomically using Valkey's transaction features, or executed non-atomically using the pipeline functionality. ```go -cmd.Set("key1","val1") -cmd.Get("key1") + // Create a new transaction + batch := pipeline.NewStandaloneBatch(true) // Atomic batch ``` -**Execute Transaction:** +**Queue Commands:** - Run all queued commands atomically - After queueing the commands, transaction can be executed with `tx.Exec()` - All the queued commands will be executed and returns an response array which consists of the commands result. + Queue commands in the order they should be executed. The batch API supports chaining commands together to build a sequence of operations. This sequence definition is added to the batch but not immediately executed. -## Standalone Mode Transaction +**Example:** ```go - // Standalone Client Transaction - config := api.NewGlideClientConfiguration(). - WithAddress(&api.NodeAddress{Host: "localhost", Port: 6379}) - - client, err := api.NewGlideClient(config) - if err != nil { - log.Fatal("error connecting to database: ", err) - } - defer client.Close() + batch.Set("key1","val1").Get("key1") +``` - tx := api.NewTransaction(client) - cmd := tx.GlideClient +**Execute Batch:** - // Queue multiple commands - cmd.Set("user:profile", "John Doe") - cmd.Set("user:email", "john@example.com") - cmd.SAdd("users", "john") + Run all queued commands + After queueing the commands, a batch can be executed with `client.Exec()` + All the queued commands will be executed and return a response array which consists of the command results. - // Execute transaction - results, err := tx.Exec() - if err != nil { - log.Printf("Transaction failed: %v", err) - return - } +## Standalone Mode Batch - // Process results - for _, result := range results { - fmt.Println(result) - } +```go + // Standalone BatchClient Example + cfg := config.NewClientConfiguration(). + WithAddress(&config.NodeAddress{Host: "localhost", Port: 6379}) + + client, err := NewClient(cfg) + if err != nil { + log.Fatal("error connecting to database: ", err) + } + defer client.Close() + + // Create new transaction + tx := pipeline.NewStandaloneBatch(true). + Set("user:profile", "John Doe"). + Set("user:email", "john@example.com"). + SAdd("users", []string{"john"}) + + // Command sequence + txs := []string{"Set - user:profile", "Set - user:email", "SAdd - users"} + + // Execute transaction + results, err := client.Exec(context.Background(), *tx, false) // false = process all commands and include errors as part of the response array + if err != nil { + log.Printf("Transaction failed: %v", err) + return + } + + // Process results + for i, result := range results { + fmt.Printf("Command %s results: %v\n", txs[i], result) + } ``` -## Cluster mode Transaction +## Cluster mode Batch -To create a cluster transaction, initialize the cluster transaction object using `api.NewClusterTransaction(clusterClient)` to create a new cluster transaction with an established cluster client as an argument. -`cmd := tx.GlideClusterClient` grants access to a transaction-scoped cluster commands interface, allowing you to construct a sequence of operations to be executed. +Cluster batches are similar to standalone batches, but they are executed on a cluster client. They also may have specific routing options to control which node the commands are executed on. In a cluster batch, all commands are executed on the same node, unless the IsAtomic flag is set to false (i.e. is a pipeline and not a transaction), in which case the commands may be split based on the key slot. ```go - // Cluster Client Transaction - clusterConfig := api.NewGlideClusterClientConfiguration(). - WithAddress(&api.NodeAddress{Host: "cluster1", Port: 7007}). - WithAddress(&api.NodeAddress{Host: "cluster2", Port: 7008}). - WithAddress(&api.NodeAddress{Host: "cluster3", Port: 7009}) - - clusterClient, err := api.NewGlideClusterClient(clusterConfig) - if err != nil { - log.Fatal("error connecting to cluster: ", err) - } - defer clusterClient.Close() - - clusterTx := api.NewClusterTransaction(clusterClient) - cmd := clusterTx.GlideClusterClient - - // Define routing option - var simpleRoute config.Route = config.RandomRoute - - // Create ping options with specific routing - pingOpts := options.ClusterPingOptions{ - PingOptions: &options.PingOptions{ - Message: "Hello Valkey Cluster", - }, - RouteOption: &options.RouteOption{ - Route: simpleRoute, - }, - } - - // Queue commands with routing - cmd.PingWithOptions(pingOpts) - // Execute transaction - results, err := clusterTx.Exec() - if err != nil { - log.Printf("Cluster transaction failed: %v", err) - return - } - - // Process results - for _, result := range results { - fmt.Println(result) - } + // Cluster BatchClient Example + // Only a single node is required to connect to the Cluster + cfg := config.NewClusterClientConfiguration(). + WithAddress(&config.NodeAddress{Host: "localhost", Port: 7010}) + + clusterClient, err := NewClusterClient(cfg) + if err != nil { + log.Fatal("error connecting to cluster: ", err) + } + defer clusterClient.Close() + + // Create new transaction + clusterTx := pipeline.NewClusterBatch(true) + + // Create ping options with specific routing + pingOpts := options.PingOptions{ + Message: "Hello Valkey Cluster", + } + + // Queue commands with routing + clusterTx.PingWithOptions(pingOpts) + + // Command sequence + txs := []string{"Ping - 'Hello Valkey Cluster'"} + + opts := pipeline.NewClusterBatchOptions(). + WithRoute(config.RandomRoute) + + // Execute transaction + results, err := clusterClient.ExecWithOptions(context.Background(), *clusterTx, false, *opts) + if err != nil { + log.Printf("Cluster transaction failed: %v", err) + return + } + + // Process results + for i, result := range results { + fmt.Printf("Command %s results: %v\n", txs[i], result) + } ``` -In cluster transactions, the routing option does not enable multi-node transaction execution. +In cluster transactions, the routing option does not enable multi-node transaction execution. ## Transaction design @@ -413,28 +413,33 @@ The transaction implementation in Valkey GLIDE was designed with several key goa The design provides a natural, Go-like interface for transactions through: -- Clean command calling syntax (`cmd.Set(...)`, `cmd.Get(...)`) -- Use of Go's embedding to inherit behavior from `baseClient` -- Explicit error handling that follows Go conventions (`result, err := tx.Exec()`) +- Clean command calling syntax (`tx.Set(...)`, `tx.Get(...)`) +- Use of Go's embedding to inherit behavior from `BaseBatch` +- Explicit error handling that follows Go conventions (`result, err := pipeline.Exec()`) +- Use of method chaining to efficiently build command sequences (e.g. `tx.Set(...).Set(...).SAdd(...)`) +- Use of Go context for timeout and cancellation support ### Command Transparency -A key design followed is that commands can be used the same way in both regular and transaction contexts: +A key design followed is that commands share almost identical APIs, with the primary difference being when they are executed: ```go -// In regular client context - executes immediately -client.Set("key1", "value1") - -// In transaction context - identical syntax, but queued instead -tx := NewTransaction(client) -cmd := tx.GlideClient -cmd.Set("key1", "value1") // Queued for later execution +// In regular client context +client := glide.NewClient(...) +// Executes immediately with context +client.Set(context.Background(), "key1", "value1") + +// In transaction context +tx := pipeline.NewStandaloneBatch(true) +// Queued for later execution +tx.Set("key1", "value1") +// Execute transaction with context +results, err := client.Exec(context.Background(), *tx, false) ``` -- Regular clients and transactions both implement a common interface -- When creating a transaction, it sets itself as the command executor -- In transaction mode, commands are queued instead of executed -- The code detects transaction context and handles results appropriately +- While the server-side command execution is the same, the client-side result and error handling may differ +- Go context is passed when the command is executed +- Cluster mode and standalone mode commands are the same with only a few exceptions for Select, Move, Scan, and PubSub commands. ### Optimistic Concurrency Support @@ -448,25 +453,16 @@ Valkey's optimistic locking mechanism is supported through: This enables patterns like: ```go -cmd.Watch([]string{"key1"}) -// Check current value -val, _ := cmd.Get("key1") -// Only update if no one else modified since Watch -cmd.Set("key1", "new-value") +client.Watch(context.Background(), []string{"key1"}) +tx := pipeline.NewStandaloneBatch(true). + Get("key1"). + Set("key1", "new-value") + // Will fail if key1 changed between Watch and Exec -result, err := tx.Exec() +raiseOnError := true +results, err := client.Exec(context.Background(), *tx, raiseOnError) ``` -### Discard Operation - - The `tx.Discard()` method can be used to explicitly cancel a transaction before execution. - -This mechanism serves to: - -- Release server-side resources associated with the transaction when the client decides not to proceed. - Clean up client-side state, such as the command queue within the `TransactionExecutor`. -- Prevent the execution of unintended commands. - This functionality is achieved by sending the `DISCARD` command to the Valkey server, which then clears the server-side command queue for the transaction. The `tx.Discard()` method will return an error if communication with the server fails during this process. - ## Join the Journey We're actively developing and enhancing the Go client, and we'd love your feedback and contributions. Try it out in your projects, share your experiences, and help us make it even better! @@ -477,12 +473,12 @@ You can join our development journey by: ## Looking Forward -As we move toward general availability, we'll be expanding command support, enhancing performance, and adding even more features to make the Valkey GLIDE Go client a great choice for Go developers. +As we move forward, we'll continue enhancing performance, and adding even more features to make the Valkey GLIDE Go client a great choice for Go developers. Checkout our [Valkey GLIDE go client](https://github.com/valkey-io/valkey-glide/tree/main/go) for the source code. -For implementation examples, please refer to the [README of the Go examples](https://github.com/valkey-io/valkey-glide/blob/main/go/README.md) for instructions on running the Standalone and Cluster examples. +For more details on how to get started with Valkey GLIDE Go client, please refer to the [README](https://github.com/valkey-io/valkey-glide/blob/main/go/README.md) for instructions on running the Standalone and Cluster examples. -For a complete reference of all available commands and their parameters, explore the [Go API documentation on pkg.go.dev](https://pkg.go.dev/github.com/valkey-io/valkey-glide/go/api), which provides detailed information on method signatures, parameters, and return types. +For a complete reference of all available commands and their parameters, explore the [Go API documentation on pkg.go.dev](https://pkg.go.dev/github.com/valkey-io/valkey-glide/go/v2), which provides detailed information on method signatures, parameters, and return types. ## Contributors @@ -500,4 +496,19 @@ A huge thank you to all the contributors who have made this possible - your dedi [Prateek Kumar](https://github.com/prateek-kumar-improving) (Improving) +[James Xin](https://github.com/jamesx-improving) (Improving) + +[Jonathan Louie](https://github.com/jonathanl-bq) (Improving) + +[TJ Zhang](https://github.com/tjzhang-BQ) (Improving) + +[Joe Brinkman](https://github.com/jbrinkman) (Improving) + +[Edward Liang](https://github.com/edlng) (Improving) + +[Chloe Yip](https://github.com/cyip10) (Improving) + +[YiPin Chen](https://github.com/yipin-chen) (Improving) + + Kudos to [Aaron Congo](https://github.com/aaron-congo) who created the backbone of the client 🚀 and to [Umit Unal](https://github.com/umit), [Michael](https://github.com/MikeMwita) for their contributions! From c547d06a46a8408646a2f900805528b8f046f910 Mon Sep 17 00:00:00 2001 From: jbrinkman Date: Tue, 17 Jun 2025 17:02:15 -0400 Subject: [PATCH 05/13] docs: apply changes based on feedback in pr253 Signed-off-by: jbrinkman --- content/blog/2025-05-08-go-client-GA.md | 65 ++++++++++--------------- 1 file changed, 26 insertions(+), 39 deletions(-) diff --git a/content/blog/2025-05-08-go-client-GA.md b/content/blog/2025-05-08-go-client-GA.md index ba4e6d33..c71540ca 100644 --- a/content/blog/2025-05-08-go-client-GA.md +++ b/content/blog/2025-05-08-go-client-GA.md @@ -5,20 +5,16 @@ date= 2025-05-08 01:01:01 authors= [ "niharikabhavaraju", "jbrinkman"] +++ -Valkey GLIDE is pleased to announce the general availability (GA) release of the GLIDE(General Language Independent Driver for the Enterprise) Go client. This release brings the power and reliability of Valkey to Go developers with an API designed for performance and developer productivity. +The Valkey GLIDE contributors are pleased to announce the general availability (GA) release of the GLIDE (General Language Independent Driver for the Enterprise) Go client. This release brings the power and reliability of Valkey to Go developers with an API designed for performance and developer productivity. -Valkey GLIDE is a multi-language client for Valkey, designed for operational excellence and incorporating best practices refined through years of experience. GLIDE ensures a consistent and unified client experience across applications, regardless of the programming language. - -With support for Java, Node.js, Python, and now Golang moving from public preview to general availability, this announcement introduces the Valkey GLIDE support for Go, expanding support to Golang developers and providing new connectivity to Valkey servers, including both standalone and cluster deployments. - -## Why You Should Be Excited +Vakey GLIDE 2.0 brings exciting new features for all of our language clients (Java, Node.js, Python, and now Go). These features include pipelines, OpenTelemetry support, MacOS x86 support, and improved performance and bug fixes. The Go client extends Valkey GLIDE to the Go community, offering a robust client that's built on the battle-tested Rust core. This client library is a thoughtfully designed experience for Go developers who need reliable, high-performance data access. ## What's new in GA? -- The major addition in the GA release is comprehensive [batch support](#batch-support) that includes both transactions and pipelines.. -- Complete support for all Valkey commands encompassing scripting, functions, pubsub, server management, and all other operations in both standalone and cluster. +- The major addition in the GA release is comprehensive [batch support](#batch-support) that includes both transactions and pipelines. +- Complete support for all Valkey commands encompassing scripting, functions, pubsub, server management, and all other operations in both standalone and cluster modes. - Support for OpenTelemetry tracing which has been added to all language clients, including Go. ## Key Features @@ -123,11 +119,11 @@ func performConcurrentOperations(client *glide.Client) { Under the hood, the client efficiently handles these concurrent requests by: -1. Using a single multiplexed connection per node to pipeline concurrent commands, minimizing socket overhead and system resources -2. Implementing thread-safe command execution -3. Efficiently routing concurrent commands to the appropriate server nodes +1. Using a single multiplexed connection per node to pipeline concurrent commands, minimizing socket overhead and system resources, +2. Implementing thread-safe command execution, +3. Efficiently routing concurrent commands to the appropriate server nodes. -While the current API is synchronous, the implementation is specifically optimized for concurrent usage through Go's native goroutines. We would love feedback about whether to add async/channel-based APIs in future releases. +While the current API is synchronous, the implementation is specifically optimized for concurrent usage through Go's native goroutines. We would love feedback about whether to add async/channel-based APIs in future releases, or anything else you think could help improve Valkey GLIDE. You can create an issue on our [GitHub repository](https://github.com/valkey-io/valkey-glide/issues) to request a feature or report a bug, or start a discussion in our [GitHub Discussions forum](https://github.com/valkey-io/valkey-glide/discussions) to share your thoughts. ## Getting Started @@ -178,9 +174,7 @@ func main() { ### Cluster Mode Connection Setup -Need to work with a Valkey cluster? - -Just as easy! The Go client automatically discovers your entire cluster topology from a single seed node. The following sample shows how to connect to a Valkey cluster through a node running locally on port 7001: +Valkey GLIDE makes it just as easy to work with a Valkey cluster! The Go client automatically discovers your entire cluster topology from a single seed node. The following sample shows how to connect to a Valkey cluster through a node running locally on port 7010: ```go package main @@ -193,9 +187,9 @@ import ( func main() { // Specify the address of any single node in your cluster - // This example connects to a local cluster node on port 7001 + // This example connects to a local cluster node on port 7010 host := "localhost" - port := 7001 + port := 7010 // Connect to a Valkey cluster through any node cfg := config.NewClusterClientConfiguration(). @@ -299,11 +293,11 @@ The Go client now includes full batch support, including transactions and pipeli **Create Batch:** -Define a new batch using `pipeline.NewStandaloneBatch(isAtomic)` or `pipeline.NewClusterBatch(isAtomic)` to begin the process of creating a batch. `isAtomic` is a boolean that determines whether the batch should be executed atomically using Valkey's transaction features, or executed non-atomically using the pipeline functionality. +Define a new batch using `pipeline.NewStandaloneBatch(isAtomic)` or `pipeline.NewClusterBatch(isAtomic)` to begin the process of creating a batch. `isAtomic` is a boolean that determines whether the batch should be executed atomically using Valkey's transaction features, or executed non-atomically using the pipeline functionality. This pipeline feature is new for all languages in Valkey GLIDE 2.0 and provides even greater performance where atomicity is not required. ```go // Create a new transaction - batch := pipeline.NewStandaloneBatch(true) // Atomic batch + batch := pipeline.NewStandaloneBatch(true) // Atomic batch == transaction ``` **Queue Commands:** @@ -320,7 +314,7 @@ Define a new batch using `pipeline.NewStandaloneBatch(isAtomic)` or `pipeline.Ne Run all queued commands After queueing the commands, a batch can be executed with `client.Exec()` - All the queued commands will be executed and return a response array which consists of the command results. + All the queued commands will be executed and return a response array which consists of the command results. Since the batch definition is separate from the execution, you can re-execute the same batch against different clients providing added flexibility. ## Standalone Mode Batch @@ -359,7 +353,7 @@ Define a new batch using `pipeline.NewStandaloneBatch(isAtomic)` or `pipeline.Ne ## Cluster mode Batch -Cluster batches are similar to standalone batches, but they are executed on a cluster client. They also may have specific routing options to control which node the commands are executed on. In a cluster batch, all commands are executed on the same node, unless the IsAtomic flag is set to false (i.e. is a pipeline and not a transaction), in which case the commands may be split based on the key slot. +Cluster batches are similar to standalone batches, but they are executed on a cluster client. They may have specific routing options to control which node the commands are executed on. In a cluster batch, all commands are executed on the same node, unless the IsAtomic flag is set to false (i.e. is a pipeline and not a transaction), in which case the commands may be split based on the key slot. Once all commands have been executed, the results (including any errors) are returned to the client. For pipelines, the results are returned in the same order as the commands were queued, even when the commands are executed on different nodes. ```go // Cluster BatchClient Example @@ -403,15 +397,13 @@ Cluster batches are similar to standalone batches, but they are executed on a cl } ``` -In cluster transactions, the routing option does not enable multi-node transaction execution. +## Batch design -## Transaction design - -The transaction implementation in Valkey GLIDE was designed with several key goals, each achieved through specific implementation approaches: +The batch implementation in Valkey GLIDE was designed with several key goals, each achieved through specific implementation approaches: ### Idiomatic Go API -The design provides a natural, Go-like interface for transactions through: +The design provides a natural, Go-like interface for batches through: - Clean command calling syntax (`tx.Set(...)`, `tx.Get(...)`) - Use of Go's embedding to inherit behavior from `BaseBatch` @@ -421,7 +413,7 @@ The design provides a natural, Go-like interface for transactions through: ### Command Transparency -A key design followed is that commands share almost identical APIs, with the primary difference being when they are executed: +A important principle in the design of the client was transparently accessing the command, in both regular and batch contexts. While the server-side command execution is the same, the client-side result and error handling will differ since the batch results must account for multiple commands and potential errors. Go context is passed when the command is executed: with regular clients this occurs when the command is called, and in batches this occurs when the batch is executed. In batches, Cluster mode and standalone mode commands are the same with only a few exceptions for Select, Move, Scan, and PubSub commands. This is slightly different from regular contexts where some commands may have specific routing options that are available when using the ClusterClient. ```go // In regular client context @@ -437,18 +429,14 @@ tx.Set("key1", "value1") results, err := client.Exec(context.Background(), *tx, false) ``` -- While the server-side command execution is the same, the client-side result and error handling may differ -- Go context is passed when the command is executed -- Cluster mode and standalone mode commands are the same with only a few exceptions for Select, Move, Scan, and PubSub commands. - ### Optimistic Concurrency Support Valkey's optimistic locking mechanism is supported through: -- Implementation of `Watch()` that monitors keys for changes -- Implementation of `Unwatch()` that releases monitoring -- Proper handling of transaction failure when watched keys change -- Passing watch state through to the Valkey server during execution +- Implementation of `Watch()` that monitors keys for changes, +- Implementation of `Unwatch()` that releases monitoring, +- Proper handling of transaction failure when watched keys change, +- Passing watch state through to the Valkey server during execution. This enables patterns like: @@ -473,7 +461,7 @@ You can join our development journey by: ## Looking Forward -As we move forward, we'll continue enhancing performance, and adding even more features to make the Valkey GLIDE Go client a great choice for Go developers. +As we move forward, we'll continue enhancing performance, and adding even more features to the Valkey GLIDE Go client to continue building on the great foundation provided by Valkey GLIDE 2.0. Checkout our [Valkey GLIDE go client](https://github.com/valkey-io/valkey-glide/tree/main/go) for the source code. For more details on how to get started with Valkey GLIDE Go client, please refer to the [README](https://github.com/valkey-io/valkey-glide/blob/main/go/README.md) for instructions on running the Standalone and Cluster examples. @@ -482,7 +470,7 @@ For a complete reference of all available commands and their parameters, explore ## Contributors -A huge thank you to all the contributors who have made this possible - your dedication and expertise have created something truly special for the Go community. +A huge thank you to all the contributors and community members who have made this possible - your dedication and expertise have created something truly special for the Go community. [Janhavi Gupta](https://github.com/janhavigupta007) (Google Cloud Platform) @@ -510,5 +498,4 @@ A huge thank you to all the contributors who have made this possible - your dedi [YiPin Chen](https://github.com/yipin-chen) (Improving) - -Kudos to [Aaron Congo](https://github.com/aaron-congo) who created the backbone of the client 🚀 and to [Umit Unal](https://github.com/umit), [Michael](https://github.com/MikeMwita) for their contributions! +Kudos to [Aaron Congo](https://github.com/aaron-congo) who created the backbone of the client 🚀, and to [Umit Unal](https://github.com/umit), [Michael](https://github.com/MikeMwita) for their code contributions, and to [Marcin Dobosz](https://github.com/marcind) who provided invaluable feedback and suggestions that pushed the team to keep improving on a daily basis! From 8d328cff2e4ef2d939145774e4e423e7c03d0043 Mon Sep 17 00:00:00 2001 From: Joseph Brinkman Date: Tue, 17 Jun 2025 20:12:57 -0400 Subject: [PATCH 06/13] Update content/blog/2025-05-08-go-client-GA.md Co-authored-by: Yury-Fridlyand Signed-off-by: Joseph Brinkman --- content/blog/2025-05-08-go-client-GA.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2025-05-08-go-client-GA.md b/content/blog/2025-05-08-go-client-GA.md index c71540ca..c110082a 100644 --- a/content/blog/2025-05-08-go-client-GA.md +++ b/content/blog/2025-05-08-go-client-GA.md @@ -5,7 +5,7 @@ date= 2025-05-08 01:01:01 authors= [ "niharikabhavaraju", "jbrinkman"] +++ -The Valkey GLIDE contributors are pleased to announce the general availability (GA) release of the GLIDE (General Language Independent Driver for the Enterprise) Go client. This release brings the power and reliability of Valkey to Go developers with an API designed for performance and developer productivity. +The Valkey GLIDE contributors are pleased to announce the general availability (GA) release of the Valkey GLIDE Go client. This release brings the power and reliability of Valkey to Go developers with an API designed for performance and developer productivity. Vakey GLIDE 2.0 brings exciting new features for all of our language clients (Java, Node.js, Python, and now Go). These features include pipelines, OpenTelemetry support, MacOS x86 support, and improved performance and bug fixes. From 8e609e4341a8af90038fd669eb3d87e9b61679a8 Mon Sep 17 00:00:00 2001 From: jbrinkman Date: Tue, 17 Jun 2025 20:57:52 -0400 Subject: [PATCH 07/13] docs: apply changes based on feedback Signed-off-by: jbrinkman --- content/blog/2025-05-08-go-client-GA.md | 32 ++++++++++++++++++------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/content/blog/2025-05-08-go-client-GA.md b/content/blog/2025-05-08-go-client-GA.md index c110082a..1827ab33 100644 --- a/content/blog/2025-05-08-go-client-GA.md +++ b/content/blog/2025-05-08-go-client-GA.md @@ -16,6 +16,7 @@ The Go client extends Valkey GLIDE to the Go community, offering a robust client - The major addition in the GA release is comprehensive [batch support](#batch-support) that includes both transactions and pipelines. - Complete support for all Valkey commands encompassing scripting, functions, pubsub, server management, and all other operations in both standalone and cluster modes. - Support for OpenTelemetry tracing which has been added to all language clients, including Go. +- Go context support for all commands and batch operations. ## Key Features @@ -269,18 +270,17 @@ The Valkey GLIDE Go client is built on top of the Valkey GLIDE core. The core fr ### Component details ```text -+------------+ +------+ +------------+ +------------+ +------------+ +------------+ -| | | | | | | | | | | | -| Go |----->| |----->| C Header |----->| FFI |----->| Rust |----->| Valkey | -| Client | | CGO | | cbindgen | | Library | | Core | | Server | -| |<-----| |<-----| |<-----| |<-----| |<-----| | -| | | | | | | | | | | | -+------------+ +------+ +------------+ +------------+ +------------+ +------------+ ++------------+ +------+ +------------+ +------------+ +------------+ +| | | | | | | | | | +| Go |----->| |----->| FFI |----->| Rust |----->| Valkey | +| Client | | CGO | | Library | | Core | | Server | +| |<-----| |<-----| |<-----| |<-----| | +| | | | | | | | | | ++------------+ +------+ +------------+ +------------+ +------------+ ``` - **Go Client**: The language-specific interface for Go developers - **CGO**: Allows Go code to call C functions -- **Cbindgen**: Automates the generation of C header files from Rust FFI Library - **Rust FFI Library**: Enables cross-language function calls between Rust and other languages - **Rust Core**: High-performance framework that connects to and communicates with Valkey servers @@ -472,6 +472,16 @@ For a complete reference of all available commands and their parameters, explore A huge thank you to all the contributors and community members who have made this possible - your dedication and expertise have created something truly special for the Go community. +[Bar Shaul](https://github.com/barshaul) (Amazon Web Services) + +[Shoham Elias](https://github.com/shohamazon) (Amazon Web Services) + +[Adar Ovadia](https://github.com/adarovadya) (Amazon Web Services) + +[Meital Krasikow](https://github.com/meitalkra) (Amazon Web Services) + +[Asaf Porat Stoler](https://github.com/asafpamzn) (Amazon Web Services) + [Janhavi Gupta](https://github.com/janhavigupta007) (Google Cloud Platform) [Niharika Bhavaraju](https://github.com/niharikabhavaraju) (Google Cloud Platform) @@ -480,6 +490,8 @@ A huge thank you to all the contributors and community members who have made thi [Omkar Mestry](https://github.com/omangesg) (Google Cloud Platform) +[Vikas Pandey](https://github.com/vikas) (Google Cloud Platform) + [Yury Fridlyand](https://github.com/Yury-Fridlyand) (Improving) [Prateek Kumar](https://github.com/prateek-kumar-improving) (Improving) @@ -498,4 +510,6 @@ A huge thank you to all the contributors and community members who have made thi [YiPin Chen](https://github.com/yipin-chen) (Improving) -Kudos to [Aaron Congo](https://github.com/aaron-congo) who created the backbone of the client 🚀, and to [Umit Unal](https://github.com/umit), [Michael](https://github.com/MikeMwita) for their code contributions, and to [Marcin Dobosz](https://github.com/marcind) who provided invaluable feedback and suggestions that pushed the team to keep improving on a daily basis! +[Andrew](https://github.com/andrew) + +Kudos to [Aaron Congo](https://github.com/aaron-congo) who created the backbone of the client 🚀, to [Umit Unal](https://github.com/umit) and [Michael](https://github.com/MikeMwita) for their early code contributions, and finally to [Marcin Dobosz](https://github.com/marcind) who provided invaluable community feedback and suggestions that pushed the team to keep improving on a daily basis! From c8a923788decdb4060668f21f402bfdeaf09baf7 Mon Sep 17 00:00:00 2001 From: Joseph Brinkman Date: Tue, 17 Jun 2025 20:59:02 -0400 Subject: [PATCH 08/13] Update content/blog/2025-05-08-go-client-GA.md Co-authored-by: Yury-Fridlyand Signed-off-by: Joseph Brinkman --- content/blog/2025-05-08-go-client-GA.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2025-05-08-go-client-GA.md b/content/blog/2025-05-08-go-client-GA.md index 1827ab33..4dc894a9 100644 --- a/content/blog/2025-05-08-go-client-GA.md +++ b/content/blog/2025-05-08-go-client-GA.md @@ -353,7 +353,7 @@ Define a new batch using `pipeline.NewStandaloneBatch(isAtomic)` or `pipeline.Ne ## Cluster mode Batch -Cluster batches are similar to standalone batches, but they are executed on a cluster client. They may have specific routing options to control which node the commands are executed on. In a cluster batch, all commands are executed on the same node, unless the IsAtomic flag is set to false (i.e. is a pipeline and not a transaction), in which case the commands may be split based on the key slot. Once all commands have been executed, the results (including any errors) are returned to the client. For pipelines, the results are returned in the same order as the commands were queued, even when the commands are executed on different nodes. +Cluster batches are similar to standalone batches, but they are executed on a cluster client. They may have specific routing options to control which node the commands are executed on. In a cluster batch, all commands are executed on the same node, unless the `IsAtomic` flag is set to `false` (i.e. is a pipeline and not a transaction), in which case the commands may be split based on the key slot. Once all commands have been executed, the results (including any errors) are returned to the client. For pipelines, the results are returned in the same order as the commands were queued, even when the commands are executed on different nodes. ```go // Cluster BatchClient Example From 07fc58101a8dc58a101e690dcea878df2a60d0a3 Mon Sep 17 00:00:00 2001 From: jbrinkman Date: Tue, 17 Jun 2025 21:05:37 -0400 Subject: [PATCH 09/13] docs: update contributions list Signed-off-by: jbrinkman --- content/blog/2025-05-08-go-client-GA.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/content/blog/2025-05-08-go-client-GA.md b/content/blog/2025-05-08-go-client-GA.md index 4dc894a9..ddeb833b 100644 --- a/content/blog/2025-05-08-go-client-GA.md +++ b/content/blog/2025-05-08-go-client-GA.md @@ -510,6 +510,10 @@ A huge thank you to all the contributors and community members who have made thi [YiPin Chen](https://github.com/yipin-chen) (Improving) -[Andrew](https://github.com/andrew) +[San H](https://github.com/SanHalacogluImproving) (Improving) -Kudos to [Aaron Congo](https://github.com/aaron-congo) who created the backbone of the client 🚀, to [Umit Unal](https://github.com/umit) and [Michael](https://github.com/MikeMwita) for their early code contributions, and finally to [Marcin Dobosz](https://github.com/marcind) who provided invaluable community feedback and suggestions that pushed the team to keep improving on a daily basis! +[Aaron Congo](https://github.com/aaron-congo) (Improving) + +[Andrew](https://github.com/andrew) (Improving) + +Kudos to our community members: [Umit Unal](https://github.com/umit) and [Mike Mwita](https://github.com/MikeMwita) for their code contributions, and [Marcin Dobosz](https://github.com/marcind) who provided invaluable community feedback and suggestions that pushed the team to keep improving on a daily basis! From 76aeb2c1cab015cdfd239f0ef14a0570ee93dea7 Mon Sep 17 00:00:00 2001 From: jbrinkman Date: Wed, 18 Jun 2025 07:35:31 -0400 Subject: [PATCH 10/13] docs: update contributors spellings and links Signed-off-by: jbrinkman --- content/blog/2025-05-08-go-client-GA.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/blog/2025-05-08-go-client-GA.md b/content/blog/2025-05-08-go-client-GA.md index ddeb833b..10362e9b 100644 --- a/content/blog/2025-05-08-go-client-GA.md +++ b/content/blog/2025-05-08-go-client-GA.md @@ -488,7 +488,7 @@ A huge thank you to all the contributors and community members who have made thi [Edric Cuartero](https://github.com/EdricCua) (Google Cloud Platform) -[Omkar Mestry](https://github.com/omangesg) (Google Cloud Platform) +[Omkar Mestry](https://github.com/omanges) (Google Cloud Platform) [Vikas Pandey](https://github.com/vikas) (Google Cloud Platform) @@ -514,6 +514,6 @@ A huge thank you to all the contributors and community members who have made thi [Aaron Congo](https://github.com/aaron-congo) (Improving) -[Andrew](https://github.com/andrew) (Improving) +[Andrew Carbonetto](https://github.com/acarbonetto) (Improving) Kudos to our community members: [Umit Unal](https://github.com/umit) and [Mike Mwita](https://github.com/MikeMwita) for their code contributions, and [Marcin Dobosz](https://github.com/marcind) who provided invaluable community feedback and suggestions that pushed the team to keep improving on a daily basis! From 1a335942ba88577ce914a4cad433b28623c59679 Mon Sep 17 00:00:00 2001 From: jbrinkman Date: Wed, 18 Jun 2025 08:36:46 -0400 Subject: [PATCH 11/13] docs: fix Vikas github url Signed-off-by: jbrinkman --- content/blog/2025-05-08-go-client-GA.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2025-05-08-go-client-GA.md b/content/blog/2025-05-08-go-client-GA.md index 10362e9b..37a392a3 100644 --- a/content/blog/2025-05-08-go-client-GA.md +++ b/content/blog/2025-05-08-go-client-GA.md @@ -490,7 +490,7 @@ A huge thank you to all the contributors and community members who have made thi [Omkar Mestry](https://github.com/omanges) (Google Cloud Platform) -[Vikas Pandey](https://github.com/vikas) (Google Cloud Platform) +[Vikas Pandey](https://github.com/vikaskpandey) (Google Cloud Platform) [Yury Fridlyand](https://github.com/Yury-Fridlyand) (Improving) From 6f826d664c4f000c3227894670700c070fe9dafe Mon Sep 17 00:00:00 2001 From: Joseph Brinkman Date: Wed, 18 Jun 2025 08:38:51 -0400 Subject: [PATCH 12/13] Update content/blog/2025-05-08-go-client-GA.md Co-authored-by: Andrew Carbonetto Signed-off-by: Joseph Brinkman --- content/blog/2025-05-08-go-client-GA.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2025-05-08-go-client-GA.md b/content/blog/2025-05-08-go-client-GA.md index 37a392a3..adf9eb83 100644 --- a/content/blog/2025-05-08-go-client-GA.md +++ b/content/blog/2025-05-08-go-client-GA.md @@ -508,7 +508,7 @@ A huge thank you to all the contributors and community members who have made thi [Chloe Yip](https://github.com/cyip10) (Improving) -[YiPin Chen](https://github.com/yipin-chen) (Improving) +[Yi-Pin Chen](https://github.com/yipin-chen) (Improving) [San H](https://github.com/SanHalacogluImproving) (Improving) From b5ce022daeb6a59c3ce1014128faf1d5329a903d Mon Sep 17 00:00:00 2001 From: Joseph Brinkman Date: Wed, 18 Jun 2025 08:39:16 -0400 Subject: [PATCH 13/13] Update content/blog/2025-05-08-go-client-GA.md Co-authored-by: Andrew Carbonetto Signed-off-by: Joseph Brinkman --- content/blog/2025-05-08-go-client-GA.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2025-05-08-go-client-GA.md b/content/blog/2025-05-08-go-client-GA.md index adf9eb83..239813e9 100644 --- a/content/blog/2025-05-08-go-client-GA.md +++ b/content/blog/2025-05-08-go-client-GA.md @@ -7,7 +7,7 @@ authors= [ "niharikabhavaraju", "jbrinkman"] The Valkey GLIDE contributors are pleased to announce the general availability (GA) release of the Valkey GLIDE Go client. This release brings the power and reliability of Valkey to Go developers with an API designed for performance and developer productivity. -Vakey GLIDE 2.0 brings exciting new features for all of our language clients (Java, Node.js, Python, and now Go). These features include pipelines, OpenTelemetry support, MacOS x86 support, and improved performance and bug fixes. +Vakey GLIDE 2.0 brings exciting new features for all of our language clients (Java, Node.js, Python, and now Go). These features include pipelines, OpenTelemetry support, MacOS x86 support, improved performance, and bug fixes. The Go client extends Valkey GLIDE to the Go community, offering a robust client that's built on the battle-tested Rust core. This client library is a thoughtfully designed experience for Go developers who need reliable, high-performance data access.