From 02da14bd59d53be6ffcb99db4daca6138150280b Mon Sep 17 00:00:00 2001 From: Alex Le Date: Tue, 28 Jul 2026 09:45:35 -0700 Subject: [PATCH] ruby: added ruby how-to examples Signed-off-by: Alex Le --- .../client-features/client-side-caching.mdx | 3 +- .../docs/how-to/client-initialization.mdx | 32 ++++ .../connections/configure-lazy-connection.mdx | 28 +++ .../connections/limit-inflight-requests.mdx | 11 ++ .../docs/how-to/connections/read-strategy.mdx | 50 +++++ .../connections/resilience-best-practices.mdx | 180 ++++++++++-------- .../timeouts-and-reconnect-strategy.mdx | 40 ++++ .../docs/how-to/execute-custom-commands.mdx | 22 +++ .../docs/how-to/execute-custom-scripts.mdx | 59 ++++++ src/content/docs/how-to/installation.mdx | 26 +++ .../how-to/load-and-execute-functions.mdx | 78 ++++++++ .../docs/how-to/modules-api/json-module.mdx | 23 +++ .../docs/how-to/modules-api/search-module.mdx | 33 ++++ .../docs/how-to/monitoring/open-telemetry.mdx | 20 ++ .../how-to/monitoring/tracking-resources.mdx | 19 ++ .../docs/how-to/security/authentication.mdx | 34 ++++ src/content/docs/how-to/security/tls.mdx | 78 ++++++++ .../docs/how-to/send-batch-commands.mdx | 98 ++++++++++ 18 files changed, 748 insertions(+), 86 deletions(-) diff --git a/src/content/docs/concepts/client-features/client-side-caching.mdx b/src/content/docs/concepts/client-features/client-side-caching.mdx index ba8f5d27..7862473e 100644 --- a/src/content/docs/concepts/client-features/client-side-caching.mdx +++ b/src/content/docs/concepts/client-features/client-side-caching.mdx @@ -535,11 +535,10 @@ Multiple clients can share the same cache instance by passing the same `ClientSi ## Server-Assisted Invalidation Server-assisted invalidation can be enabled in the client side configuration by setting server assisted to true. -You do not need to call [`CLIENT TRACKING`](https://valkey.io/commands/client-tracking/) yourself; GLIDE issues `CLIENT TRACKING ON BCAST` on your behalf during connection setup. +You do not need to call [`CLIENT TRACKING`](https://valkey.io/commands/client-tracking/) yourself; GLIDE issues `CLIENT TRACKING ON BCAST` on your behalf during connection setup. Once enabled, the client receives push invalidation messages from the server whenever a cached key is modified, eliminating the staleness window inherent in TTL-only mode — the server notifies the client immediately when data changes, and the client removes the stale entry from its local cache. - ### Enabling server-assisted mode diff --git a/src/content/docs/how-to/client-initialization.mdx b/src/content/docs/how-to/client-initialization.mdx index b4c0d927..b22a2e96 100644 --- a/src/content/docs/how-to/client-initialization.mdx +++ b/src/content/docs/how-to/client-initialization.mdx @@ -111,6 +111,17 @@ The `NodeAddress` type represents the host and port of a cluster node. The host await using var client = await GlideClusterClient.CreateClient(config); ``` + + + ```ruby + require "valkey" + + client = Valkey.new( + nodes: [{ host: "address.example.com", port: 6379 }], + cluster_mode: true + ) + ``` + ### Request Routing @@ -143,6 +154,10 @@ For more details on the routing of specific commands: Please refer to [the documentation within the code](https://github.com/valkey-io/valkey-glide-csharp/blob/main/sources/Valkey.Glide/Route.cs) for routing configuration. + + + Please refer to [the documentation within the code](https://github.com/valkey-io/valkey-glide-ruby/blob/main/lib/valkey/route.rb) for routing configuration. + ### Response Aggregation @@ -175,6 +190,10 @@ To learn more about response aggregation for specific commands: Please refer to [the documentation within the code](https://github.com/valkey-io/valkey-glide-csharp/blob/main/sources/Valkey.Glide/ClusterValue.cs) for cluster response types. + + + Please refer to the documentation within the code. + ### Topology Updates @@ -306,6 +325,19 @@ Valkey GLIDE also supports Standalone deployments, where the database is hosted await using var client = await GlideClient.CreateClient(config); ``` + + + ```ruby + require "valkey" + + # The Ruby client connects to a single standalone address. + # Use the read_from option to route reads to replicas. + client = Valkey.new(host: "primary.example.com", port: 6379) + + # Or connect with a URL + client = Valkey.new(url: "redis://primary.example.com:6379/0") + ``` + ## Configuration Options diff --git a/src/content/docs/how-to/connections/configure-lazy-connection.mdx b/src/content/docs/how-to/connections/configure-lazy-connection.mdx index 5b1bb92f..62929244 100644 --- a/src/content/docs/how-to/connections/configure-lazy-connection.mdx +++ b/src/content/docs/how-to/connections/configure-lazy-connection.mdx @@ -189,6 +189,34 @@ Lazy connection can be configured through the client configuration object. $clusterClient->close(); ``` + + + ```ruby + require "valkey" + + # Standalone client with lazy connect + standalone_client = Valkey.new( + host: "localhost", + port: 6379, + lazy_connect: true, + timeout: 5.0 + ) + + # Cluster client with lazy connect + cluster_client = Valkey.new( + nodes: [ + { host: "localhost", port: 7000 }, + { host: "localhost", port: 7001 } + ], + cluster_mode: true, + lazy_connect: true, + timeout: 5.0 + ) + + # No connection established yet - this will trigger the connection + result = standalone_client.ping + ``` + ## How It Works diff --git a/src/content/docs/how-to/connections/limit-inflight-requests.mdx b/src/content/docs/how-to/connections/limit-inflight-requests.mdx index 202ae33b..5e452649 100644 --- a/src/content/docs/how-to/connections/limit-inflight-requests.mdx +++ b/src/content/docs/how-to/connections/limit-inflight-requests.mdx @@ -58,6 +58,17 @@ Inflight requests limit can be configured through the general client configurati ) ``` + + + ```ruby + # Limit to 1000 inflight requests per connection + client = Valkey.new( + nodes: [{ host: "localhost", port: 7000 }], + cluster_mode: true, + inflight_requests_limit: 1000 + ) + ``` + ## Why Limit Inflight Requests diff --git a/src/content/docs/how-to/connections/read-strategy.mdx b/src/content/docs/how-to/connections/read-strategy.mdx index 2e9e6772..386494cd 100644 --- a/src/content/docs/how-to/connections/read-strategy.mdx +++ b/src/content/docs/how-to/connections/read-strategy.mdx @@ -141,6 +141,22 @@ Valkey GLIDE provides support for the following read strategies, allowing you to $client->get('key1'); ``` + + + ```ruby + require "valkey" + + client = Valkey.new( + nodes: [{ host: "address.example.com", port: 6379 }], + cluster_mode: true, + read_from: Valkey::ReadFrom::PREFER_REPLICA + ) + + client.set("key1", "val1") + # get will read from one of the replicas + client.get("key1") + ``` + ### AZ Affinity @@ -272,6 +288,23 @@ When using the AZ Affinity read strategy, the `clientAz` setting is required to $client->get('key1'); ``` + + + ```ruby + require "valkey" + + client = Valkey.new( + nodes: [{ host: "address.example.com", port: 6379 }], + cluster_mode: true, + read_from: Valkey::ReadFrom::AZ_AFFINITY, + client_az: "us-east-1a" + ) + + client.set("key1", "val1") + # get will read from one of the replicas in the same client's availability zone if they exist + client.get("key1") + ``` + ### AZ Affinity Replicas and Primary Read Strategy @@ -403,4 +436,21 @@ When using the AZ Affinity Replicas and Primary read strategy, the `clientAz` se $client->get('key1'); ``` + + + ```ruby + require "valkey" + + client = Valkey.new( + nodes: [{ host: "address.example.com", port: 6379 }], + cluster_mode: true, + read_from: Valkey::ReadFrom::AZ_AFFINITY_REPLICAS_AND_PRIMARY, + client_az: "us-east-1a" + ) + + client.set("key1", "val1") + # get will read from one of the replicas or the primary in the same client's availability zone if they exist + client.get("key1") + ``` + diff --git a/src/content/docs/how-to/connections/resilience-best-practices.mdx b/src/content/docs/how-to/connections/resilience-best-practices.mdx index b744d702..7977deb4 100644 --- a/src/content/docs/how-to/connections/resilience-best-practices.mdx +++ b/src/content/docs/how-to/connections/resilience-best-practices.mdx @@ -12,80 +12,92 @@ This guide covers configuration and operational practices that help Valkey GLIDE A resilient client combines request timeout, inflight limiting, and the circuit breaker: - - -```python -from glide import ( - GlideClusterClient, - GlideClusterClientConfiguration, - ClientCircuitBreakerConfiguration, - NodeAddress, -) - -config = GlideClusterClientConfiguration( - addresses=[NodeAddress(host="localhost", port=6379)], - request_timeout=500, - inflight_requests_limit=1000, - client_circuit_breaker=ClientCircuitBreakerConfiguration(), -) -client = await GlideClusterClient.create(config) -``` - - - - -```java -import glide.api.GlideClusterClient; -import glide.api.models.configuration.GlideClusterClientConfiguration; -import glide.api.models.configuration.ClientCircuitBreakerConfiguration; -import glide.api.models.configuration.NodeAddress; - -GlideClusterClient client = GlideClusterClient.createClient( - GlideClusterClientConfiguration.builder() - .address(NodeAddress.builder().host("localhost").port(6379).build()) - .requestTimeout(500) - .inflightRequestsLimit(1000) - .clientCircuitBreakerConfiguration( - ClientCircuitBreakerConfiguration.builder().build()) - .build() -).get(); -``` - - - - -```typescript -import { GlideClusterClient } from "@valkey/valkey-glide"; - -const client = await GlideClusterClient.createClient({ - addresses: [{ host: "localhost", port: 6379 }], - requestTimeout: 500, - inflightRequestsLimit: 1000, - clientCircuitBreaker: {}, -}); -``` - - - - -```go -import ( - "time" - - glide "github.com/valkey-io/valkey-glide/go/v2" - "github.com/valkey-io/valkey-glide/go/v2/config" -) - -clientConfig := config.NewClusterClientConfiguration(). - WithAddress(&config.NodeAddress{Host: "localhost", Port: 6379}). - WithRequestTimeout(500 * time.Millisecond). - WithInflightRequestsLimit(1000). - WithClientCircuitBreaker(&config.ClientCircuitBreakerConfiguration{}) - -client, err := glide.NewClusterClient(clientConfig) -``` - - + + ```python + from glide import ( + GlideClusterClient, + GlideClusterClientConfiguration, + ClientCircuitBreakerConfiguration, + NodeAddress, + ) + + config = GlideClusterClientConfiguration( + addresses=[NodeAddress(host="localhost", port=6379)], + request_timeout=500, + inflight_requests_limit=1000, + client_circuit_breaker=ClientCircuitBreakerConfiguration(), + ) + client = await GlideClusterClient.create(config) + ``` + + + + ```java + import glide.api.GlideClusterClient; + import glide.api.models.configuration.GlideClusterClientConfiguration; + import glide.api.models.configuration.ClientCircuitBreakerConfiguration; + import glide.api.models.configuration.NodeAddress; + + GlideClusterClient client = GlideClusterClient.createClient( + GlideClusterClientConfiguration.builder() + .address(NodeAddress.builder().host("localhost").port(6379).build()) + .requestTimeout(500) + .inflightRequestsLimit(1000) + .clientCircuitBreakerConfiguration( + ClientCircuitBreakerConfiguration.builder().build()) + .build() + ).get(); + ``` + + + + ```typescript + import { GlideClusterClient } from "@valkey/valkey-glide"; + + const client = await GlideClusterClient.createClient({ + addresses: [{ host: "localhost", port: 6379 }], + requestTimeout: 500, + inflightRequestsLimit: 1000, + clientCircuitBreaker: {}, + }); + ``` + + + + ```go + import ( + "time" + + glide "github.com/valkey-io/valkey-glide/go/v2" + "github.com/valkey-io/valkey-glide/go/v2/config" + ) + + clientConfig := config.NewClusterClientConfiguration(). + WithAddress(&config.NodeAddress{Host: "localhost", Port: 6379}). + WithRequestTimeout(500 * time.Millisecond). + WithInflightRequestsLimit(1000). + WithClientCircuitBreaker(&config.ClientCircuitBreakerConfiguration{}) + + client, err := glide.NewClusterClient(clientConfig) + ``` + + + + ```ruby + require "valkey" + + client = Valkey.new( + nodes: [{ host: "localhost", port: 6379 }], + cluster_mode: true, + timeout: 0.5, # request timeout in seconds (500 ms) + inflight_requests_limit: 1000 + ) + ``` + + :::note + The client circuit breaker is not yet available in GLIDE Ruby. + ::: + ## Single Client Instance @@ -111,11 +123,11 @@ The `inflightRequestsLimit` controls how many concurrent requests a single clien **Key points:** -- The inflight limit should accommodate your expected concurrency: at throughput `T` ops/s with average latency `L` seconds, you need roughly `T × L` inflight slots (e.g., 50k ops/s at 1ms = ~50 slots, 50k ops/s at 10ms = ~500 slots). -- When the limit is reached, new requests block until a slot frees up. The circuit breaker (if enabled) rejects before this blocking occurs. -- If using Java with `ForkJoinPool.managedBlock()` (common in Akka, Play, or reactive frameworks), set the limit to 2-3x your thread pool size. This prevents thread explosion where `managedBlock()` spawns compensating threads faster than requests complete. -- If you see "inflight requests limit reached" warnings in logs, either increase the limit or reduce concurrency at the application level. -- Setting the limit too high risks memory pressure under sustained backpressure. The default of 1000 is appropriate for most workloads. +* The inflight limit should accommodate your expected concurrency: at throughput `T` ops/s with average latency `L` seconds, you need roughly `T × L` inflight slots (e.g., 50k ops/s at 1ms = ~50 slots, 50k ops/s at 10ms = ~500 slots). +* When the limit is reached, new requests block until a slot frees up. The circuit breaker (if enabled) rejects before this blocking occurs. +* If using Java with `ForkJoinPool.managedBlock()` (common in Akka, Play, or reactive frameworks), set the limit to 2-3x your thread pool size. This prevents thread explosion where `managedBlock()` spawns compensating threads faster than requests complete. +* If you see "inflight requests limit reached" warnings in logs, either increase the limit or reduce concurrency at the application level. +* Setting the limit too high risks memory pressure under sustained backpressure. The default of 1000 is appropriate for most workloads. ## Thread Pool Sizing (Java) @@ -168,11 +180,11 @@ See the dedicated [Circuit Breaker guide](/how-to/connections/circuit-breaker) f **Key points:** -- Enable CB for workloads where thread exhaustion is a concern during outages. -- The CB rejects before the request reaches the network, so rejections are near-zero-cost. -- Enable `countTimeouts` to protect against dead or unreachable nodes. In cluster mode, dead nodes surface primarily as timeouts, so this flag is required for the CB to trip on sustained node outages. -- Recovery is optimistic: in HalfOpen, all traffic is allowed through. After consecutive successes the breaker closes. If a failure occurs, it returns to Open. -- When the CB rejects, handle the `CircuitBreakerException`/`CircuitBreakerError` in your application by returning a cached or degraded response, or signaling the caller to retry after a delay. +* Enable CB for workloads where thread exhaustion is a concern during outages. +* The CB rejects before the request reaches the network, so rejections are near-zero-cost. +* Enable `countTimeouts` to protect against dead or unreachable nodes. In cluster mode, dead nodes surface primarily as timeouts, so this flag is required for the CB to trip on sustained node outages. +* Recovery is optimistic: in HalfOpen, all traffic is allowed through. After consecutive successes the breaker closes. If a failure occurs, it returns to Open. +* When the CB rejects, handle the `CircuitBreakerException`/`CircuitBreakerError` in your application by returning a cached or degraded response, or signaling the caller to retry after a delay. ## Standalone vs Cluster diff --git a/src/content/docs/how-to/connections/timeouts-and-reconnect-strategy.mdx b/src/content/docs/how-to/connections/timeouts-and-reconnect-strategy.mdx index 7074974b..c9fcd227 100644 --- a/src/content/docs/how-to/connections/timeouts-and-reconnect-strategy.mdx +++ b/src/content/docs/how-to/connections/timeouts-and-reconnect-strategy.mdx @@ -111,6 +111,18 @@ Valkey GLIDE allows you to configure timeout settings and reconnect strategies. await using var client = await GlideClusterClient.CreateClient(config); ``` + + + ```ruby + require "valkey" + + client = Valkey.new( + nodes: [{ host: "address.example.com", port: 6379 }], + cluster_mode: true, + timeout: 0.5 # request timeout in seconds (500 ms) + ) + ``` + ## Setting Connection Timeout @@ -212,6 +224,18 @@ The connection timeout controls how long the client waits for a TCP/TLS connecti await using var client = await GlideClusterClient.CreateClient(config); ``` + + + ```ruby + require "valkey" + + client = Valkey.new( + nodes: [{ host: "address.example.com", port: 6379 }], + cluster_mode: true, + connect_timeout: 5.0 # connection timeout in seconds (5000 ms) + ) + ``` + ## Configuring Reconnect Strategy @@ -336,4 +360,20 @@ The connection timeout controls how long the client waits for a TCP/TLS connecti await using var client = await GlideClient.CreateClient(config); ``` + + + ```ruby + require "valkey" + + # GLIDE derives an exponential backoff strategy from these + # redis-rb-style options (delays are in seconds) + client = Valkey.new( + nodes: [{ host: "address.example.com", port: 6379 }], + cluster_mode: true, + reconnect_attempts: 3, + reconnect_delay: 0.5, + reconnect_delay_max: 5.0 + ) + ``` + diff --git a/src/content/docs/how-to/execute-custom-commands.mdx b/src/content/docs/how-to/execute-custom-commands.mdx index e6eb8617..4c548672 100644 --- a/src/content/docs/how-to/execute-custom-commands.mdx +++ b/src/content/docs/how-to/execute-custom-commands.mdx @@ -155,6 +155,28 @@ Pass the command name and arguments as an array of strings: Console.WriteLine(result); // hello ``` + + + ```ruby + require "valkey" + + client = Valkey.new(host: "localhost", port: 6379) + + # Execute a SET command + client.call("SET", "mykey", "hello") + + # Execute a GET command + result = client.call("GET", "mykey") + puts result # hello + + # call_v takes the whole command as a single array — + # useful when the command is built dynamically + result = client.call_v(["MGET", "mykey"]) + puts result.inspect # ["hello"] + + client.close + ``` + ## Limitations diff --git a/src/content/docs/how-to/execute-custom-scripts.mdx b/src/content/docs/how-to/execute-custom-scripts.mdx index 874a30d9..e8fc1e47 100644 --- a/src/content/docs/how-to/execute-custom-scripts.mdx +++ b/src/content/docs/how-to/execute-custom-scripts.mdx @@ -380,6 +380,65 @@ The following steps shows how to run a simple a custom Lua script using GLIDE. Other GLIDE clients provide a `Script` API that automatically handles script caching and SHA1 management. In PHP, you manually manage this using `eval()` and `evalsha()`. For detailed patterns and best practices, see the [Valkey Scripting Reference](/reference/scripting-reference). ::: + + + + 1. Define the lua script + + ```ruby + lua = <<~LUA + server.call('SET', KEYS[1], ARGV[1]) + return KEYS[1] .. ': ' .. server.call('GET', KEYS[1]) + LUA + ``` + + 2. Execute the script with keys and arguments using `eval` + + ```ruby + keys = ["username"] + args = ["John Doe"] + result = client.eval(lua, keys: keys, args: args) + puts result # username: John Doe + ``` + + +
+ Full Example + + ```ruby + require "valkey" + + client = Valkey.new(host: "localhost", port: 6379) + + lua = <<~LUA + server.call('SET', KEYS[1], ARGV[1]) + return KEYS[1] .. ': ' .. server.call('GET', KEYS[1]) + LUA + + keys = ["username"] + args = ["John Doe"] + result = client.eval(lua, keys: keys, args: args) + puts result + + client.close + ``` +
+ + ### Using script\_load and evalsha for Repeated Execution + + For scripts executed multiple times, load the script once with `script_load` and execute it by its SHA1 hash: + + ```ruby + sha = client.script_load(lua) + + result = client.evalsha(sha, keys: ["username"], args: ["John Doe"]) + puts result # username: John Doe + + # invoke_script is equivalent and also takes the SHA1 hash + result = client.invoke_script(sha, keys: ["username"], args: ["John Doe"]) + puts result # username: John Doe + ``` +
## What's Next diff --git a/src/content/docs/how-to/installation.mdx b/src/content/docs/how-to/installation.mdx index 12512db3..ed84f6b3 100644 --- a/src/content/docs/how-to/installation.mdx +++ b/src/content/docs/how-to/installation.mdx @@ -325,4 +325,30 @@ Windows support is currently available for Valkey GLIDE Java and C#. dotnet add package Valkey.Glide ``` + + + **Requirements:** Ruby 2.6+ (MRI) or JRuby 3.0+ + + To get started, Valkey GLIDE Ruby is available on [RubyGems](https://rubygems.org/gems/valkey-glide-rb). + + ```bash + gem install valkey-glide-rb + ``` + + Or add it to your `Gemfile`: + + ```ruby + gem "valkey-glide-rb" + ``` + + Verify installation: + + ```bash + ruby -e 'require "valkey"; puts Valkey::VERSION' + ``` + + :::note + The gem ships prebuilt native libraries for Linux and macOS, including Alpine Linux / musl (3.18+), and depends on the [`ffi`](https://github.com/ffi/ffi) gem. + ::: + diff --git a/src/content/docs/how-to/load-and-execute-functions.mdx b/src/content/docs/how-to/load-and-execute-functions.mdx index 9bab4d8a..d5cdc1b1 100644 --- a/src/content/docs/how-to/load-and-execute-functions.mdx +++ b/src/content/docs/how-to/load-and-execute-functions.mdx @@ -409,6 +409,65 @@ The following example shows a simple example of loading and executing a Valkey F ``` + + + + 1. Define the lua script. + + ```ruby + lua_code = <<~LUA + #!lua name=example_module + server.register_function('set_then_get', function(key, value) + server.call('SET', key, value) + return server.call('GET', key) + end) + LUA + ``` + + 2. Load the function to Valkey. + + ```ruby + client.function_load(lua_code, replace: true) + ``` + + 3. Call the function. + + ```ruby + client.set("page:home:visits", "0") + result = client.fcall("set_then_get", keys: ["page:home:visits"], args: ["1"]) + puts result # 1 + ``` + + +
+ Full Example + + ```ruby + require "valkey" + + client = Valkey.new(host: "localhost", port: 6379) + + # lua code that updates a key and returns its new value + lua_code = <<~LUA + #!lua name=page_visits + server.register_function('update_visits', function(visits) + server.call('INCR', visits[1]) + return server.call('GET', visits[1]) + end) + LUA + + # loading the function to valkey + client.function_load(lua_code, replace: true) + + client.set("page:home:visits", "0") + # calling the previously loaded function + result = client.fcall("update_visits", keys: ["page:home:visits"]) + puts result # 1 + + client.close + ``` +
+
## Read-Only Functions @@ -543,6 +602,25 @@ GLIDE clients implement `FCALL_RO` command to execute read-only functions. $result = $client->fcall_ro('get_value', [], [], 'randomNode'); ``` + + + ```ruby + result = client.fcall_ro("get_value", keys: ["mykey"]) + ``` + + **With Routing (Cluster Mode)** + + ```ruby + # Route to all nodes + result = client.fcall_ro("get_value", route: Valkey::Route.all_nodes) + + # Route to all primary nodes + result = client.fcall_ro("get_value", route: Valkey::Route.all_primaries) + + # Route to a random node + result = client.fcall_ro("get_value", route: Valkey::Route.random) + ``` + ## What's Next diff --git a/src/content/docs/how-to/modules-api/json-module.mdx b/src/content/docs/how-to/modules-api/json-module.mdx index 418cec68..eb66abea 100644 --- a/src/content/docs/how-to/modules-api/json-module.mdx +++ b/src/content/docs/how-to/modules-api/json-module.mdx @@ -176,6 +176,29 @@ Use `INFO MODULES` or `MODULE LIST` command to see the status of all loaded modu Console.WriteLine($"Output: {response}"); // Output: {"a":1.0,"b":2} ``` + + + ```ruby + require "valkey" + require "json" + + # Both standalone and cluster mode are supported + client = Valkey.new(host: "localhost", port: 6379) + + key = "testKey" + path = "$" + json_value = '{"a": 1.0, "b": 2}' + + # JSON.SET + set_response = client.json_set(key, path, json_value) + # set_response == "OK" + + # JSON.GET + get_response = client.json_get(key, "$") + # get_response == '[{"a":1.0,"b":2}]' + JSON.parse(get_response) # => [{"a" => 1.0, "b" => 2}] + ``` + ## What's Next diff --git a/src/content/docs/how-to/modules-api/search-module.mdx b/src/content/docs/how-to/modules-api/search-module.mdx index 4db293e9..c0f54b45 100644 --- a/src/content/docs/how-to/modules-api/search-module.mdx +++ b/src/content/docs/how-to/modules-api/search-module.mdx @@ -360,6 +360,39 @@ Use `INFO MODULES` or `MODULE LIST` command to see the status of all loaded modu Console.WriteLine($"Vectors: [{string.Join(", ", vectors)}]"); // Vectors: [0000000000000000, 00000000000080BF] ``` + + + ```ruby + require "valkey" + + # Both standalone and cluster mode are supported + client = Valkey.new(host: "localhost", port: 6379) + + prefix = "{hash}:" + index = prefix + "index" + + # FT.CREATE a Hash index with a vector field + client.ft_create(index, "ON", "HASH", "PREFIX", "1", prefix, + "SCHEMA", "vec", "AS", "VEC", "VECTOR", "HNSW", "6", + "TYPE", "FLOAT32", "DIM", "2", "DISTANCE_METRIC", "L2") + # => "OK" + + # Insert hash data with vector fields (2-dimensional float32 vectors = 8 bytes each) + vec0 = [0.0, 0.0].pack("e2") # "\x00\x00\x00\x00\x00\x00\x00\x00" + vec1 = [0.0, -1.0].pack("e2") # "\x00\x00\x00\x00\x00\x00\x80\xBF" + client.hset(prefix + "0", "vec", vec0) + client.hset(prefix + "1", "vec", vec1) + + sleep 1 # let server digest the data and update index + + # FT.SEARCH + result = client.ft_search(index, "*=>[KNN 2 @VEC $query_vec]", + "PARAMS", "2", "query_vec", vec0, + "DIALECT", "2") + # result[0] == 2 + # the remaining entries contain "{hash}:0" and "{hash}:1" with their vector values + ``` + ## What's Next diff --git a/src/content/docs/how-to/monitoring/open-telemetry.mdx b/src/content/docs/how-to/monitoring/open-telemetry.mdx index 7ea8705e..2ca79b27 100644 --- a/src/content/docs/how-to/monitoring/open-telemetry.mdx +++ b/src/content/docs/how-to/monitoring/open-telemetry.mdx @@ -159,6 +159,26 @@ GLIDE does not export data directly to third-party services—instead, it sends ); ``` + + + ```ruby + require "valkey" + + # Initialize once per process, before creating clients + Valkey::OpenTelemetry.init( + traces: { + endpoint: "http://localhost:4318/v1/traces", + sample_percentage: 10 # Optional, defaults to 1 + }, + metrics: { + endpoint: "http://localhost:4318/v1/metrics" + }, + flush_interval_ms: 1000 # Optional, defaults to 5000 + ) + + client = Valkey.new(host: "localhost", port: 6379) + ``` + :::tip diff --git a/src/content/docs/how-to/monitoring/tracking-resources.mdx b/src/content/docs/how-to/monitoring/tracking-resources.mdx index afcb271c..560ab981 100644 --- a/src/content/docs/how-to/monitoring/tracking-resources.mdx +++ b/src/content/docs/how-to/monitoring/tracking-resources.mdx @@ -130,4 +130,23 @@ GLIDE 1.2 introduces a new non-Valkey API: `getStatistics` which returns statist echo "Total Clients: {$stats['total_clients']}\n"; ``` + + + ```ruby + require "valkey" + + client = Valkey.new( + nodes: [{ host: "address.example.com", port: 6379 }], + cluster_mode: true, + timeout: 0.5 # request timeout in seconds (500 ms) + ) + + # Retrieve statistics + stats = client.get_statistics + + # Example: Accessing and printing statistics + puts "Total Connections: #{stats[:total_connections]}" + puts "Total Clients: #{stats[:total_clients]}" + ``` + diff --git a/src/content/docs/how-to/security/authentication.mdx b/src/content/docs/how-to/security/authentication.mdx index 41310745..852d8c15 100644 --- a/src/content/docs/how-to/security/authentication.mdx +++ b/src/content/docs/how-to/security/authentication.mdx @@ -124,6 +124,25 @@ Both standalone and cluster mode support authentication authentication with user $client->connect(addresses: $addresses, credentials: $credentials); ``` + + + ```ruby + require "valkey" + + client = Valkey.new( + host: "primary.example.com", + port: 6379, + username: "user1", + password: "passwordA" + ) + ``` + + Credentials can also be embedded in a connection URL: + + ```ruby + client = Valkey.new(url: "redis://user1:passwordA@primary.example.com:6379") + ``` + ### Cluster @@ -225,6 +244,21 @@ Both standalone and cluster mode support authentication authentication with user ); ``` + + + ```ruby + require "valkey" + + nodes = [{ host: "address.example.com", port: 6379 }] + + client = Valkey.new( + nodes: nodes, + cluster_mode: true, + username: "user1", + password: "passwordA" + ) + ``` + ## Best Practices diff --git a/src/content/docs/how-to/security/tls.mdx b/src/content/docs/how-to/security/tls.mdx index 07a85598..2201b77a 100644 --- a/src/content/docs/how-to/security/tls.mdx +++ b/src/content/docs/how-to/security/tls.mdx @@ -110,6 +110,16 @@ Enabling TLS is as simple as setting `use_tls=True` in your configuration. The c ); ``` + + + ```ruby + require "valkey" + + nodes = [{ host: "address.example.com", port: 6379 }] + + client = Valkey.new(nodes: nodes, cluster_mode: true, ssl: true) + ``` + ### Standalone @@ -215,6 +225,20 @@ Enabling TLS is as simple as setting `use_tls=True` in your configuration. The c $client->connect(addresses: $addresses, use_tls: true); ``` + + + ```ruby + require "valkey" + + client = Valkey.new(host: "primary.example.com", port: 6379, ssl: true) + ``` + + A `rediss://` URL enables TLS as well: + + ```ruby + client = Valkey.new(url: "rediss://primary.example.com:6379") + ``` + ## Advanced TLS Configurations @@ -527,6 +551,60 @@ You can provide custom root certificates for TLS connections. This is useful whe ); ``` + + + **Certificate Behavior:** + + * If `ssl_params` is not provided, the system's default certificate trust store is used + * `ca_file` takes a path to a CA certificate file in PEM format + * Multiple certificates can be provided as PEM strings via the `root_certs` array, or by pointing `ca_path` at a directory of `.crt`/`.pem` files + + #### Example - Connecting with Custom Root Certificate from File + + ```ruby + require "valkey" + + client = Valkey.new( + host: "address.example.com", + port: 6379, + ssl: true, + ssl_params: { ca_file: "/path/to/ca-cert.pem" } + ) + ``` + + #### Example - Using Certificate as String + + ```ruby + cert_data = <<~CERT + -----BEGIN CERTIFICATE----- + MIIDXTCCAkWgAwIBAgIJAKL0UG+mRKmzMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV + ... + -----END CERTIFICATE----- + CERT + + client = Valkey.new( + host: "address.example.com", + port: 6379, + ssl: true, + ssl_params: { root_certs: [cert_data] } + ) + ``` + + #### Example - Client Certificate (Mutual TLS) + + ```ruby + client = Valkey.new( + host: "address.example.com", + port: 6379, + ssl: true, + ssl_params: { + ca_file: "/path/to/ca-cert.pem", + cert: "/path/to/client-cert.pem", + key: "/path/to/client-key.pem" + } + ) + ``` + ### TLS Certificate Format diff --git a/src/content/docs/how-to/send-batch-commands.mdx b/src/content/docs/how-to/send-batch-commands.mdx index 82b153a9..5e39d780 100644 --- a/src/content/docs/how-to/send-batch-commands.mdx +++ b/src/content/docs/how-to/send-batch-commands.mdx @@ -226,6 +226,41 @@ A transaction is an all-or-nothing set of commands sent in a single request to V // Atomic Batch Results: OK, OK, 50, 50, 50 ``` + + + ```ruby + require "valkey" + + # Initialize client + client = Valkey.new(host: "localhost", port: 6379) + + # Create an atomic transaction using multi + results = client.multi do |tx| + tx.set("account:source", "100") + tx.set("account:dest", "0") + tx.incrby("account:dest", 50) + tx.decrby("account:source", 50) + tx.get("account:source") + end + + puts "Atomic Transaction Results: #{results.inspect}" + # Atomic Transaction Results: ["OK", "OK", 50, 50, "50"] + ``` + + Use `watch` / `unwatch` for optimistic locking around a transaction: + + ```ruby + client.watch("account:source") do + if client.get("account:source") == "100" + client.multi do |tx| + tx.decrby("account:source", 50) + end + else + client.unwatch + end + end + ``` + ### Cluster @@ -433,6 +468,26 @@ A transaction is an all-or-nothing set of commands sent in a single request to V // Atomic Cluster Batch: OK, 6, 6 ``` + + + ```ruby + require "valkey" + + # Initialize cluster client + nodes = [{ host: "127.0.0.1", port: 6379 }] + client = Valkey.new(nodes: nodes, cluster_mode: true) + + # Create an atomic transaction (all keys map to same slot) + results = client.multi do |tx| + tx.set("user:100:visits", "1") + tx.incrby("user:100:visits", 5) + tx.get("user:100:visits") + end + + puts "Atomic Cluster Transaction: #{results.inspect}" + # Atomic Cluster Transaction: ["OK", 6, "6"] + ``` + :::caution[Important] @@ -627,6 +682,26 @@ Pipelining is a group of commands sent in a single request that does not guarant // Pipeline Results: OK, OK, value1, value2 ``` + + + ```ruby + require "valkey" + + # Initialize client + client = Valkey.new(host: "localhost", port: 6379) + + # Create non-atomic pipeline using pipelined + results = client.pipelined do |pipe| + pipe.set("temp:key1", "value1") + pipe.set("temp:key2", "value2") + pipe.get("temp:key1") + pipe.get("temp:key2") + end + + puts "Pipeline Results: #{results.inspect}" + # Pipeline Results: ["OK", "OK", "value1", "value2"] + ``` + ### Cluster @@ -866,6 +941,29 @@ Pipelining is a group of commands sent in a single request that does not guarant // Pipeline Cluster Results: OK, 125, 125, 1, 2, [user2, user1] ``` + + + ```ruby + require "valkey" + + # Initialize cluster client + nodes = [{ host: "localhost", port: 6379 }] + client = Valkey.new(nodes: nodes, cluster_mode: true) + + # Create pipeline spanning multiple slots + results = client.pipelined do |pipe| + pipe.set("page:home:views", "100") + pipe.incrby("page:home:views", 25) + pipe.get("page:home:views") + pipe.lpush("recent:logins", "user1") + pipe.lpush("recent:logins", "user2") + pipe.lrange("recent:logins", 0, 1) + end + + puts "Pipeline Cluster Results: #{results.inspect}" + # Pipeline Cluster Results: ["OK", 125, "125", 1, 2, ["user2", "user1"]] + ``` + ## Next Steps