diff --git a/.github/workflows/static-checks.yml b/.github/workflows/static-checks.yml index b201a068..d685c677 100644 --- a/.github/workflows/static-checks.yml +++ b/.github/workflows/static-checks.yml @@ -33,6 +33,9 @@ jobs: - name: Check formatting run: pnpm format:check + - name: Check language tab ordering + run: pnpm check:tab-order + - name: Build (will also check for internal links) run: pnpm build diff --git a/AGENTS.md b/AGENTS.md index ce29ffbe..4c64950e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -136,6 +136,8 @@ Per-language code examples use Starlight tabs synced across pages: ``` - Tab labels (in this order): `Python`, `Java`, `Node`, `Go`, `PHP`, `C#`, `Ruby` +- Languages a page doesn't cover are simply omitted; the remaining tabs keep this relative order +- This order is enforced by `pnpm check:tab-order` (run in CI). The check lives at `scripts/check-tab-order.mjs` — update the `CANONICAL` array there if the order ever changes - Examples across tabs on the same page should be equivalent: same key names, values, hosts/ports, and flow ## Commit Requirements diff --git a/package.json b/package.json index 3acd6639..d4eb5e55 100755 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "format:check:non-mdx": "prettier --check .", "format:check:mdx": "bash scripts/check-format.sh", "format:check": "pnpm run format:check:non-mdx && pnpm run format:check:mdx", + "check:tab-order": "node scripts/check-tab-order.mjs", "test-deploy": "pnpm build && ./test-deploy.sh" }, "dependencies": { diff --git a/scripts/check-tab-order.mjs b/scripts/check-tab-order.mjs new file mode 100644 index 00000000..5d1bfe28 --- /dev/null +++ b/scripts/check-tab-order.mjs @@ -0,0 +1,124 @@ +#!/usr/bin/env node +// Validates that language groups in the docs follow the canonical +// ordering defined in AGENTS.md. Language tabs that are absent from a group are +// simply skipped; a group is a violation only when the RELATIVE order of the +// languages it does contain differs from the canonical order. +// +// Run: node scripts/check-tab-order.mjs (also exposed as `pnpm check:tab-order`) +// Exits non-zero and prints file:line for every offending group. + +import { readdirSync, readFileSync, statSync } from "node:fs"; +import { join, relative } from "node:path"; +import { fileURLToPath } from "node:url"; + +const ROOT = fileURLToPath(new URL("..", import.meta.url)); +const DOCS_DIR = join(ROOT, "src/content/docs"); + +// Canonical order — keep in sync with the "Language Tabs" section of AGENTS.md. +const CANONICAL = ["Python", "Java", "Node", "Go", "PHP", "C#", "Ruby"]; +const RANK = new Map(CANONICAL.map((lang, i) => [lang, i])); + +// Normalize label variants to their canonical key. +function normalize(label) { + if (label === "Node.js") return "Node"; + return label; +} + +function isLanguage(label) { + return RANK.has(normalize(label)); +} + +function mdxFiles(dir) { + const out = []; + for (const entry of readdirSync(dir)) { + const full = join(dir, entry); + if (statSync(full).isDirectory()) out.push(...mdxFiles(full)); + else if (entry.endsWith(".mdx") || entry.endsWith(".md")) out.push(full); + } + return out; +} + +const ITEM_RE = /]*\blabel=(?:"([^"]*)"|'([^']*)')/g; + +// Parse a file into a list of tab groups. Uses a stack so that a +// is attributed to its immediately enclosing , and nested tab groups +// (e.g. build-tool tabs inside a language tab) are handled independently. +function parseGroups(text) { + const lines = text.split("\n"); + const stack = []; + const groups = []; + lines.forEach((line, idx) => { + // A single line can, in principle, contain multiple tokens; scan in order. + // We process opens/items/closes by their column position on the line. + const tokens = []; + let m; + const openRe = /<(?:Tabs|ParamTabs)\b/g; + while ((m = openRe.exec(line))) tokens.push({ col: m.index, type: "open" }); + const closeRe = /<\/(?:Tabs|ParamTabs)>/g; + while ((m = closeRe.exec(line))) + tokens.push({ col: m.index, type: "close" }); + ITEM_RE.lastIndex = 0; + while ((m = ITEM_RE.exec(line))) { + tokens.push({ col: m.index, type: "item", label: m[1] ?? m[2] }); + } + tokens.sort((a, b) => a.col - b.col); + for (const t of tokens) { + if (t.type === "open") { + const g = { line: idx + 1, labels: [] }; + stack.push(g); + groups.push(g); + } else if (t.type === "close") { + stack.pop(); + } else if (t.type === "item") { + if (stack.length) stack[stack.length - 1].labels.push(t.label); + } + } + }); + return groups; +} + +function checkGroup(labels) { + const langs = labels.filter(isLanguage).map(normalize); + for (let i = 1; i < langs.length; i++) { + if (RANK.get(langs[i]) < RANK.get(langs[i - 1])) { + return langs; // out of order + } + } + return null; +} + +let violations = 0; +let filesWithViolations = 0; + +for (const file of mdxFiles(DOCS_DIR)) { + const text = readFileSync(file, "utf8"); + const groups = parseGroups(text); + let fileHadViolation = false; + for (const g of groups) { + const bad = checkGroup(g.labels); + if (bad) { + if (!fileHadViolation) { + console.error(`\n${relative(ROOT, file)}`); + fileHadViolation = true; + filesWithViolations++; + } + const expected = [...bad].sort((a, b) => RANK.get(a) - RANK.get(b)); + console.error( + ` line ${g.line}: [${bad.join(", ")}] → expected [${expected.join(", ")}]`, + ); + violations++; + } + } +} + +const canonicalStr = CANONICAL.join(" → "); +if (violations > 0) { + console.error( + `\n✖ ${violations} tab group(s) in ${filesWithViolations} file(s) violate the canonical language order (${canonicalStr}).`, + ); + process.exit(1); +} else { + console.log( + `✓ All language tab groups follow the canonical order (${canonicalStr}).`, + ); +} diff --git a/src/content/docs/commands/valkey-string.mdx b/src/content/docs/commands/valkey-string.mdx index 338bba21..23132913 100644 --- a/src/content/docs/commands/valkey-string.mdx +++ b/src/content/docs/commands/valkey-string.mdx @@ -194,6 +194,37 @@ Valkey strings store sequences of bytes, which may include text, serialized obje Go strings are immutable sequences of bytes and can safely represent binary data. + + PHP uses native `string` type for all Valkey string operations. PHP strings are binary-safe and can contain arbitrary bytes. + + #### Usage + + Commands accept `string` arguments and return `string` or `mixed` values: + + ```php + // Create client + $client = new ValkeyGlide(); + $client->connect(addresses: [['host' => 'localhost', 'port' => 6379]]); + + // Using regular strings + $client->set('key', 'value'); + $result = $client->get('key'); // Returns string: 'value' + + // Using binary data - PHP strings are binary-safe + $binaryKey = "\x01\x02\x03\xFE"; + $binaryValue = "\xDE\xAD\xBE\xEF"; + + $client->set($binaryKey, $binaryValue); + $result = $client->get($binaryKey); // Returns binary string + ``` + + #### Type Handling + + * PHP strings are binary-safe and can store any byte sequence + * No special type or conversion needed for binary data + * Commands return `string` for string values, `mixed` for commands that may return different types + + C# uses `GlideString` as a wrapper type that can hold either a UTF-8 `string` or raw `byte[]` data. @@ -260,35 +291,4 @@ Valkey strings store sequences of bytes, which may include text, serialized obje byte[][] bytesArr = gsArr2.ToByteArrays(); // to byte[][] ``` - - - PHP uses native `string` type for all Valkey string operations. PHP strings are binary-safe and can contain arbitrary bytes. - - #### Usage - - Commands accept `string` arguments and return `string` or `mixed` values: - - ```php - // Create client - $client = new ValkeyGlide(); - $client->connect(addresses: [['host' => 'localhost', 'port' => 6379]]); - - // Using regular strings - $client->set('key', 'value'); - $result = $client->get('key'); // Returns string: 'value' - - // Using binary data - PHP strings are binary-safe - $binaryKey = "\x01\x02\x03\xFE"; - $binaryValue = "\xDE\xAD\xBE\xEF"; - - $client->set($binaryKey, $binaryValue); - $result = $client->get($binaryKey); // Returns binary string - ``` - - #### Type Handling - - * PHP strings are binary-safe and can store any byte sequence - * No special type or conversion needed for binary data - * Commands return `string` for string values, `mixed` for commands that may return different types - diff --git a/src/content/docs/concepts/architecture/async-execution.mdx b/src/content/docs/concepts/architecture/async-execution.mdx index 805e0ab1..2feae17f 100644 --- a/src/content/docs/concepts/architecture/async-execution.mdx +++ b/src/content/docs/concepts/architecture/async-execution.mdx @@ -46,13 +46,6 @@ To achieve this, each of GLIDE's clients supports the language's native asynchro ::: - - ```typescript - // Support Javascript Promises syntax - const status = await client.set("user:101", "active"); - ``` - - ```java // Async set operation using Future interface @@ -60,6 +53,13 @@ To achieve this, each of GLIDE's clients supports the language's native asynchro ``` + + ```typescript + // Support Javascript Promises syntax + const status = await client.set("user:101", "active"); + ``` + + ```go go func(ctx){ diff --git a/src/content/docs/concepts/architecture/memory-model.mdx b/src/content/docs/concepts/architecture/memory-model.mdx index 96d3ec37..a794d5aa 100644 --- a/src/content/docs/concepts/architecture/memory-model.mdx +++ b/src/content/docs/concepts/architecture/memory-model.mdx @@ -61,6 +61,16 @@ Under steady load, the Rust core also holds: ## Language-Specific Notes + + The Rust core's native memory is visible in process RSS but **not** reported + by `sys.getsizeof` or the `tracemalloc` module — those inspect Python + objects only. To observe total footprint use OS-level tools (`ps`, + `/proc/self/status`, container memory metrics, or `psutil`). + + Python response objects (`bytes`, `str`, lists for multi-value replies) live + on the CPython heap and are subject to normal reference-counted reclamation. + + GLIDE's Java client does **not** use JVM NIO direct (off-heap) buffers for network I/O. All socket reads and writes are performed in the Rust core; @@ -79,16 +89,6 @@ Under steady load, the Rust core also holds: point. - - The Rust core's native memory is visible in process RSS but **not** reported - by `sys.getsizeof` or the `tracemalloc` module — those inspect Python - objects only. To observe total footprint use OS-level tools (`ps`, - `/proc/self/status`, container memory metrics, or `psutil`). - - Python response objects (`bytes`, `str`, lists for multi-value replies) live - on the CPython heap and are subject to normal reference-counted reclamation. - - The Rust core allocates outside the V8 heap; V8's `process.memoryUsage()` reports `rss` (which includes the native side) and `heapUsed` (which does @@ -104,12 +104,6 @@ Under steady load, the Rust core also holds: based on OS-reported RSS, not `MemStats.Sys`. - - The Rust core's allocations are outside the managed heap and will not be - reported by `GC.GetTotalMemory`. Process-level counters - (`Process.WorkingSet64`, OS-level RSS) reflect the true footprint. - - GLIDE PHP is a C extension built against the Zend Engine (PHP's runtime). PHP objects — including associative arrays returned by commands — are @@ -127,6 +121,12 @@ Under steady load, the Rust core also holds: linked list on the C side. If the PHP process does not consume messages promptly, this queue grows without limit. + + + The Rust core's allocations are outside the managed heap and will not be + reported by `GC.GetTotalMemory`. Process-level counters + (`Process.WorkingSet64`, OS-level RSS) reflect the true footprint. + ## Tuning Knobs @@ -172,6 +172,13 @@ expected. **From the runtime:** + + ```python + import psutil, os + rss_mb = psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024 + ``` + + ```java // JVM heap @@ -186,13 +193,6 @@ expected. ``` - - ```python - import psutil, os - rss_mb = psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024 - ``` - - ```javascript const { rss, heapUsed, external } = process.memoryUsage(); @@ -207,13 +207,6 @@ expected. ``` - - ```csharp - using System.Diagnostics; - long rss = Process.GetCurrentProcess().WorkingSet64; - ``` - - ```php // emalloc-tracked memory only (excludes Rust core) @@ -224,6 +217,13 @@ expected. $peakMemory = memory_get_peak_usage(true); ``` + + + ```csharp + using System.Diagnostics; + long rss = Process.GetCurrentProcess().WorkingSet64; + ``` + **From the OS:** on Linux, `cat /proc/$PID/status | grep VmRSS` gives the 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 7862473e..41f5ff71 100644 --- a/src/content/docs/concepts/client-features/client-side-caching.mdx +++ b/src/content/docs/concepts/client-features/client-side-caching.mdx @@ -387,29 +387,6 @@ Multiple clients can share the same cache instance by passing the same `ClientSi ``` - - ```typescript - // Both clients share the same cache - const cache = ClientSideCache.create(1024, 60000); - - const client1 = await GlideClient.createClient({ - addresses: [{ host: "localhost", port: 6379 }], - clientSideCache: cache, - }); - const client2 = await GlideClient.createClient({ - addresses: [{ host: "localhost", port: 6379 }], - clientSideCache: cache, - }); - - // client1 populates the cache - await client1.set("key", "value"); - await client1.get("key"); // Cache miss — fetches from server - - // client2 gets a cache hit without contacting the server - const result = await client2.get("key"); // Cache hit - ``` - - ```java // Both clients share the same cache @@ -441,6 +418,29 @@ Multiple clients can share the same cache instance by passing the same `ClientSi ``` + + ```typescript + // Both clients share the same cache + const cache = ClientSideCache.create(1024, 60000); + + const client1 = await GlideClient.createClient({ + addresses: [{ host: "localhost", port: 6379 }], + clientSideCache: cache, + }); + const client2 = await GlideClient.createClient({ + addresses: [{ host: "localhost", port: 6379 }], + clientSideCache: cache, + }); + + // client1 populates the cache + await client1.set("key", "value"); + await client1.get("key"); // Cache miss — fetches from server + + // client2 gets a cache hit without contacting the server + const result = await client2.get("key"); // Cache hit + ``` + + ```go // Both clients share the same cache @@ -542,6 +542,10 @@ Once enabled, the client receives push invalidation messages from the server whe ### Enabling server-assisted mode + + Server-assisted invalidation is not yet available in the Python client. Progress is tracked in [issue #5963](https://github.com/valkey-io/valkey-glide/issues/5963). + + ```java import glide.api.GlideClient; @@ -563,10 +567,6 @@ Once enabled, the client receives push invalidation messages from the server whe ``` - - Server-assisted invalidation is not yet available in the Python client. Progress is tracked in [issue #5963](https://github.com/valkey-io/valkey-glide/issues/5963). - - Server-assisted invalidation is not yet available in the Node.js client. Progress is tracked in [issue #5964](https://github.com/valkey-io/valkey-glide/issues/5964). @@ -593,6 +593,10 @@ Once enabled, the client receives push invalidation messages from the server whe The `clientTrackingInfo()` command returns the current tracking state of the connection. This is useful for verifying that server-assisted caching is active. + + Not yet available. See [issue #5963](https://github.com/valkey-io/valkey-glide/issues/5963). + + ```java // Standalone client @@ -610,10 +614,6 @@ The `clientTrackingInfo()` command returns the current tracking state of the con ``` - - Not yet available. See [issue #5963](https://github.com/valkey-io/valkey-glide/issues/5963). - - Not yet available. See [issue #5964](https://github.com/valkey-io/valkey-glide/issues/5964). diff --git a/src/content/docs/concepts/client-features/cluster-scan.mdx b/src/content/docs/concepts/client-features/cluster-scan.mdx index 16a74500..b2b228d6 100644 --- a/src/content/docs/concepts/client-features/cluster-scan.mdx +++ b/src/content/docs/concepts/client-features/cluster-scan.mdx @@ -40,6 +40,12 @@ To start iterating, create a `ClusterScanCursor`: ``` + + ```php + $cursor = new ClusterScanCursor(); + ``` + + ```csharp using Valkey.Glide; @@ -54,12 +60,6 @@ To start iterating, create a `ClusterScanCursor`: } ``` - - - ```php - $cursor = new ClusterScanCursor(); - ``` - Each cursor returned by an iteration is an RC to a new state object. Using the same cursor object will handle the same scan iteration again. A new cursor object should be used for each iteration to continue the scan. diff --git a/src/content/docs/how-to/compressing-data.mdx b/src/content/docs/how-to/compressing-data.mdx index 16d8c84e..819fcccd 100644 --- a/src/content/docs/how-to/compressing-data.mdx +++ b/src/content/docs/how-to/compressing-data.mdx @@ -97,6 +97,21 @@ When enabled, values are transparently compressed before being sent to the serve ``` + + ```php + $client = new ValkeyGlide(); + $client->connect( + addresses: [['host' => 'localhost', 'port' => 6379]], + compression: [ + 'enabled' => true, + 'backend' => ValkeyGlide::COMPRESSION_BACKEND_ZSTD, + 'compression_level' => 3, + 'min_compression_size' => 128, + ], + ); + ``` + + ```csharp using Valkey.Glide; @@ -115,21 +130,6 @@ When enabled, values are transparently compressed before being sent to the serve await using var client = await GlideClient.CreateClient(config); ``` - - - ```php - $client = new ValkeyGlide(); - $client->connect( - addresses: [['host' => 'localhost', 'port' => 6379]], - compression: [ - 'enabled' => true, - 'backend' => ValkeyGlide::COMPRESSION_BACKEND_ZSTD, - 'compression_level' => 3, - 'min_compression_size' => 128, - ], - ); - ``` - ## When to use compression diff --git a/src/content/docs/how-to/connections/address-resolver.mdx b/src/content/docs/how-to/connections/address-resolver.mdx index fa8053d4..2134fbb1 100644 --- a/src/content/docs/how-to/connections/address-resolver.mdx +++ b/src/content/docs/how-to/connections/address-resolver.mdx @@ -119,25 +119,6 @@ Pass a callable to the address resolver parameter. The callable receives the ori ``` - - ```csharp - using Valkey.Glide; - - var realHost = "127.0.0.1"; - ushort realPort = 6379; - - var config = new ConnectionConfiguration.StandaloneClientConfigurationBuilder() - .WithAddress("host.unreachable", 9999) - .WithAddressResolver((host, port) => (realHost, realPort)) - .Build(); - - await using var client = await GlideClient.CreateClient(config); - - // Client is now connected to 127.0.0.1:6379 - await client.SetAsync("key", "value"); - ``` - - ```php $realHost = '127.0.0.1'; @@ -157,6 +138,25 @@ Pass a callable to the address resolver parameter. The callable receives the ori $client->set('key', 'value'); ``` + + + ```csharp + using Valkey.Glide; + + var realHost = "127.0.0.1"; + ushort realPort = 6379; + + var config = new ConnectionConfiguration.StandaloneClientConfigurationBuilder() + .WithAddress("host.unreachable", 9999) + .WithAddressResolver((host, port) => (realHost, realPort)) + .Build(); + + await using var client = await GlideClient.CreateClient(config); + + // Client is now connected to 127.0.0.1:6379 + await client.SetAsync("key", "value"); + ``` + ## Fallback Behavior @@ -252,6 +252,21 @@ If the resolver throws an exception or returns invalid data, the client falls ba ``` + + ```php + // This resolver throws — the client will use the original address + $client = new ValkeyGlide(); + $client->connect( + addresses: [['host' => 'localhost', 'port' => 6379]], + address_resolver: function (string $host, int $port): array { + throw new \RuntimeException('Resolution failed'); + }, + ); + + // Still connects to localhost:6379 + ``` + + ```csharp using Valkey.Glide; @@ -267,21 +282,6 @@ If the resolver throws an exception or returns invalid data, the client falls ba // Still connects to localhost:6379 ``` - - - ```php - // This resolver throws — the client will use the original address - $client = new ValkeyGlide(); - $client->connect( - addresses: [['host' => 'localhost', 'port' => 6379]], - address_resolver: function (string $host, int $port): array { - throw new \RuntimeException('Resolution failed'); - }, - ); - - // Still connects to localhost:6379 - ``` - ## Cluster Client @@ -361,6 +361,17 @@ The cluster client supports the same resolver signature. ``` + + ```php + $cluster = new ValkeyGlideCluster( + addresses: [['host' => 'internal-dns.service', 'port' => 7000]], + address_resolver: function (string $host, int $port): array { + return ['host' => getActualHost($host), 'port' => $port]; + }, + ); + ``` + + ```csharp using Valkey.Glide; @@ -376,17 +387,6 @@ The cluster client supports the same resolver signature. await using var client = await GlideClusterClient.CreateClient(config); ``` - - - ```php - $cluster = new ValkeyGlideCluster( - addresses: [['host' => 'internal-dns.service', 'port' => 7000]], - address_resolver: function (string $host, int $port): array { - return ['host' => getActualHost($host), 'port' => $port]; - }, - ); - ``` - ## Notes 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 62929244..f3f9eb0b 100644 --- a/src/content/docs/how-to/connections/configure-lazy-connection.mdx +++ b/src/content/docs/how-to/connections/configure-lazy-connection.mdx @@ -12,32 +12,6 @@ GLIDE supports **lazy connection mode**, which defers the establishment of physi Lazy connection can be configured through the client configuration object. - - ```typescript - import { GlideClient, GlideClusterClient } from "@valkey/valkey-glide"; - - // Standalone client with lazy connect - const standaloneClient = await GlideClient.createClient({ - addresses: [{ host: "localhost", port: 6379 }], - lazyConnect: true, - requestTimeout: 5000 - }); - - // Cluster client with lazy connect - const clusterClient = await GlideClusterClient.createClient({ - addresses: [ - { host: "localhost", port: 7000 }, - { host: "localhost", port: 7001 } - ], - lazyConnect: true, - requestTimeout: 5000 - }); - - // No connection established yet - this will trigger the connection - const result = await standaloneClient.ping(); - ``` - - ```python from glide import GlideClient, GlideClusterClient, NodeAddress @@ -98,6 +72,32 @@ Lazy connection can be configured through the client configuration object. ``` + + ```typescript + import { GlideClient, GlideClusterClient } from "@valkey/valkey-glide"; + + // Standalone client with lazy connect + const standaloneClient = await GlideClient.createClient({ + addresses: [{ host: "localhost", port: 6379 }], + lazyConnect: true, + requestTimeout: 5000 + }); + + // Cluster client with lazy connect + const clusterClient = await GlideClusterClient.createClient({ + addresses: [ + { host: "localhost", port: 7000 }, + { host: "localhost", port: 7001 } + ], + lazyConnect: true, + requestTimeout: 5000 + }); + + // No connection established yet - this will trigger the connection + const result = await standaloneClient.ping(); + ``` + + ```go import ( @@ -133,6 +133,34 @@ Lazy connection can be configured through the client configuration object. ``` + + ```php + // Standalone client with lazy connect + $standaloneClient = new ValkeyGlide(); + $standaloneClient->connect( + addresses: [['host' => 'localhost', 'port' => 6379]], + lazy_connect: true, + request_timeout: 5000 + ); + + // Cluster client with lazy connect + $clusterClient = new ValkeyGlideCluster( + addresses: [ + ['host' => 'localhost', 'port' => 7000], + ['host' => 'localhost', 'port' => 7001] + ], + lazy_connect: true, + request_timeout: 5000 + ); + + // No connection established yet - this will trigger the connection + $result = $standaloneClient->ping(); + + $standaloneClient->close(); + $clusterClient->close(); + ``` + + ```csharp using Valkey.Glide; @@ -162,34 +190,6 @@ Lazy connection can be configured through the client configuration object. ``` - - ```php - // Standalone client with lazy connect - $standaloneClient = new ValkeyGlide(); - $standaloneClient->connect( - addresses: [['host' => 'localhost', 'port' => 6379]], - lazy_connect: true, - request_timeout: 5000 - ); - - // Cluster client with lazy connect - $clusterClient = new ValkeyGlideCluster( - addresses: [ - ['host' => 'localhost', 'port' => 7000], - ['host' => 'localhost', 'port' => 7001] - ], - lazy_connect: true, - request_timeout: 5000 - ); - - // No connection established yet - this will trigger the connection - $result = $standaloneClient->ping(); - - $standaloneClient->close(); - $clusterClient->close(); - ``` - - ```ruby require "valkey" 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 5e452649..b8cba2d2 100644 --- a/src/content/docs/how-to/connections/limit-inflight-requests.mdx +++ b/src/content/docs/how-to/connections/limit-inflight-requests.mdx @@ -12,17 +12,12 @@ To ensure system stability and prevent out-of-memory (OOM) errors, Valkey GLIDE Inflight requests limit can be configured through the general client configurations. - - :::note - This feature is not yet available in GLIDE C#. - ::: - - - - ```go - // Limit to 1000 inflight requests per connection - clusterConfig := config.NewClusterClientConfiguration(). - WithInflightRequestsLimit(1000) + + ```python + # Limit to 1000 inflight requests per connection + cluster_config = GlideClusterClientConfiguration( + inflight_requests_limit=1000, + ) ``` @@ -44,19 +39,24 @@ Inflight requests limit can be configured through the general client configurati ``` + + ```go + // Limit to 1000 inflight requests per connection + clusterConfig := config.NewClusterClientConfiguration(). + WithInflightRequestsLimit(1000) + ``` + + :::note PHP GLIDE uses a synchronous blocking API, which means only one request can be in-flight at a time. Therefore, the inflight request limit configuration is not applicable and is not exposed in the PHP API. ::: - - ```python - # Limit to 1000 inflight requests per connection - cluster_config = GlideClusterClientConfiguration( - inflight_requests_limit=1000, - ) - ``` + + :::note + This feature is not yet available in GLIDE C#. + ::: diff --git a/src/content/docs/how-to/connections/read-strategy.mdx b/src/content/docs/how-to/connections/read-strategy.mdx index 386494cd..7e64cd0f 100644 --- a/src/content/docs/how-to/connections/read-strategy.mdx +++ b/src/content/docs/how-to/connections/read-strategy.mdx @@ -107,6 +107,23 @@ Valkey GLIDE provides support for the following read strategies, allowing you to ``` + + ```php + $addresses = [ + ['host' => 'address.example.com', 'port' => 6379] + ]; + + $client = new ValkeyGlideCluster( + addresses: $addresses, + read_from: ValkeyGlide::READ_FROM_PREFER_REPLICA + ); + + $client->set('key1', 'val1'); + // get will read from one of the replicas + $client->get('key1'); + ``` + + ```csharp using Valkey.Glide; @@ -125,23 +142,6 @@ Valkey GLIDE provides support for the following read strategies, allowing you to ``` - - ```php - $addresses = [ - ['host' => 'address.example.com', 'port' => 6379] - ]; - - $client = new ValkeyGlideCluster( - addresses: $addresses, - read_from: ValkeyGlide::READ_FROM_PREFER_REPLICA - ); - - $client->set('key1', 'val1'); - // get will read from one of the replicas - $client->get('key1'); - ``` - - ```ruby require "valkey" @@ -253,24 +253,6 @@ When using the AZ Affinity read strategy, the `clientAz` setting is required to ``` - - ```csharp - using Valkey.Glide; - using static Valkey.Glide.ConnectionConfiguration; - - var config = new ClusterClientConfigurationBuilder() - .WithAddress("address.example.com", 6379) - .WithReadFrom(new ReadFrom(ReadFromStrategy.AzAffinity, "us-east-1a")) - .Build(); - - await using var client = await GlideClusterClient.CreateClient(config); - await client.SetAsync("key1", "val1"); - - // GetAsync will read from one of the replicas in the same client's availability zone if they exist - await client.GetAsync("key1"); - ``` - - ```php $addresses = [ @@ -289,6 +271,24 @@ When using the AZ Affinity read strategy, the `clientAz` setting is required to ``` + + ```csharp + using Valkey.Glide; + using static Valkey.Glide.ConnectionConfiguration; + + var config = new ClusterClientConfigurationBuilder() + .WithAddress("address.example.com", 6379) + .WithReadFrom(new ReadFrom(ReadFromStrategy.AzAffinity, "us-east-1a")) + .Build(); + + await using var client = await GlideClusterClient.CreateClient(config); + await client.SetAsync("key1", "val1"); + + // GetAsync will read from one of the replicas in the same client's availability zone if they exist + await client.GetAsync("key1"); + ``` + + ```ruby require "valkey" @@ -401,24 +401,6 @@ When using the AZ Affinity Replicas and Primary read strategy, the `clientAz` se ``` - - ```csharp - using Valkey.Glide; - using static Valkey.Glide.ConnectionConfiguration; - - var config = new ClusterClientConfigurationBuilder() - .WithAddress("address.example.com", 6379) - .WithReadFrom(new ReadFrom(ReadFromStrategy.AzAffinityReplicasAndPrimary, "us-east-1a")) - .Build(); - - await using var client = await GlideClusterClient.CreateClient(config); - await client.SetAsync("key1", "val1"); - - // GetAsync will read from one of the replicas or the primary in the same client's availability zone if they exist - await client.GetAsync("key1"); - ``` - - ```php $addresses = [ @@ -437,6 +419,24 @@ When using the AZ Affinity Replicas and Primary read strategy, the `clientAz` se ``` + + ```csharp + using Valkey.Glide; + using static Valkey.Glide.ConnectionConfiguration; + + var config = new ClusterClientConfigurationBuilder() + .WithAddress("address.example.com", 6379) + .WithReadFrom(new ReadFrom(ReadFromStrategy.AzAffinityReplicasAndPrimary, "us-east-1a")) + .Build(); + + await using var client = await GlideClusterClient.CreateClient(config); + await client.SetAsync("key1", "val1"); + + // GetAsync will read from one of the replicas or the primary in the same client's availability zone if they exist + await client.GetAsync("key1"); + ``` + + ```ruby require "valkey" diff --git a/src/content/docs/how-to/execute-custom-scripts.mdx b/src/content/docs/how-to/execute-custom-scripts.mdx index e8fc1e47..fd6bf51d 100644 --- a/src/content/docs/how-to/execute-custom-scripts.mdx +++ b/src/content/docs/how-to/execute-custom-scripts.mdx @@ -279,43 +279,6 @@ The following steps shows how to run a simple a custom Lua script using GLIDE. - - - 1. Define the Lua script as a string. - 2. Create a `Script` object with the Lua code. - 3. Execute the script using `ScriptInvokeAsync`. - - -
- Full Example - - ```csharp - using Valkey.Glide; - using static Valkey.Glide.ConnectionConfiguration; - - var config = new StandaloneClientConfigurationBuilder().Build(); - await using var client = await GlideClient.CreateClient(config); - - // 1. Define the Lua script as a string - var lua = @" - server.call('SET', KEYS[1], ARGV[1]) - return KEYS[1] .. ': ' .. server.call('GET', KEYS[1]) - "; - - // 2. Create a `Script` object with the Lua code. - using var script = new Script(lua); - - // 3. Execute the script using `ScriptInvokeAsync`. - var options = new ScriptOptions() - .WithKeys("username") - .WithArgs("John Doe"); - var result = await client.ScriptInvokeAsync(script, options); - - Console.WriteLine(result); // username: John Doe - ``` -
-
- :::caution The `invokeScript` command is not supported in Valkey GLIDE PHP. @@ -381,6 +344,43 @@ The following steps shows how to run a simple a custom Lua script using GLIDE. ::: + + + 1. Define the Lua script as a string. + 2. Create a `Script` object with the Lua code. + 3. Execute the script using `ScriptInvokeAsync`. + + +
+ Full Example + + ```csharp + using Valkey.Glide; + using static Valkey.Glide.ConnectionConfiguration; + + var config = new StandaloneClientConfigurationBuilder().Build(); + await using var client = await GlideClient.CreateClient(config); + + // 1. Define the Lua script as a string + var lua = @" + server.call('SET', KEYS[1], ARGV[1]) + return KEYS[1] .. ': ' .. server.call('GET', KEYS[1]) + "; + + // 2. Create a `Script` object with the Lua code. + using var script = new Script(lua); + + // 3. Execute the script using `ScriptInvokeAsync`. + var options = new ScriptOptions() + .WithKeys("username") + .WithArgs("John Doe"); + var result = await client.ScriptInvokeAsync(script, options); + + Console.WriteLine(result); // username: John Doe + ``` +
+
+ 1. Define the lua script 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 d5cdc1b1..207049ca 100644 --- a/src/content/docs/how-to/load-and-execute-functions.mdx +++ b/src/content/docs/how-to/load-and-execute-functions.mdx @@ -313,46 +313,6 @@ The following example shows a simple example of loading and executing a Valkey F - - - 1. Define the Lua script as a string. - 2. Load the function to Valkey using `FunctionLoadAsync`. - 3. Call the function using `FCallAsync`. - - -
- Full Example - - ```csharp - using Valkey.Glide; - using static Valkey.Glide.ConnectionConfiguration; - - var config = new StandaloneClientConfigurationBuilder() - .WithAddress("localhost", 6379) - .Build(); - await using var client = await GlideClient.CreateClient(config); - - await client.SetAsync("page:home:visits", "0"); - - // 1. Define the Lua script as a string. - // lua code that updates a key and returns its new value - var luaCode = @"#!lua name=page_visits - server.register_function('update_visits', function(visits) - server.call('INCR', visits[1]) - return server.call('GET', visits[1]) - end) - "; - - // 2. Load the function to Valkey using `FunctionLoadAsync`. - await client.FunctionLoadAsync(luaCode, replace: true); - - // 3. Call the function using `FCallAsync`. - var result = await client.FCallAsync("update_visits", ["page:home:visits"], []); - Console.WriteLine(result); // 1 - ``` -
-
- 1. Define the lua script. @@ -410,6 +370,46 @@ The following example shows a simple example of loading and executing a Valkey F + + + 1. Define the Lua script as a string. + 2. Load the function to Valkey using `FunctionLoadAsync`. + 3. Call the function using `FCallAsync`. + + +
+ Full Example + + ```csharp + using Valkey.Glide; + using static Valkey.Glide.ConnectionConfiguration; + + var config = new StandaloneClientConfigurationBuilder() + .WithAddress("localhost", 6379) + .Build(); + await using var client = await GlideClient.CreateClient(config); + + await client.SetAsync("page:home:visits", "0"); + + // 1. Define the Lua script as a string. + // lua code that updates a key and returns its new value + var luaCode = @"#!lua name=page_visits + server.register_function('update_visits', function(visits) + server.call('INCR', visits[1]) + return server.call('GET', visits[1]) + end) + "; + + // 2. Load the function to Valkey using `FunctionLoadAsync`. + await client.FunctionLoadAsync(luaCode, replace: true); + + // 3. Call the function using `FCallAsync`. + var result = await client.FCallAsync("update_visits", ["page:home:visits"], []); + Console.WriteLine(result); // 1 + ``` +
+
+ 1. Define the lua script. @@ -558,6 +558,25 @@ GLIDE clients implement `FCALL_RO` command to execute read-only functions. ``` + + ```php + $result = $client->fcall_ro('get_value', ['mykey']); + ``` + + **With Routing (Cluster Mode)** + + ```php + // Route to all nodes + $result = $client->fcall_ro('get_value', [], [], 'allNodes'); + + // Route to all primary nodes + $result = $client->fcall_ro('get_value', [], [], 'allPrimaries'); + + // Route to a random node + $result = $client->fcall_ro('get_value', [], [], 'randomNode'); + ``` + + ```csharp using Valkey.Glide; @@ -584,25 +603,6 @@ GLIDE clients implement `FCALL_RO` command to execute read-only functions. ``` - - ```php - $result = $client->fcall_ro('get_value', ['mykey']); - ``` - - **With Routing (Cluster Mode)** - - ```php - // Route to all nodes - $result = $client->fcall_ro('get_value', [], [], 'allNodes'); - - // Route to all primary nodes - $result = $client->fcall_ro('get_value', [], [], 'allPrimaries'); - - // Route to a random node - $result = $client->fcall_ro('get_value', [], [], 'randomNode'); - ``` - - ```ruby result = client.fcall_ro("get_value", keys: ["mykey"]) 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 eb66abea..e7c5ef20 100644 --- a/src/content/docs/how-to/modules-api/json-module.mdx +++ b/src/content/docs/how-to/modules-api/json-module.mdx @@ -22,6 +22,32 @@ Use `INFO MODULES` or `MODULE LIST` command to see the status of all loaded modu ## Examples + + :::tip[API Reference] + The API reference can be found [here](https://glide.valkey.io/languages/python/api/reference/glide/async_commands/glide_json/). + ::: + + ```python + from glide import GlideClient, GlideClientConfiguration, NodeAddress, glide_json + import json + + # Both standalone and cluster mode are supported + config = GlideClientConfiguration([NodeAddress("localhost", 6379)]) + client = await GlideClient.create(config) + value = {'a': 1.0, 'b': 2} + json_str = json.dumps(value) # Convert Python dictionary to JSON string using json.dumps() + + # Sets the value at `doc` as a JSON object. + set_response = await glide_json.set(client, "doc", "$", json_str) + print(set_response) # "OK" - Indicates successful setting of the value at path '$' in the key stored at `doc`. + + # Gets the value at path '$' in the JSON document stored at `doc`. + get_response = await glide_json.get(client, "doc", "$") + print(get_response) # b"[{\"a\":1.0,\"b\":2}]" + json.loads(str(get_response)) # [{"a": 1.0, "b" :2}] - JSON object retrieved from the key `doc` using json.loads() + ``` + + :::tip[API Reference] The API reference can be found [here](https://glide.valkey.io/languages/java/api/glide/api/commands/servermodules/Json.html). @@ -80,32 +106,6 @@ Use `INFO MODULES` or `MODULE LIST` command to see the status of all loaded modu ``` - - :::tip[API Reference] - The API reference can be found [here](https://glide.valkey.io/languages/python/api/reference/glide/async_commands/glide_json/). - ::: - - ```python - from glide import GlideClient, GlideClientConfiguration, NodeAddress, glide_json - import json - - # Both standalone and cluster mode are supported - config = GlideClientConfiguration([NodeAddress("localhost", 6379)]) - client = await GlideClient.create(config) - value = {'a': 1.0, 'b': 2} - json_str = json.dumps(value) # Convert Python dictionary to JSON string using json.dumps() - - # Sets the value at `doc` as a JSON object. - set_response = await glide_json.set(client, "doc", "$", json_str) - print(set_response) # "OK" - Indicates successful setting of the value at path '$' in the key stored at `doc`. - - # Gets the value at path '$' in the JSON document stored at `doc`. - get_response = await glide_json.get(client, "doc", "$") - print(get_response) # b"[{\"a\":1.0,\"b\":2}]" - json.loads(str(get_response)) # [{"a": 1.0, "b" :2}] - JSON object retrieved from the key `doc` using json.loads() - ``` - - ```go import ( 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 c0f54b45..8a52268b 100644 --- a/src/content/docs/how-to/modules-api/search-module.mdx +++ b/src/content/docs/how-to/modules-api/search-module.mdx @@ -23,6 +23,68 @@ Use `INFO MODULES` or `MODULE LIST` command to see the status of all loaded modu ## Examples + + :::tip[API Reference] + The API reference can be found [here](https://glide.valkey.io/languages/python/api/reference/glide/async_commands/ft/). + ::: + + ```python + from glide import ( + GlideClient, + GlideClientConfiguration, + NodeAddress, + ft, + glide_json, + DataType, + NumericField, + ReturnField, + FtCreateOptions, + FtSearchOptions, + ) + import json + import time + + # Both standalone and cluster mode are supported + config = GlideClientConfiguration([NodeAddress("localhost", 6379)]) + client = await GlideClient.create(config) + + prefix = "{json}:" + json_key1 = prefix + "1" + json_key2 = prefix + "2" + json_value1 = {"a": 11111, "b": 2, "c": 3} + json_value2 = {"a": 22222, "b": 2, "c": 3} + index = prefix + "index" + + # FT.CREATE + await ft.create( + client, + index, + schema=[ + NumericField("$.a", "a"), + NumericField("$.b", "b"), + ], + options=FtCreateOptions(data_type=DataType.JSON, prefixes=[prefix]), + ) + + await glide_json.set(client, json_key1, "$", json.dumps(json_value1)) + await glide_json.set(client, json_key2, "$", json.dumps(json_value2)) + + time.sleep(1) # let server digest the data and update index + + # FT.SEARCH + ft_search_options = FtSearchOptions( + return_fields=[ + ReturnField(field_identifier="a", alias="a_new"), + ReturnField(field_identifier="b", alias="b_new"), + ] + ) + + search_result = await ft.search(client, index, "@a:[-inf +inf]", options=ft_search_options) + # search_result[0] == 2 + # search_result[1] contains results with "{json}:1" and "{json}:2" + ``` + + :::tip[Javadoc] The API reference can be found [here](https://glide.valkey.io/languages/java/api/glide/api/commands/servermodules/FT.html). @@ -151,68 +213,6 @@ Use `INFO MODULES` or `MODULE LIST` command to see the status of all loaded modu ``` - - :::tip[API Reference] - The API reference can be found [here](https://glide.valkey.io/languages/python/api/reference/glide/async_commands/ft/). - ::: - - ```python - from glide import ( - GlideClient, - GlideClientConfiguration, - NodeAddress, - ft, - glide_json, - DataType, - NumericField, - ReturnField, - FtCreateOptions, - FtSearchOptions, - ) - import json - import time - - # Both standalone and cluster mode are supported - config = GlideClientConfiguration([NodeAddress("localhost", 6379)]) - client = await GlideClient.create(config) - - prefix = "{json}:" - json_key1 = prefix + "1" - json_key2 = prefix + "2" - json_value1 = {"a": 11111, "b": 2, "c": 3} - json_value2 = {"a": 22222, "b": 2, "c": 3} - index = prefix + "index" - - # FT.CREATE - await ft.create( - client, - index, - schema=[ - NumericField("$.a", "a"), - NumericField("$.b", "b"), - ], - options=FtCreateOptions(data_type=DataType.JSON, prefixes=[prefix]), - ) - - await glide_json.set(client, json_key1, "$", json.dumps(json_value1)) - await glide_json.set(client, json_key2, "$", json.dumps(json_value2)) - - time.sleep(1) # let server digest the data and update index - - # FT.SEARCH - ft_search_options = FtSearchOptions( - return_fields=[ - ReturnField(field_identifier="a", alias="a_new"), - ReturnField(field_identifier="b", alias="b_new"), - ] - ) - - search_result = await ft.search(client, index, "@a:[-inf +inf]", options=ft_search_options) - # search_result[0] == 2 - # search_result[1] contains results with "{json}:1" and "{json}:2" - ``` - - ```go import ( diff --git a/src/content/docs/how-to/monitoring/logging.mdx b/src/content/docs/how-to/monitoring/logging.mdx index ea8615c0..81a1bdad 100644 --- a/src/content/docs/how-to/monitoring/logging.mdx +++ b/src/content/docs/how-to/monitoring/logging.mdx @@ -47,55 +47,6 @@ The following examples show how to configure the logger in GLIDE clients. Refer to the API [reference](https://glide.valkey.io/languages/python/api/glide_async/logger/?h=logger) for the full logger interface. - - ```typescript - import { GlideClient, Logger } from "@valkey/valkey-glide"; - - // Configure the log level and the output file name. - Logger.setLoggerConfig("debug", "./glide_logs"); - - // Logging example - const message = "Hello World!"; - const identifier = "LoggingExample"; - Logger.log("info", identifier, message); - - // Standalone client configuration - const addresses = [{ host: "localhost", port: 6379 }]; - const client = await GlideClient.createClient({ - addresses: addresses, - }); - - await client.ping(); - - ``` - - Refer to the API [reference](https://glide.valkey.io/languages/nodejs/api/classes/Logger.Logger.html) for the full logger interface. - - - - ```csharp - using Valkey.Glide; - using static Valkey.Glide.ConnectionConfiguration; - - // Configure the log level and the output file name. - Logger.SetLoggerConfig(Level.Debug, "./glide_logs"); - - // Logging example - Logger.Log(Level.Info, "LoggingExample", "Hello World!"); - - // Standalone client configuration - var config = new StandaloneClientConfigurationBuilder() - .WithAddress("localhost", 6379) - .Build(); - - // Create a standalone client - await using var client = await GlideClient.CreateClient(config); - await client.PingAsync(); - ``` - - Refer to the API [reference](https://github.com/valkey-io/valkey-glide-csharp/blob/main/sources/Valkey.Glide/Logger.cs#L33) for the full logger interface. - - ```java package com.example; @@ -140,6 +91,31 @@ The following examples show how to configure the logger in GLIDE clients. Refer to the API [reference](https://glide.valkey.io/languages/java/api/glide/api/logging/Logger.html) for the full logger interface. + + ```typescript + import { GlideClient, Logger } from "@valkey/valkey-glide"; + + // Configure the log level and the output file name. + Logger.setLoggerConfig("debug", "./glide_logs"); + + // Logging example + const message = "Hello World!"; + const identifier = "LoggingExample"; + Logger.log("info", identifier, message); + + // Standalone client configuration + const addresses = [{ host: "localhost", port: 6379 }]; + const client = await GlideClient.createClient({ + addresses: addresses, + }); + + await client.ping(); + + ``` + + Refer to the API [reference](https://glide.valkey.io/languages/nodejs/api/classes/Logger.Logger.html) for the full logger interface. + + ```php // Configure the log level and the output file name @@ -161,6 +137,30 @@ The following examples show how to configure the logger in GLIDE clients. Refer to the [logger functions](https://github.com/valkey-io/valkey-glide-php/blob/main/logger.stub.php) for the full logger interface. + + + ```csharp + using Valkey.Glide; + using static Valkey.Glide.ConnectionConfiguration; + + // Configure the log level and the output file name. + Logger.SetLoggerConfig(Level.Debug, "./glide_logs"); + + // Logging example + Logger.Log(Level.Info, "LoggingExample", "Hello World!"); + + // Standalone client configuration + var config = new StandaloneClientConfigurationBuilder() + .WithAddress("localhost", 6379) + .Build(); + + // Create a standalone client + await using var client = await GlideClient.CreateClient(config); + await client.PingAsync(); + ``` + + Refer to the API [reference](https://github.com/valkey-io/valkey-glide-csharp/blob/main/sources/Valkey.Glide/Logger.cs#L33) for the full logger interface. + ## Log Files diff --git a/src/content/docs/how-to/monitoring/monitor-command.mdx b/src/content/docs/how-to/monitoring/monitor-command.mdx index 99031092..00e04b39 100644 --- a/src/content/docs/how-to/monitoring/monitor-command.mdx +++ b/src/content/docs/how-to/monitoring/monitor-command.mdx @@ -48,22 +48,6 @@ There are two modes for consuming monitor messages: ``` - - ```typescript - import { GlideMonitorClient, MonitorLine } from "@valkey/valkey-glide"; - - const monitor = await GlideMonitorClient.create( - { addresses: [{ host: "localhost", port: 6379 }] }, - (line: MonitorLine) => { - console.log(`${line.timestamp} [${line.db}] ${line.clientAddr} ${line.command} ${line.args.join(" ")}`); - }, - ); - - // Close when done. - await monitor.close(); - ``` - - ```java import glide.api.MonitorClient; @@ -84,6 +68,22 @@ There are two modes for consuming monitor messages: ``` + + ```typescript + import { GlideMonitorClient, MonitorLine } from "@valkey/valkey-glide"; + + const monitor = await GlideMonitorClient.create( + { addresses: [{ host: "localhost", port: 6379 }] }, + (line: MonitorLine) => { + console.log(`${line.timestamp} [${line.db}] ${line.clientAddr} ${line.command} ${line.args.join(" ")}`); + }, + ); + + // Close when done. + await monitor.close(); + ``` + + ```go package main @@ -134,21 +134,6 @@ There are two modes for consuming monitor messages: ``` - - ```typescript - import { GlideMonitorClient } from "@valkey/valkey-glide"; - - const monitor = await GlideMonitorClient.create({ - addresses: [{ host: "localhost", port: 6379 }], - }); - - const line = await monitor.getNextMessage(); - console.log(`${line.command} ${line.args.join(" ")}`); - - await monitor.close(); - ``` - - ```java import glide.api.MonitorClient; @@ -167,6 +152,21 @@ There are two modes for consuming monitor messages: ``` + + ```typescript + import { GlideMonitorClient } from "@valkey/valkey-glide"; + + const monitor = await GlideMonitorClient.create({ + addresses: [{ host: "localhost", port: 6379 }], + }); + + const line = await monitor.getNextMessage(); + console.log(`${line.command} ${line.args.join(" ")}`); + + await monitor.close(); + ``` + + ```go package main diff --git a/src/content/docs/how-to/monitoring/open-telemetry.mdx b/src/content/docs/how-to/monitoring/open-telemetry.mdx index 2ca79b27..bf9e3e5f 100644 --- a/src/content/docs/how-to/monitoring/open-telemetry.mdx +++ b/src/content/docs/how-to/monitoring/open-telemetry.mdx @@ -109,29 +109,6 @@ GLIDE does not export data directly to third-party services—instead, it sends ``` - - ```csharp - using Valkey.Glide; - - OpenTelemetry.Init( - OpenTelemetryConfig.CreateBuilder() - .WithTraces( - TracesConfig.CreateBuilder() - .WithEndpoint("http://localhost:4318/v1/traces") - .WithSamplePercentage(10) // Optional, defaults to 1. Can also be changed at runtime via SetSamplePercentage(). - .Build() - ) - .WithMetrics( - MetricsConfig.CreateBuilder() - .WithEndpoint("http://localhost:4318/v1/metrics") - .Build() - ) - .WithFlushInterval(TimeSpan.FromSeconds(1)) // Optional, defaults to 5000 ms - .Build() - ); - ``` - - ```php use ValkeyGlide\OpenTelemetry\{OpenTelemetryConfig, TracesConfig, MetricsConfig}; @@ -160,6 +137,29 @@ GLIDE does not export data directly to third-party services—instead, it sends ``` + + ```csharp + using Valkey.Glide; + + OpenTelemetry.Init( + OpenTelemetryConfig.CreateBuilder() + .WithTraces( + TracesConfig.CreateBuilder() + .WithEndpoint("http://localhost:4318/v1/traces") + .WithSamplePercentage(10) // Optional, defaults to 1. Can also be changed at runtime via SetSamplePercentage(). + .Build() + ) + .WithMetrics( + MetricsConfig.CreateBuilder() + .WithEndpoint("http://localhost:4318/v1/metrics") + .Build() + ) + .WithFlushInterval(TimeSpan.FromSeconds(1)) // Optional, defaults to 5000 ms + .Build() + ); + ``` + + ```ruby require "valkey" diff --git a/src/content/docs/how-to/monitoring/tracking-resources.mdx b/src/content/docs/how-to/monitoring/tracking-resources.mdx index 560ab981..2270a591 100644 --- a/src/content/docs/how-to/monitoring/tracking-resources.mdx +++ b/src/content/docs/how-to/monitoring/tracking-resources.mdx @@ -105,12 +105,6 @@ GLIDE 1.2 introduces a new non-Valkey API: `getStatistics` which returns statist ``` - - :::note[Coming soon] - Statistics API documentation for C# is coming soon. - ::: - - ```php $addresses = [ @@ -131,6 +125,12 @@ GLIDE 1.2 introduces a new non-Valkey API: `getStatistics` which returns statist ``` + + :::note[Coming soon] + Statistics API documentation for C# is coming soon. + ::: + + ```ruby require "valkey" diff --git a/src/content/docs/how-to/publish-and-subscribe-messages.mdx b/src/content/docs/how-to/publish-and-subscribe-messages.mdx index 005e57f5..2f9b5255 100644 --- a/src/content/docs/how-to/publish-and-subscribe-messages.mdx +++ b/src/content/docs/how-to/publish-and-subscribe-messages.mdx @@ -26,6 +26,19 @@ Since the RESP protocol does not guarantee strong delivery semantics for PubSub To publish a message, create a separate client to your Valkey instance. Both Standalone and Cluster are supported. + + ```py + publisher_config = GlideClientConfiguration( + [NodeAddress("localhost", 6379)] + ) + + publisher = await GlideClient.create(publisher_config) + + # Publish message on 'ch1' channel + await publisher.publish("Test message", "ch1") + ``` + + ```java GlideClientConfiguration publisherConfig = GlideClientConfiguration.builder() @@ -52,19 +65,6 @@ To publish a message, create a separate client to your Valkey instance. Both Sta ``` - - ```py - publisher_config = GlideClientConfiguration( - [NodeAddress("localhost", 6379)] - ) - - publisher = await GlideClient.create(publisher_config) - - # Publish message on 'ch1' channel - await publisher.publish("Test message", "ch1") - ``` - - ```go publisher, _ := NewGlideClient(NewGlideClientConfiguration(). @@ -116,6 +116,41 @@ Starting with GLIDE 2.3, you can subscribe to channels and patterns at any point No special configuration is needed at client creation time — you can subscribe dynamically on any client. Messages are buffered and retrievable via polling by default. If you want callback-based delivery instead (explained [below](#callback)), provide a subscription configuration with a callback at client creation time (the initial channel set can be empty). + + ```py + # Create a regular client — no subscription configuration needed + config = GlideClientConfiguration( + [NodeAddress("localhost", 6379)], + ) + client = await GlideClient.create(config) + + # --- Exact channel subscriptions --- + + # Blocking: waits up to 5000ms for server confirmation + await client.subscribe({"news", "updates"}, timeout_ms=5000) + + # Non-blocking (lazy): returns immediately, subscribes in background + await client.subscribe_lazy({"alerts"}) + + # --- Pattern subscriptions --- + + # Blocking + await client.psubscribe({"chat*", "event*"}, timeout_ms=5000) + + # Non-blocking (lazy) + await client.psubscribe_lazy({"log*"}) + + # --- Sharded subscriptions (cluster mode only) --- + # await cluster_client.ssubscribe({"shard-ch1"}, timeout_ms=5000) + # await cluster_client.ssubscribe_lazy({"shard-ch2"}) + + # Retrieve messages via polling + msg = await client.get_pubsub_message() + + await client.close() + ``` + + ```java // Create a regular client — no subscription configuration needed @@ -188,41 +223,6 @@ No special configuration is needed at client creation time — you can subscribe ``` - - ```py - # Create a regular client — no subscription configuration needed - config = GlideClientConfiguration( - [NodeAddress("localhost", 6379)], - ) - client = await GlideClient.create(config) - - # --- Exact channel subscriptions --- - - # Blocking: waits up to 5000ms for server confirmation - await client.subscribe({"news", "updates"}, timeout_ms=5000) - - # Non-blocking (lazy): returns immediately, subscribes in background - await client.subscribe_lazy({"alerts"}) - - # --- Pattern subscriptions --- - - # Blocking - await client.psubscribe({"chat*", "event*"}, timeout_ms=5000) - - # Non-blocking (lazy) - await client.psubscribe_lazy({"log*"}) - - # --- Sharded subscriptions (cluster mode only) --- - # await cluster_client.ssubscribe({"shard-ch1"}, timeout_ms=5000) - # await cluster_client.ssubscribe_lazy({"shard-ch2"}) - - # Retrieve messages via polling - msg = await client.get_pubsub_message() - - await client.close() - ``` - - ```go ctx := context.Background() @@ -339,6 +339,37 @@ No special configuration is needed at client creation time — you can subscribe For GLIDE versions prior to 2.3, or when your subscriptions are known at startup, you can define them in the client configuration. These subscriptions are applied immediately when the client connects. + + ```py + # Define callback and context + def callback(msg: CoreCommands.PubSubMsg, ctx: Any): + print(f"Received {msg}, context {ctx}\n") + context = "example" + + # Configure the client to invoke the callback for messages published + # to 'ch1' and 'ch2' and to channels matched by 'chat*' glob pattern. + subscription_config = GlideClientConfiguration.PubSubSubscriptions( + channels_and_patterns={ + GlideClientConfiguration.PubSubChannelModes.Exact: {"ch1", "ch2"}, + GlideClientConfiguration.PubSubChannelModes.Pattern: {"chat*"} + }, + callback=callback, + context=context, + ) + + config = GlideClientConfiguration( + [NodeAddress("localhost", 6379)], + pubsub_subscriptions=subscription_config + ) + + listening_client = await GlideClient.create(config) + + # Do some work/wait - the callback will be invoked on incoming messages + + await listening_client.close() # Unsubscribe happens here + ``` + + ```java // Define callback and context @@ -399,37 +430,6 @@ For GLIDE versions prior to 2.3, or when your subscriptions are known at startup ``` - - ```py - # Define callback and context - def callback(msg: CoreCommands.PubSubMsg, ctx: Any): - print(f"Received {msg}, context {ctx}\n") - context = "example" - - # Configure the client to invoke the callback for messages published - # to 'ch1' and 'ch2' and to channels matched by 'chat*' glob pattern. - subscription_config = GlideClientConfiguration.PubSubSubscriptions( - channels_and_patterns={ - GlideClientConfiguration.PubSubChannelModes.Exact: {"ch1", "ch2"}, - GlideClientConfiguration.PubSubChannelModes.Pattern: {"chat*"} - }, - callback=callback, - context=context, - ) - - config = GlideClientConfiguration( - [NodeAddress("localhost", 6379)], - pubsub_subscriptions=subscription_config - ) - - listening_client = await GlideClient.create(config) - - # Do some work/wait - the callback will be invoked on incoming messages - - await listening_client.close() # Unsubscribe happens here - ``` - - ```go // Define callback and context @@ -504,6 +504,38 @@ When a callback is configured, all incoming messages are sent to that callback. To use callback-based delivery, provide a subscription configuration with a callback at client creation time. The callback fires for every incoming message — from both config-based and dynamic subscriptions. + + ```py + received = [] + + def callback(msg: CoreCommands.PubSubMsg, context: Any): + received.append(msg.message) + print(f"Received '{msg.message}' on '{msg.channel}'") + + config = GlideClientConfiguration( + [NodeAddress("localhost", 6379)], + pubsub_subscriptions=GlideClientConfiguration.PubSubSubscriptions( + channels_and_patterns={}, + callback=callback, + context=None, + ), + ) + client = await GlideClient.create(config) + + # Subscribe dynamically — messages are delivered to the callback + await client.subscribe({"news"}, timeout_ms=5000) + + # Publish a message (from another client) + await publishing_client.publish("Hello!", "news") + await asyncio.sleep(0.5) + + # Verify the callback received the message + assert "Hello!" in received + + await client.close() + ``` + + ```java List received = Collections.synchronizedList(new ArrayList<>()); @@ -565,38 +597,6 @@ To use callback-based delivery, provide a subscription configuration with a call ``` - - ```py - received = [] - - def callback(msg: CoreCommands.PubSubMsg, context: Any): - received.append(msg.message) - print(f"Received '{msg.message}' on '{msg.channel}'") - - config = GlideClientConfiguration( - [NodeAddress("localhost", 6379)], - pubsub_subscriptions=GlideClientConfiguration.PubSubSubscriptions( - channels_and_patterns={}, - callback=callback, - context=None, - ), - ) - client = await GlideClient.create(config) - - # Subscribe dynamically — messages are delivered to the callback - await client.subscribe({"news"}, timeout_ms=5000) - - # Publish a message (from another client) - await publishing_client.publish("Hello!", "news") - await asyncio.sleep(0.5) - - # Verify the callback received the message - assert "Hello!" in received - - await client.close() - ``` - - ```go var received []string @@ -685,6 +685,34 @@ If no callback is configured, messages are buffered in an unbounded queue. You r * **Non-blocking poll:** `tryGetPubSubMessage()` / `try_get_pubsub_message()` — returns the next message or `null`/`None` immediately. + + ```py + # Configure the client to receive messages published to 'ch1' + # and 'ch2' and to channels matched by 'chat*' glob pattern. + subscriptions_config = GlideClientConfiguration.PubSubSubscriptions( + channels_and_patterns={ + GlideClientConfiguration.PubSubChannelModes.Exact: {"ch1", "ch2"}, + GlideClientConfiguration.PubSubChannelModes.Pattern: {"chat*"}, + }, + ) + + config = GlideClientConfiguration( + [NodeAddress("localhost", 6379)], + pubsub_subscriptions=subscriptions_config, + ) + + listening_client = await GlideClient.create(config) + + # Non-blocking: returns None if no message is available + message = listening_client.try_get_pubsub_message() + + # Async: waits for the next message + message = await listening_client.get_pubsub_message() + + await listening_client.close() # Unsubscribe happens here + ``` + + ```java // Configure the client to receive messages published to 'ch1' @@ -738,34 +766,6 @@ If no callback is configured, messages are buffered in an unbounded queue. You r ``` - - ```py - # Configure the client to receive messages published to 'ch1' - # and 'ch2' and to channels matched by 'chat*' glob pattern. - subscriptions_config = GlideClientConfiguration.PubSubSubscriptions( - channels_and_patterns={ - GlideClientConfiguration.PubSubChannelModes.Exact: {"ch1", "ch2"}, - GlideClientConfiguration.PubSubChannelModes.Pattern: {"chat*"}, - }, - ) - - config = GlideClientConfiguration( - [NodeAddress("localhost", 6379)], - pubsub_subscriptions=subscriptions_config, - ) - - listening_client = await GlideClient.create(config) - - # Non-blocking: returns None if no message is available - message = listening_client.try_get_pubsub_message() - - # Async: waits for the next message - message = await listening_client.get_pubsub_message() - - await listening_client.close() # Unsubscribe happens here - ``` - - ```go // Create a signal channel to receive notifications of new messages @@ -873,6 +873,33 @@ Prior to GLIDE 2.3, runtime unsubscribing is not available. Subscriptions are re ::: + + ```py + # Unsubscribe from specific exact channels (blocking) + await client.unsubscribe({"news"}, timeout_ms=5000) + + # Unsubscribe from specific exact channels (lazy) + await client.unsubscribe_lazy({"alerts"}) + + # Unsubscribe from all exact channels + await client.unsubscribe(ALL_CHANNELS, timeout_ms=5000) + + # Unsubscribe from specific patterns (blocking) + await client.punsubscribe({"chat*"}, timeout_ms=5000) + + # Unsubscribe from specific patterns (lazy) + await client.punsubscribe_lazy({"log*"}) + + # Unsubscribe from all patterns + await client.punsubscribe(ALL_PATTERNS, timeout_ms=5000) + + # Sharded unsubscribe (cluster mode only) + # await cluster_client.sunsubscribe({"shard-ch1"}, timeout_ms=5000) + # await cluster_client.sunsubscribe_lazy({"shard-ch2"}) + # await cluster_client.sunsubscribe(ALL_SHARDED_CHANNELS, timeout_ms=5000) + ``` + + ```java // Unsubscribe from specific exact channels (blocking) @@ -927,33 +954,6 @@ Prior to GLIDE 2.3, runtime unsubscribing is not available. Subscriptions are re ``` - - ```py - # Unsubscribe from specific exact channels (blocking) - await client.unsubscribe({"news"}, timeout_ms=5000) - - # Unsubscribe from specific exact channels (lazy) - await client.unsubscribe_lazy({"alerts"}) - - # Unsubscribe from all exact channels - await client.unsubscribe(ALL_CHANNELS, timeout_ms=5000) - - # Unsubscribe from specific patterns (blocking) - await client.punsubscribe({"chat*"}, timeout_ms=5000) - - # Unsubscribe from specific patterns (lazy) - await client.punsubscribe_lazy({"log*"}) - - # Unsubscribe from all patterns - await client.punsubscribe(ALL_PATTERNS, timeout_ms=5000) - - # Sharded unsubscribe (cluster mode only) - # await cluster_client.sunsubscribe({"shard-ch1"}, timeout_ms=5000) - # await cluster_client.sunsubscribe_lazy({"shard-ch2"}) - # await cluster_client.sunsubscribe(ALL_SHARDED_CHANNELS, timeout_ms=5000) - ``` - - ```go // Unsubscribe from specific exact channels (blocking) @@ -1046,6 +1046,14 @@ Prior to GLIDE 2.3, runtime unsubscribing is not available. Subscriptions are re Use `get_subscriptions()` to inspect the current subscription state. It returns both the **desired** subscriptions (what you've requested) and the **actual** subscriptions (what the server has confirmed). This is useful for verifying that lazy subscriptions have been fully applied. + + ```py + state = await client.get_subscriptions() + print(f"Desired: {state.desired_subscriptions}") + print(f"Actual: {state.actual_subscriptions}") + ``` + + ```java PubSubState state = client.getSubscriptions().get(); @@ -1062,14 +1070,6 @@ Use `get_subscriptions()` to inspect the current subscription state. It returns ``` - - ```py - state = await client.get_subscriptions() - print(f"Desired: {state.desired_subscriptions}") - print(f"Actual: {state.actual_subscriptions}") - ``` - - ```go state, err := client.GetSubscriptions(ctx) diff --git a/src/content/docs/how-to/scan-cluster.mdx b/src/content/docs/how-to/scan-cluster.mdx index c633b5bc..00ba1dc5 100644 --- a/src/content/docs/how-to/scan-cluster.mdx +++ b/src/content/docs/how-to/scan-cluster.mdx @@ -78,21 +78,6 @@ For a detailed explanation of Cluster Scan, see our [article](/concepts/client-f ``` - - ```csharp - using Valkey.Glide; - using static Valkey.Glide.ConnectionConfiguration; - - var clusterConfig = new ClusterClientConfigurationBuilder().Build(); - await using var clusterClient = await GlideClusterClient.CreateClient(clusterConfig); - - await foreach (var key in clusterClient.ScanAsync()) - { - Console.WriteLine($"Key: {key}"); - } - ``` - - ```php $cursor = new ClusterScanCursor(); @@ -113,6 +98,21 @@ For a detailed explanation of Cluster Scan, see our [article](/concepts/client-f } ``` + + + ```csharp + using Valkey.Glide; + using static Valkey.Glide.ConnectionConfiguration; + + var clusterConfig = new ClusterClientConfigurationBuilder().Build(); + await using var clusterClient = await GlideClusterClient.CreateClient(clusterConfig); + + await foreach (var key in clusterClient.ScanAsync()) + { + Console.WriteLine($"Key: {key}"); + } + ``` + ## Scan Using Patterns @@ -198,6 +198,29 @@ For a detailed explanation of Cluster Scan, see our [article](/concepts/client-f ``` + + ```php + $client->mset(['my_key1' => 'value1', 'my_key2' => 'value2', 'not_my_key' => 'value3', 'something_else' => 'value4']); + + $cursor = new ClusterScanCursor(); + $matchingKeys = []; + + while (true) { + $keys = $client->scan($cursor, '*key*'); + if ($keys) { + $matchingKeys = array_merge($matchingKeys, $keys); + } + + $cursor = new ClusterScanCursor($cursor->getNextCursor()); + + if ($cursor->isFinished()) { + break; + } + } + // Returns matching keys such as ['my_key1', 'my_key2', 'not_my_key'] + ``` + + ```csharp using Valkey.Glide; @@ -224,29 +247,6 @@ For a detailed explanation of Cluster Scan, see our [article](/concepts/client-f // Returns matching keys such as ["my_key1", "my_key2", "not_my_key"] ``` - - - ```php - $client->mset(['my_key1' => 'value1', 'my_key2' => 'value2', 'not_my_key' => 'value3', 'something_else' => 'value4']); - - $cursor = new ClusterScanCursor(); - $matchingKeys = []; - - while (true) { - $keys = $client->scan($cursor, '*key*'); - if ($keys) { - $matchingKeys = array_merge($matchingKeys, $keys); - } - - $cursor = new ClusterScanCursor($cursor->getNextCursor()); - - if ($cursor->isFinished()) { - break; - } - } - // Returns matching keys such as ['my_key1', 'my_key2', 'not_my_key'] - ``` - ## Scan With The Count Option @@ -324,6 +324,16 @@ For a detailed explanation of Cluster Scan, see our [article](/concepts/client-f ``` + + ```php + $client->mset(['my_key1' => 'value1', 'my_key2' => 'value2', 'not_my_key' => 'value3', 'something_else' => 'value4']); + + $cursor = new ClusterScanCursor(); + $keys = $client->scan($cursor, null, 1); // 1 is the count parameter + // Returns around `count` keys: ['my_key1'] + ``` + + ```csharp using Valkey.Glide; @@ -350,16 +360,6 @@ For a detailed explanation of Cluster Scan, see our [article](/concepts/client-f // Returns around `count` keys per iteration ``` - - - ```php - $client->mset(['my_key1' => 'value1', 'my_key2' => 'value2', 'not_my_key' => 'value3', 'something_else' => 'value4']); - - $cursor = new ClusterScanCursor(); - $keys = $client->scan($cursor, null, 1); // 1 is the count parameter - // Returns around `count` keys: ['my_key1'] - ``` - ## Scan For a Specific Data Type. @@ -448,6 +448,30 @@ For a detailed explanation of Cluster Scan, see our [article](/concepts/client-f ``` + + ```php + $client->mset(['key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3']); + $client->sadd('this_is_a_set', 'value4'); + + $cursor = new ClusterScanCursor(); + $allKeys = []; + + while (true) { + $keys = $client->scan($cursor, null, 0, 'string'); + if ($keys) { + $allKeys = array_merge($allKeys, $keys); + } + + $cursor = new ClusterScanCursor($cursor->getNextCursor()); + + if ($cursor->isFinished()) { + break; + } + } + // Output: ['key1', 'key2', 'key3'] + ``` + + ```csharp using Valkey.Glide; @@ -474,28 +498,4 @@ For a detailed explanation of Cluster Scan, see our [article](/concepts/client-f // keys: ["key1", "key2", "key3"] ``` - - - ```php - $client->mset(['key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3']); - $client->sadd('this_is_a_set', 'value4'); - - $cursor = new ClusterScanCursor(); - $allKeys = []; - - while (true) { - $keys = $client->scan($cursor, null, 0, 'string'); - if ($keys) { - $allKeys = array_merge($allKeys, $keys); - } - - $cursor = new ClusterScanCursor($cursor->getNextCursor()); - - if ($cursor->isFinished()) { - break; - } - } - // Output: ['key1', 'key2', 'key3'] - ``` - diff --git a/src/content/docs/how-to/security/dynamic-authentication.mdx b/src/content/docs/how-to/security/dynamic-authentication.mdx index e8c7993f..be9aa650 100644 --- a/src/content/docs/how-to/security/dynamic-authentication.mdx +++ b/src/content/docs/how-to/security/dynamic-authentication.mdx @@ -17,6 +17,44 @@ The dynamic password update functionality does not rotate the password on the se Below are examples demonstrating how to utilize the dynamic password update feature in different programming languages using GLIDE. + + ```python + import asyncio + from glide import GlideClusterClientConfiguration, NodeAddress, GlideClusterClient + + async def main(): + # Define your server credentials + credentials = ServerCredentials( + username='your-username', + password='your-password-or-token' + ) + # Define the list of node addresses + addresses = [ + NodeAddress("my-instance.valkey.us-central1.gcp.cloud", 6379), + ] + # Create a configuration for the GlideClusterClient + config = GlideClusterClientConfiguration( + addresses=addresses, + credentials=credentials, + request_timeout=250, + client_name='my-client' + ) + + # Create the GlideClusterClient instance + client = await GlideClusterClient.create_client(config) + + # Update password dynamically + await client.update_connection_password('your-new-password') + # To perform immediate re-authentication, set the second parameter to true + await client.update_connection_password('your-new-password', True) + # Resetting password by passing None + await client.update_connection_password(None) # Note: This will clear the password from the connection configuration. + + + asyncio.run(main()) + ``` + + ```java import com.valkey.glide.GlideClusterClient; @@ -103,78 +141,12 @@ Below are examples demonstrating how to utilize the dynamic password update feat ``` - - ```python - import asyncio - from glide import GlideClusterClientConfiguration, NodeAddress, GlideClusterClient - - async def main(): - # Define your server credentials - credentials = ServerCredentials( - username='your-username', - password='your-password-or-token' - ) - # Define the list of node addresses - addresses = [ - NodeAddress("my-instance.valkey.us-central1.gcp.cloud", 6379), - ] - # Create a configuration for the GlideClusterClient - config = GlideClusterClientConfiguration( - addresses=addresses, - credentials=credentials, - request_timeout=250, - client_name='my-client' - ) - - # Create the GlideClusterClient instance - client = await GlideClusterClient.create_client(config) - - # Update password dynamically - await client.update_connection_password('your-new-password') - # To perform immediate re-authentication, set the second parameter to true - await client.update_connection_password('your-new-password', True) - # Resetting password by passing None - await client.update_connection_password(None) # Note: This will clear the password from the connection configuration. - - - asyncio.run(main()) - ``` - - ```go // TODO: Add Example ``` - - ```csharp - using Valkey.Glide; - using static Valkey.Glide.ConnectionConfiguration; - - var config = new ClusterClientConfigurationBuilder() - .WithAddress("localhost", 6379) - .WithAuthentication("your-username", "your-password-or-token") - .WithRequestTimeout(TimeSpan.FromMilliseconds(5000)) - .WithClientName("my-client") - .Build(); - - await using var client = await GlideClusterClient.CreateClient(config); - - // Update password with lazy re-authentication. - await client.UpdateConnectionPasswordAsync("your-new-password"); - - // Update password with immediate re-authentication. - await client.UpdateConnectionPasswordAsync("your-new-password", immediateAuth: true); - - // Clear password with lazy re-authentication. - await client.ClearConnectionPasswordAsync(); - - // Clear password with immediate re-authentication. - await client.ClearConnectionPasswordAsync(immediateAuth: true); - ``` - - ```php // Define your server credentials @@ -207,6 +179,34 @@ Below are examples demonstrating how to utilize the dynamic password update feat $client->clearConnectionPassword(); // Note: This will clear the password from the connection configuration. ``` + + + ```csharp + using Valkey.Glide; + using static Valkey.Glide.ConnectionConfiguration; + + var config = new ClusterClientConfigurationBuilder() + .WithAddress("localhost", 6379) + .WithAuthentication("your-username", "your-password-or-token") + .WithRequestTimeout(TimeSpan.FromMilliseconds(5000)) + .WithClientName("my-client") + .Build(); + + await using var client = await GlideClusterClient.CreateClient(config); + + // Update password with lazy re-authentication. + await client.UpdateConnectionPasswordAsync("your-new-password"); + + // Update password with immediate re-authentication. + await client.UpdateConnectionPasswordAsync("your-new-password", immediateAuth: true); + + // Clear password with lazy re-authentication. + await client.ClearConnectionPasswordAsync(); + + // Clear password with immediate re-authentication. + await client.ClearConnectionPasswordAsync(immediateAuth: true); + ``` + :::caution @@ -226,6 +226,14 @@ When dynamically switching to a new credential, ensure that it has enough permis In scenarios where a username is not required (e.g., IAM authentication), you can omit it or set it to `null`. + + ```python + credentials = ServerCredentials( + password='your-password-or-token' + ) + ``` + + ```java ServerCredentials credentials = ServerCredentials.builder() @@ -242,20 +250,20 @@ In scenarios where a username is not required (e.g., IAM authentication), you ca ``` - - ```python - credentials = ServerCredentials( - password='your-password-or-token' - ) - ``` - - ```go // TODO: Add Example ``` + + ```php + $credentials = [ + 'password' => 'your-password-or-token' + ]; + ``` + + ```csharp using static Valkey.Glide.ConnectionConfiguration; @@ -266,14 +274,6 @@ In scenarios where a username is not required (e.g., IAM authentication), you ca .Build(); ``` - - - ```php - $credentials = [ - 'password' => 'your-password-or-token' - ]; - ``` - ## Immediate Re-Auth diff --git a/src/content/docs/how-to/security/iam-integration.mdx b/src/content/docs/how-to/security/iam-integration.mdx index 480cd65d..861262e3 100644 --- a/src/content/docs/how-to/security/iam-integration.mdx +++ b/src/content/docs/how-to/security/iam-integration.mdx @@ -34,31 +34,6 @@ For GLIDE versions below 2.2, see the [guide](/how-to/security/iam-integration-u ## Examples - - ```csharp - using Valkey.Glide; - using static Valkey.Glide.ConnectionConfiguration; - - // Configure IAM authentication - // Automatically regenerates the token every 5 mins (default: 300 seconds) - var iamConfig = new IamAuthConfig( - clusterName: "clustername", - serviceType: ServiceType.ElastiCache, // or ServiceType.MemoryDB - region: "us-east-1" - // refreshIntervalSeconds: 100 // Optional, default is 300 seconds - ); - - var credentials = new ServerCredentials("username", iamConfig); - - var config = new ClusterClientConfigurationBuilder() - .WithAddress("endpoint.example.com", 6379) - .WithCredentials(credentials) - .Build(); - - await using var client = await GlideClusterClient.CreateClient(config); - ``` - - ```python from glide import ( @@ -214,4 +189,29 @@ For GLIDE versions below 2.2, see the [guide](/how-to/security/iam-integration-u ... ``` + + + ```csharp + using Valkey.Glide; + using static Valkey.Glide.ConnectionConfiguration; + + // Configure IAM authentication + // Automatically regenerates the token every 5 mins (default: 300 seconds) + var iamConfig = new IamAuthConfig( + clusterName: "clustername", + serviceType: ServiceType.ElastiCache, // or ServiceType.MemoryDB + region: "us-east-1" + // refreshIntervalSeconds: 100 // Optional, default is 300 seconds + ); + + var credentials = new ServerCredentials("username", iamConfig); + + var config = new ClusterClientConfigurationBuilder() + .WithAddress("endpoint.example.com", 6379) + .WithCredentials(credentials) + .Build(); + + await using var client = await GlideClusterClient.CreateClient(config); + ``` + diff --git a/src/content/docs/how-to/security/tls.mdx b/src/content/docs/how-to/security/tls.mdx index 76735d7f..45871e11 100644 --- a/src/content/docs/how-to/security/tls.mdx +++ b/src/content/docs/how-to/security/tls.mdx @@ -84,6 +84,19 @@ Enabling TLS is as simple as setting `use_tls=True` in your configuration. The c ``` + + ```php + $addresses = [ + ['host' => 'address.example.com', 'port' => 6379] + ]; + + $client = new ValkeyGlideCluster( + addresses: $addresses, + use_tls: true + ); + ``` + + ```csharp using Valkey.Glide; @@ -98,19 +111,6 @@ Enabling TLS is as simple as setting `use_tls=True` in your configuration. The c ``` - - ```php - $addresses = [ - ['host' => 'address.example.com', 'port' => 6379] - ]; - - $client = new ValkeyGlideCluster( - addresses: $addresses, - use_tls: true - ); - ``` - - ```ruby require "valkey" @@ -197,6 +197,19 @@ Enabling TLS is as simple as setting `use_tls=True` in your configuration. The c ``` + + ```php + $addresses = [ + ['host' => 'primary.example.com', 'port' => 6379], + ['host' => 'replica1.example.com', 'port' => 6379], + ['host' => 'replica2.example.com', 'port' => 6379] + ]; + + $client = new ValkeyGlide(); + $client->connect(addresses: $addresses, use_tls: true); + ``` + + ```csharp using Valkey.Glide; @@ -213,19 +226,6 @@ Enabling TLS is as simple as setting `use_tls=True` in your configuration. The c ``` - - ```php - $addresses = [ - ['host' => 'primary.example.com', 'port' => 6379], - ['host' => 'replica1.example.com', 'port' => 6379], - ['host' => 'replica2.example.com', 'port' => 6379] - ]; - - $client = new ValkeyGlide(); - $client->connect(addresses: $addresses, use_tls: true); - ``` - - ```ruby require "valkey" @@ -297,6 +297,16 @@ Insecure TLS mode bypasses certificate verification. This is useful when connect ::: + + ```php + $client = new ValkeyGlideCluster( + addresses: [['host' => 'address.example.com', 'port' => 6379]], + use_tls: true, + advanced_config: ['tls_config' => ['use_insecure_tls' => true]] + ); + ``` + + ```csharp using Valkey.Glide; @@ -311,16 +321,6 @@ Insecure TLS mode bypasses certificate verification. This is useful when connect await using var client = await GlideClusterClient.CreateClient(config); ``` - - - ```php - $client = new ValkeyGlideCluster( - addresses: [['host' => 'address.example.com', 'port' => 6379]], - use_tls: true, - advanced_config: ['tls_config' => ['use_insecure_tls' => true]] - ); - ``` - ### Custom Root Certificates @@ -456,6 +456,39 @@ You can provide custom root certificates for TLS connections. This is useful whe ::: + + #### Example - Connecting with Custom Root Certificate + + ```php + // Read certificate file + $rootCert = file_get_contents('/path/to/ca-cert.pem'); + + $client = new ValkeyGlide(); + $client->connect( + addresses: [['host' => 'address.example.com', 'port' => 6379]], + use_tls: true, + advanced_config: ['tls_config' => ['root_certs' => $rootCert]] + ); + ``` + + #### Example - Using Certificate as String + + ```php + $certData = <<<'CERT' + -----BEGIN CERTIFICATE----- + MIIDXTCCAkWgAwIBAgIJAKL0UG+mRKmzMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV + ... + -----END CERTIFICATE----- + CERT; + + $client = new ValkeyGlideCluster( + addresses: [['host' => 'address.example.com', 'port' => 6379]], + use_tls: true, + advanced_config: ['tls_config' => ['root_certs' => $certData]] + ); + ``` + + **Certificate Behavior:** @@ -519,39 +552,6 @@ You can provide custom root certificates for TLS connections. This is useful whe ``` - - #### Example - Connecting with Custom Root Certificate - - ```php - // Read certificate file - $rootCert = file_get_contents('/path/to/ca-cert.pem'); - - $client = new ValkeyGlide(); - $client->connect( - addresses: [['host' => 'address.example.com', 'port' => 6379]], - use_tls: true, - advanced_config: ['tls_config' => ['root_certs' => $rootCert]] - ); - ``` - - #### Example - Using Certificate as String - - ```php - $certData = <<<'CERT' - -----BEGIN CERTIFICATE----- - MIIDXTCCAkWgAwIBAgIJAKL0UG+mRKmzMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV - ... - -----END CERTIFICATE----- - CERT; - - $client = new ValkeyGlideCluster( - addresses: [['host' => 'address.example.com', 'port' => 6379]], - use_tls: true, - advanced_config: ['tls_config' => ['root_certs' => $certData]] - ); - ``` - - **Certificate Behavior:** @@ -714,15 +714,15 @@ MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7... ``` - + :::note[Coming soon] - Mutual TLS configuration documentation for C# is coming soon. + Mutual TLS configuration documentation for PHP is coming soon. ::: - + :::note[Coming soon] - Mutual TLS configuration documentation for PHP is coming soon. + Mutual TLS configuration documentation for C# is coming soon. ::: @@ -811,15 +811,15 @@ Use `WithMutualTLSFromFiles(certPath, keyPath)` (Go) or `useMutualTlsWithReload( ``` - + :::note[Coming soon] - Mutual TLS configuration documentation for C# is coming soon. + Mutual TLS configuration documentation for PHP is coming soon. ::: - + :::note[Coming soon] - Mutual TLS configuration documentation for PHP is coming soon. + Mutual TLS configuration documentation for C# is coming soon. ::: @@ -910,15 +910,15 @@ Pass a positive interval in seconds to override the core default. In Java, use ` ``` - + :::note[Coming soon] - Mutual TLS configuration documentation for C# is coming soon. + Mutual TLS configuration documentation for PHP is coming soon. ::: - + :::note[Coming soon] - Mutual TLS configuration documentation for PHP is coming soon. + Mutual TLS configuration documentation for C# is coming soon. ::: @@ -1015,15 +1015,15 @@ Helpers exist to read PEM bytes from disk and feed the in-memory mode. ``` - + :::note[Coming soon] - Mutual TLS configuration documentation for C# is coming soon. + Mutual TLS configuration documentation for PHP is coming soon. ::: - + :::note[Coming soon] - Mutual TLS configuration documentation for PHP is coming soon. + Mutual TLS configuration documentation for C# is coming soon. :::
diff --git a/src/content/docs/reference/connection-options.mdx b/src/content/docs/reference/connection-options.mdx index 980e0735..e9d83d2c 100644 --- a/src/content/docs/reference/connection-options.mdx +++ b/src/content/docs/reference/connection-options.mdx @@ -41,19 +41,6 @@ The following are the configuration references for each Glide clients: ```
- - ```typescript - import { GlideClientOptions } from "@valkey/valkey-glide"; - - const config: GlideClientOptions = { - addresses: [{ host: "localhost", port: 6379 }], - useTLS: false, - requestTimeout: 1000, - clientName: "node_app" - }; - ``` - - ```java import glide.api.models.configuration.GlideClientConfiguration; @@ -68,6 +55,19 @@ The following are the configuration references for each Glide clients: ``` + + ```typescript + import { GlideClientOptions } from "@valkey/valkey-glide"; + + const config: GlideClientOptions = { + addresses: [{ host: "localhost", port: 6379 }], + useTLS: false, + requestTimeout: 1000, + clientName: "node_app" + }; + ``` + + ```go import "github.com/valkey-io/valkey-glide/go/v2/config" diff --git a/src/content/docs/reference/scripting-reference.mdx b/src/content/docs/reference/scripting-reference.mdx index ce902f86..3e0a52ab 100644 --- a/src/content/docs/reference/scripting-reference.mdx +++ b/src/content/docs/reference/scripting-reference.mdx @@ -295,6 +295,20 @@ end ``` + + ```php + // Read script from file + $rateLimit = file_get_contents('rate_limit.lua'); + + // Usage + $result = $client->eval( + $rateLimit, + ['rate_limit:user:123', '10', '60'], // 10 requests per 60 seconds + 1 // num_keys + ); + ``` + + ```csharp using Valkey.Glide; @@ -316,20 +330,6 @@ end ``` - - ```php - // Read script from file - $rateLimit = file_get_contents('rate_limit.lua'); - - // Usage - $result = $client->eval( - $rateLimit, - ['rate_limit:user:123', '10', '60'], // 10 requests per 60 seconds - 1 // num_keys - ); - ``` - - ```ruby # Read script from file @@ -513,6 +513,34 @@ end ``` + + ```php + // Read scripts from files + $acquireLock = file_get_contents('acquire_lock.lua'); + $releaseLock = file_get_contents('release_lock.lua'); + + // Acquire lock + $lockAcquired = $client->eval( + $acquireLock, + ['lock:resource:123', 'unique_token', '30'], // 30 second expiration + 1 // num_keys + ); + + if ($lockAcquired) { + try { + // Do work while holding lock + } finally { + // Release lock + $client->eval( + $releaseLock, + ['lock:resource:123', 'unique_token'], + 1 // num_keys + ); + } + } + ``` + + ```csharp using Valkey.Glide; @@ -554,34 +582,6 @@ end ``` - - ```php - // Read scripts from files - $acquireLock = file_get_contents('acquire_lock.lua'); - $releaseLock = file_get_contents('release_lock.lua'); - - // Acquire lock - $lockAcquired = $client->eval( - $acquireLock, - ['lock:resource:123', 'unique_token', '30'], // 30 second expiration - 1 // num_keys - ); - - if ($lockAcquired) { - try { - // Do work while holding lock - } finally { - // Release lock - $client->eval( - $releaseLock, - ['lock:resource:123', 'unique_token'], - 1 // num_keys - ); - } - } - ``` - - ```ruby # Read scripts from files @@ -707,6 +707,20 @@ end ``` + + ```php + // Read script from file + $conditionalUpdate = file_get_contents('conditional_update.lua'); + + // Update only if current value matches expected + $updated = $client->eval( + $conditionalUpdate, + ['user:123:status', 'pending', 'active'], // Change from "pending" to "active" + 1 // num_keys + ); + ``` + + ```csharp using Valkey.Glide; @@ -728,20 +742,6 @@ end ``` - - ```php - // Read script from file - $conditionalUpdate = file_get_contents('conditional_update.lua'); - - // Update only if current value matches expected - $updated = $client->eval( - $conditionalUpdate, - ['user:123:status', 'pending', 'active'], // Change from "pending" to "active" - 1 // num_keys - ); - ``` - - ```ruby # Read script from file @@ -863,6 +863,28 @@ end ``` + + ```php + // Handle script execution errors + $script = "return redis.call('INCR', 'not_a_number')"; + + try { + $result = $client->eval($script, ['not_a_number'], 0); + } catch (ValkeyGlideException $e) { + $message = $e->getMessage(); + if (str_contains($message, 'WRONGTYPE') || str_contains($message, 'not an integer')) { + echo "Type error in script\n"; + } elseif (str_contains(strtolower($message), 'syntax error')) { + echo "Lua syntax error in script\n"; + } elseif (str_contains(strtolower($message), 'unknown command')) { + echo "Invalid Redis command in script\n"; + } else { + echo "Script error: $message\n"; + } + } + ``` + + ```csharp using Valkey.Glide; @@ -894,28 +916,6 @@ end ``` - - ```php - // Handle script execution errors - $script = "return redis.call('INCR', 'not_a_number')"; - - try { - $result = $client->eval($script, ['not_a_number'], 0); - } catch (ValkeyGlideException $e) { - $message = $e->getMessage(); - if (str_contains($message, 'WRONGTYPE') || str_contains($message, 'not an integer')) { - echo "Type error in script\n"; - } elseif (str_contains(strtolower($message), 'syntax error')) { - echo "Lua syntax error in script\n"; - } elseif (str_contains(strtolower($message), 'unknown command')) { - echo "Invalid Redis command in script\n"; - } else { - echo "Script error: $message\n"; - } - } - ``` - - ```ruby # Handle script execution errors @@ -1107,6 +1107,43 @@ When a client timeout occurs, the client stops waiting for a response, but the s ``` + + **Requirements:** PHP 8.1+ + + ```php + // Configure client timeout for long-running scripts + $client = new ValkeyGlide(); + $client->connect( + addresses: [['host' => 'localhost', 'port' => 6379]], + request_timeout: 30000 // 30 seconds for long scripts (default is 250ms) + ); + + // Handle long-running scripts + $longScript = <<<'LUA' + local start = redis.call('TIME')[1] + while redis.call('TIME')[1] - start < 25 do + redis.call('GET', 'dummy_key') -- Read-only operation + end + return 'Done' + LUA; + + try { + $result = $client->eval($longScript, [], 0); + echo "Script completed: $result\n"; + } catch (ValkeyGlideException $e) { + $message = $e->getMessage(); + if (str_contains(strtolower($message), 'timeout')) { + echo "Client timeout - script may still be running on server!\n"; + echo "Consider increasing request_timeout in client configuration\n"; + } elseif (str_contains($message, 'Script killed')) { + echo "Script was killed by server (only possible for read-only scripts)\n"; + } else { + echo "Script error: $message\n"; + } + } + ``` + + ```csharp using Valkey.Glide; @@ -1149,43 +1186,6 @@ When a client timeout occurs, the client stops waiting for a response, but the s ``` - - **Requirements:** PHP 8.1+ - - ```php - // Configure client timeout for long-running scripts - $client = new ValkeyGlide(); - $client->connect( - addresses: [['host' => 'localhost', 'port' => 6379]], - request_timeout: 30000 // 30 seconds for long scripts (default is 250ms) - ); - - // Handle long-running scripts - $longScript = <<<'LUA' - local start = redis.call('TIME')[1] - while redis.call('TIME')[1] - start < 25 do - redis.call('GET', 'dummy_key') -- Read-only operation - end - return 'Done' - LUA; - - try { - $result = $client->eval($longScript, [], 0); - echo "Script completed: $result\n"; - } catch (ValkeyGlideException $e) { - $message = $e->getMessage(); - if (str_contains(strtolower($message), 'timeout')) { - echo "Client timeout - script may still be running on server!\n"; - echo "Consider increasing request_timeout in client configuration\n"; - } elseif (str_contains($message, 'Script killed')) { - echo "Script was killed by server (only possible for read-only scripts)\n"; - } else { - echo "Script error: $message\n"; - } - } - ``` - - ```ruby require "valkey" @@ -1302,6 +1302,24 @@ When a client timeout occurs, the client stops waiting for a response, but the s ``` + + ```php + // Handle cluster routing errors + try { + $result = $clusterClient->eval( + $script, + ['key1', 'key2'], // Might be in different slots + 2 // num_keys + ); + } catch (ValkeyGlideException $e) { + if (str_contains($e->getMessage(), 'CROSSSLOT')) { + echo "Keys are in different slots\n"; + // Use hash tags or route explicitly + } + } + ``` + + ```csharp using Valkey.Glide; @@ -1326,24 +1344,6 @@ When a client timeout occurs, the client stops waiting for a response, but the s ``` - - ```php - // Handle cluster routing errors - try { - $result = $clusterClient->eval( - $script, - ['key1', 'key2'], // Might be in different slots - 2 // num_keys - ); - } catch (ValkeyGlideException $e) { - if (str_contains($e->getMessage(), 'CROSSSLOT')) { - echo "Keys are in different slots\n"; - // Use hash tags or route explicitly - } - } - ``` - - ```ruby # Handle cluster routing errors @@ -1484,6 +1484,31 @@ When a client timeout occurs, the client stops waiting for a response, but the s ``` + + ```php + // Good: Conditional update with multiple data structures + $conditionalUpdate = <<<'LUA' + local current = redis.call('GET', KEYS[1]) + local threshold = tonumber(ARGV[2]) + + if current and tonumber(current) >= threshold then + redis.call('SET', KEYS[1], ARGV[1]) + redis.call('LPUSH', KEYS[2], ARGV[1]) + redis.call('EXPIRE', KEYS[2], ARGV[3]) + return 1 + else + return 0 + end + LUA; + + $result = $client->eval( + $conditionalUpdate, + ['user:score', 'user:history', '100', '50', '86400'], // new score, threshold, expire in 1 day + 2 // num_keys + ); + ``` + + ```csharp using Valkey.Glide; @@ -1515,31 +1540,6 @@ When a client timeout occurs, the client stops waiting for a response, but the s ``` - - ```php - // Good: Conditional update with multiple data structures - $conditionalUpdate = <<<'LUA' - local current = redis.call('GET', KEYS[1]) - local threshold = tonumber(ARGV[2]) - - if current and tonumber(current) >= threshold then - redis.call('SET', KEYS[1], ARGV[1]) - redis.call('LPUSH', KEYS[2], ARGV[1]) - redis.call('EXPIRE', KEYS[2], ARGV[3]) - return 1 - else - return 0 - end - LUA; - - $result = $client->eval( - $conditionalUpdate, - ['user:score', 'user:history', '100', '50', '86400'], // new score, threshold, expire in 1 day - 2 // num_keys - ); - ``` - - ```ruby # Good: Conditional update with multiple data structures @@ -1635,6 +1635,20 @@ When a client timeout occurs, the client stops waiting for a response, but the s ``` + + ```php + // Good: Proper nil handling + $safeScript = <<<'LUA' + local val = redis.call('GET', KEYS[1]) + if val then + return val + else + return 'default_value' + end + LUA; + ``` + + ```csharp using Valkey.Glide; @@ -1651,20 +1665,6 @@ When a client timeout occurs, the client stops waiting for a response, but the s ``` - - ```php - // Good: Proper nil handling - $safeScript = <<<'LUA' - local val = redis.call('GET', KEYS[1]) - if val then - return val - else - return 'default_value' - end - LUA; - ``` - - ```ruby # Good: Proper nil handling @@ -1733,6 +1733,16 @@ When a client timeout occurs, the client stops waiting for a response, but the s ``` + + ```php + // Good: Return appropriate types + $typedScript = <<<'LUA' + local value = redis.call('GET', KEYS[1]) + return tonumber(value) or 0 -- Ensure numeric return, default to 0 if nil + LUA; + ``` + + ```csharp using Valkey.Glide; @@ -1745,16 +1755,6 @@ When a client timeout occurs, the client stops waiting for a response, but the s ``` - - ```php - // Good: Return appropriate types - $typedScript = <<<'LUA' - local value = redis.call('GET', KEYS[1]) - return tonumber(value) or 0 -- Ensure numeric return, default to 0 if nil - LUA; - ``` - - ```ruby # Good: Return appropriate types @@ -1857,6 +1857,24 @@ When a client timeout occurs, the client stops waiting for a response, but the s ``` + + ```php + // Good: Use hash tags for related keys + $clusterScript = <<<'LUA' + redis.call('SET', KEYS[1], ARGV[1]) + redis.call('SET', KEYS[2], ARGV[2]) + return 'OK' + LUA; + + // Execute with hash tags + $clusterClient->eval( + $clusterScript, + ['user:{123}:name', 'user:{123}:email', 'John', 'john@example.com'], + 2 // num_keys + ); + ``` + + ```csharp using Valkey.Glide; @@ -1881,24 +1899,6 @@ When a client timeout occurs, the client stops waiting for a response, but the s ``` - - ```php - // Good: Use hash tags for related keys - $clusterScript = <<<'LUA' - redis.call('SET', KEYS[1], ARGV[1]) - redis.call('SET', KEYS[2], ARGV[2]) - return 'OK' - LUA; - - // Execute with hash tags - $clusterClient->eval( - $clusterScript, - ['user:{123}:name', 'user:{123}:email', 'John', 'john@example.com'], - 2 // num_keys - ); - ``` - - ```ruby # Good: Use hash tags for related keys diff --git a/src/content/docs/troubleshooting.mdx b/src/content/docs/troubleshooting.mdx index 2e7f55b9..d16e76ce 100644 --- a/src/content/docs/troubleshooting.mdx +++ b/src/content/docs/troubleshooting.mdx @@ -52,14 +52,6 @@ To enable logging, you will need to set the logger from within your application. ``` - - ```csharp - using Valkey.Glide; - - Logger.SetLoggerConfig(Level.Debug); - ``` - - ```php use Glide\Logger; @@ -67,6 +59,14 @@ To enable logging, you will need to set the logger from within your application. Logger::setLoggerConfig(Logger::LEVEL_DEBUG); ``` + + + ```csharp + using Valkey.Glide; + + Logger.SetLoggerConfig(Level.Debug); + ``` +
:::tip @@ -165,6 +165,20 @@ To fix this, increase the **connection timeout** which is separate from the **re ``` + + ```php + $client = new ValkeyGlide(); + $client->connect( + addresses: [['host' => 'localhost', 'port' => 6379]], + request_timeout: 5000, // Request timeout in milliseconds + advanced_config: [ + 'connection_timeout' => 10000, // Connection timeout in milliseconds + 'socket_timeout' => 5000 // Socket read/write timeout in milliseconds + ] + ); + ``` + + ```csharp using Valkey.Glide; @@ -180,20 +194,6 @@ To fix this, increase the **connection timeout** which is separate from the **re ``` - - ```php - $client = new ValkeyGlide(); - $client->connect( - addresses: [['host' => 'localhost', 'port' => 6379]], - request_timeout: 5000, // Request timeout in milliseconds - advanced_config: [ - 'connection_timeout' => 10000, // Connection timeout in milliseconds - 'socket_timeout' => 5000 // Socket read/write timeout in milliseconds - ] - ); - ``` - - ```ruby require "valkey"