From f617d2495b0914bfde6d4495a7f9f95130ab0186 Mon Sep 17 00:00:00 2001 From: kiro-agent Date: Fri, 5 Jun 2026 16:32:54 +0000 Subject: [PATCH] docs: add address resolver how-to guide Signed-off-by: Alex Le --- .../how-to/connections/address-resolver.mdx | 401 ++++++++++++++++++ 1 file changed, 401 insertions(+) create mode 100644 src/content/docs/how-to/connections/address-resolver.mdx diff --git a/src/content/docs/how-to/connections/address-resolver.mdx b/src/content/docs/how-to/connections/address-resolver.mdx new file mode 100644 index 00000000..fa8053d4 --- /dev/null +++ b/src/content/docs/how-to/connections/address-resolver.mdx @@ -0,0 +1,401 @@ +--- +title: Address Resolver +description: Intercept host/port resolution at connection time using a custom callback +--- + +import { TabItem, Tabs } from '@astrojs/starlight/components'; + +The **address resolver** is a callback that intercepts host/port resolution when a GLIDE client connects to Valkey. It's useful for DNS overrides, service mesh scenarios, or testing with fake addresses. + +:::note +The address resolver is available in the Node.js, Python, Java, Go, C#, and PHP clients. In Python it is provided by the async client. +::: + +## Basic Usage + +Pass a callable to the address resolver parameter. The callable receives the original host and port, and returns the resolved address. + + + + ```python + from glide import GlideClient, GlideClientConfiguration, NodeAddress + + real_host = "127.0.0.1" + real_port = 6379 + + # The resolver receives the configured host/port and returns the address to use + def resolver(host: str, port: int) -> tuple[str, int]: + return (real_host, real_port) + + config = GlideClientConfiguration( + addresses=[NodeAddress("host.unreachable", 9999)], + address_resolver=resolver, + ) + client = await GlideClient.create(config) + + # Client is now connected to 127.0.0.1:6379 + await client.set("key", "value") + ``` + + + + ```java + import glide.api.GlideClient; + import glide.api.models.configuration.AddressResolver; + import glide.api.models.configuration.GlideClientConfiguration; + import glide.api.models.configuration.NodeAddress; + import glide.api.models.configuration.ResolvedAddress; + + String realHost = "127.0.0.1"; + int realPort = 6379; + + // The resolver receives the configured host/port and returns the address to use + AddressResolver resolver = (host, port) -> new ResolvedAddress(realHost, realPort); + + GlideClient client = + GlideClient.createClient( + GlideClientConfiguration.builder() + .address(NodeAddress.builder().host("host.unreachable").port(9999).build()) + .addressResolver(resolver) + .build()) + .get(); + + // Client is now connected to 127.0.0.1:6379 + client.set("key", "value").get(); + ``` + + + + ```typescript + import { GlideClient } from "@valkey/valkey-glide"; + + const realHost = "127.0.0.1"; + const realPort = 6379; + + // The resolver receives the configured host/port and returns the address to use + const resolver = (host: string, port: number): [string, number] => { + return [realHost, realPort]; + }; + + const client = await GlideClient.createClient({ + addresses: [{ host: "host.unreachable", port: 9999 }], + addressResolver: resolver, + }); + + // Client is now connected to 127.0.0.1:6379 + await client.set("key", "value"); + ``` + + + + ```go + import ( + "context" + + glide "github.com/valkey-io/valkey-glide/go/v2" + "github.com/valkey-io/valkey-glide/go/v2/config" + ) + + realHost := "127.0.0.1" + realPort := 6379 + + // The resolver receives the configured host/port and returns the address to use + resolver := func(host string, port int) (string, int) { + return realHost, realPort + } + + clientConfig := config.NewClientConfiguration(). + WithAddress(&config.NodeAddress{Host: "host.unreachable", Port: 9999}). + WithAddressResolver(resolver) + + client, err := glide.NewClient(clientConfig) + if err != nil { + // Handle error + } + defer client.Close() + + // Client is now connected to 127.0.0.1:6379 + client.Set(context.Background(), "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"); + ``` + + + + ```php + $realHost = '127.0.0.1'; + $realPort = 6379; + + $resolver = function (string $host, int $port) use ($realHost, $realPort): array { + return ['host' => $realHost, 'port' => $realPort]; + }; + + $client = new ValkeyGlide(); + $client->connect( + addresses: [['host' => 'host.unreachable', 'port' => 9999]], + address_resolver: $resolver, + ); + + // Client is now connected to 127.0.0.1:6379 + $client->set('key', 'value'); + ``` + + + +## Fallback Behavior + +If the resolver throws an exception or returns invalid data, the client falls back to the original address. + + + + ```python + from glide import GlideClient, GlideClientConfiguration, NodeAddress + + # This resolver throws — the client will use the original address. + # Returning None or an invalid value triggers the same fallback. + def resolver(host: str, port: int) -> tuple[str, int]: + raise RuntimeError("Resolution failed") + + client = await GlideClient.create( + GlideClientConfiguration( + addresses=[NodeAddress("localhost", 6379)], + address_resolver=resolver, + ) + ) + + # Still connects to localhost:6379 + ``` + + + + ```java + import glide.api.GlideClient; + import glide.api.models.configuration.AddressResolver; + import glide.api.models.configuration.GlideClientConfiguration; + import glide.api.models.configuration.NodeAddress; + + // This resolver throws — the client will use the original address + AddressResolver resolver = + (host, port) -> { + throw new RuntimeException("Resolution failed"); + }; + + GlideClient client = + GlideClient.createClient( + GlideClientConfiguration.builder() + .address(NodeAddress.builder().host("localhost").port(6379).build()) + .addressResolver(resolver) + .build()) + .get(); + + // Still connects to localhost:6379 + ``` + + + + ```typescript + import { GlideClient } from "@valkey/valkey-glide"; + + // This resolver throws — the client will use the original address + const client = await GlideClient.createClient({ + addresses: [{ host: "localhost", port: 6379 }], + addressResolver: (host: string, port: number): [string, number] => { + throw new Error("Resolution failed"); + }, + }); + + // Still connects to localhost:6379 + ``` + + + + ```go + import ( + glide "github.com/valkey-io/valkey-glide/go/v2" + "github.com/valkey-io/valkey-glide/go/v2/config" + ) + + // This resolver panics — the client will use the original address. + // Returning an empty host or an out-of-range port triggers the same fallback. + resolver := func(host string, port int) (string, int) { + panic("Resolution failed") + } + + clientConfig := config.NewClientConfiguration(). + WithAddress(&config.NodeAddress{Host: "localhost", Port: 6379}). + WithAddressResolver(resolver) + + client, err := glide.NewClient(clientConfig) + if err != nil { + // Handle error + } + defer client.Close() + + // Still connects to localhost:6379 + ``` + + + + ```csharp + using Valkey.Glide; + + // This resolver throws — the client will use the original address + var config = new ConnectionConfiguration.StandaloneClientConfigurationBuilder() + .WithAddress("localhost", 6379) + .WithAddressResolver((host, port) => throw new InvalidOperationException("Resolution failed!")) + .Build(); + + await using var client = await GlideClient.CreateClient(config); + + // 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 + +The cluster client supports the same resolver signature. + + + + ```python + from glide import GlideClusterClient, GlideClusterClientConfiguration, NodeAddress + + def resolver(host: str, port: int) -> tuple[str, int]: + return (get_actual_host(host), port) + + client = await GlideClusterClient.create( + GlideClusterClientConfiguration( + addresses=[NodeAddress("internal-dns.service", 7000)], + address_resolver=resolver, + ) + ) + ``` + + + + ```java + import glide.api.GlideClusterClient; + import glide.api.models.configuration.AddressResolver; + import glide.api.models.configuration.GlideClusterClientConfiguration; + import glide.api.models.configuration.NodeAddress; + import glide.api.models.configuration.ResolvedAddress; + + AddressResolver resolver = (host, port) -> new ResolvedAddress(getActualHost(host), port); + + GlideClusterClient client = + GlideClusterClient.createClient( + GlideClusterClientConfiguration.builder() + .address(NodeAddress.builder().host("internal-dns.service").port(7000).build()) + .addressResolver(resolver) + .build()) + .get(); + ``` + + + + ```typescript + import { GlideClusterClient } from "@valkey/valkey-glide"; + + const client = await GlideClusterClient.createClient({ + addresses: [{ host: "internal-dns.service", port: 7000 }], + addressResolver: (host: string, port: number): [string, number] => { + return [getActualHost(host), port]; + }, + }); + ``` + + + + ```go + import ( + glide "github.com/valkey-io/valkey-glide/go/v2" + "github.com/valkey-io/valkey-glide/go/v2/config" + ) + + resolver := func(host string, port int) (string, int) { + return getActualHost(host), port + } + + clientConfig := config.NewClusterClientConfiguration(). + WithAddress(&config.NodeAddress{Host: "internal-dns.service", Port: 7000}). + WithAddressResolver(resolver) + + client, err := glide.NewClusterClient(clientConfig) + if err != nil { + // Handle error + } + defer client.Close() + ``` + + + + ```csharp + using Valkey.Glide; + + // Your custom host resolution logic + string GetActualHost(string host) => host; + + var config = new ConnectionConfiguration.ClusterClientConfigurationBuilder() + .WithAddress("internal-dns.service", 7000) + .WithAddressResolver((host, port) => (GetActualHost(host), port)) + .Build(); + + 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 + +* Each client instance maintains its own resolver — multiple clients can use different resolvers without interfering with each other. +* The resolver is called synchronously during connection establishment and during cluster topology refreshes, so it must be fast and avoid blocking operations. +* **Node.js / Python / Java**: If the resolver throws, the client falls back to the original address. +* **Python**: Returning `None` or an otherwise invalid value triggers fallback to the original address. Address resolver support is provided by the async client. +* **Java**: The resolver must be thread-safe. +* **Go**: The resolver may be called from multiple goroutines and must be safe for concurrent use. Returning an empty host, a port outside the range 1–65535, or panicking triggers fallback to the original address. +* **PHP**: Resolver state is cleared on PHP module shutdown (MSHUTDOWN) and request shutdown (RSHUTDOWN). +* **C#**: The resolver delegate must be thread-safe. The maximum resolved hostname length is 1024 bytes — if exceeded, the client falls back to the original address. The delegate's GC pin is handled internally by the client.