Your Valkey cluster spans three availability zones, your application reads from replicas to take load off the primaries, and the data transfer line on the cloud bill keeps growing. The client picks replicas at random or round robin, with no idea which zone each one is in, so many of those reads leave the zone they started in. Valkey GLIDE 2.5.3, the official Valkey client, can keep those reads in the caller's zone with its AZ-affinity read strategies. Getting there takes a server setting, a client setting, and a client migration. Skip the client setting and the Python client refuses to start with client_az must be set when read_from is set to AZ_AFFINITY. Skip the server setting and reads still cross zones. This guide covers what GLIDE is, how AZ-affinity routing works, how to configure it on both sides, what GLIDE does not support yet, and how to move a service from redis-py, Jedis, ioredis, or go-redis without a flag day.
- Valkey GLIDE is a Rust core with bindings for Python, Java, Node.js, and Go in the main repository, plus C#, PHP, and Ruby clients in separate repositories. As of November 2026 the latest GitHub release is 2.5.3 (25 September 2026); 2.6.0 is at release candidate 5.
- AZ affinity needs both halves:
availability-zoneset on every Valkey node (8.0 and later) andread_fromplusclient_azset on the client. GLIDE reads each node's zone fromINFOwhen it connects. - Pick the strategy by topology.
AZ_AFFINITYuses only replicas, so in a zone whose only node is the shard primary, reads leave the zone.AZ_AFFINITY_REPLICAS_AND_PRIMARYuses that local primary instead. - GLIDE has no Sentinel support (issue #2761) and does not handle
CLIENT CAPA redirect(issue #2368). Stay on your current client for Sentinel deployments. - Migrate one service at a time behind a flag. Keep blocking commands,
WATCH, and large values on separate GLIDE clients, and review timeouts: GLIDE's default request timeout is 250 ms.
Scope and review date: November 2026. This post covers Valkey GLIDE 2.5.3 (with notes on the 2.6.0 release candidates) against self-managed Valkey 8.0 to 9.1 and Amazon ElastiCache for Valkey. Configuration names, defaults, and commands were checked against the GLIDE source at the v2.5.3 tag, the GLIDE documentation, and the Valkey documentation on 10 November 2026. They were not run in a lab, so test them on staging first.
What is Valkey GLIDE and which languages does it support?
GLIDE (General Language Independent Driver for the Enterprise) is a client library owned by the Valkey project. One core, written in Rust, does connection management, cluster topology tracking, command routing, and retries. Each language binding is a thin layer over that core, so a Python service and a Java service on the same cluster follow the same routing and reconnection rules. The GLIDE README lists Valkey 7.2 to 9.1 and Redis OSS 6.2 to 7.2 as supported engines.
| Language | Package | Latest stable, as of 10 November 2026 |
|---|---|---|
| Python (async and sync) | valkey-glide, valkey-glide-sync | 2.5.3 (see the note below) |
| Java | io.valkey:valkey-glide | 2.5.3 |
| Node.js | @valkey/valkey-glide | 2.5.3 |
| Go | github.com/valkey-io/valkey-glide/go/v2 | 2.5.3 |
| C# | Valkey.Glide (separate repository) | 1.1.0 |
| PHP | valkey-glide-php (separate repository) | 1.1.2; 1.2.0-rc2 in testing |
| Ruby | valkey-glide-rb (separate repository) | 1.0.0 |
| Rust | valkey-glide crate | 0.9.0 preview |
# Which GLIDE version does each service resolve today?
pip show valkey-glide | grep -i '^version'
npm ls @valkey/valkey-glide
go list -m github.com/valkey-io/valkey-glide/go/v2Interpretation: pin an exact version rather than a range. As of 10 November 2026, PyPI lists a valkey-glide 2.6.0 upload dated 6 November, while GitHub has no 2.6.0 release, the changelog still reads "Pending 2.6", and Maven, npm, and the Go proxy are at 2.6.0-rc5. Treat 2.5.3 as the current release until the GitHub releases page shows 2.6.0.
Check the platform before you plan anything else. The 2.5.3 READMEs for Python, Java, and Go say Alpine Linux (musl) is not supported, while the Node.js client publishes musl builds. A service built on an Alpine image needs a glibc base image before it can run GLIDE.
Why do teams move from redis-py, Jedis, ioredis, or go-redis to GLIDE?
Three reasons come up most. First, cluster handling lives in the core. It follows MOVED and ASK redirects, refreshes its slot map after a MOVED, and runs a periodic topology check every 60 seconds by default (DEFAULT_PERIODIC_TOPOLOGY_CHECKS_INTERVAL in the 2.5.3 core). It also detects a circular MOVED, where a node redirects to itself, and refreshes the connection instead of retrying forever. Second, reconnection is the same in every language. The client retries forever with exponential backoff and jitter, and since 2.5.1 the core holds requests that arrive while a cluster connection is being rebuilt instead of failing them at once. Third, read routing can take availability zones into account, which the replica-read options in redis-py, ioredis, and go-redis do not.
Legacy client and GLIDE behavior side by side
Switch tabs to see what changes for your service when the client changes
Legacy client
| Behavior | redis-py, Jedis, ioredis, go-redis |
|---|---|
| Connections | A pool you size per process; go-redis PoolSize applies per cluster node |
| Replica reads | redis-py load_balancing_strategy, ioredis scaleReads, go-redis ReadOnly with RouteRandomly or RouteByLatency |
| Zone awareness | No AZ option in the redis-py, ioredis, or go-redis replica settings; ioredis scaleReads accepts a custom function |
| Pipelines and transactions | pipeline() and multi() objects, one style per library |
Blocking commands and WATCH | Take a connection from the pool |
| Request timeout | Library default; read yours before you switch |
| Sentinel | Supported by all four |
Valkey GLIDE 2.5.3
| Behavior | Valkey GLIDE 2.5.3 |
|---|---|
| Connections | One multiplexed connection per node per client; one client per process |
| Replica reads | read_from: PREFER_REPLICA, ALL_NODES, or an AZ strategy |
| Zone awareness | client_az with AZ_AFFINITY, AZ_AFFINITY_REPLICAS_AND_PRIMARY, or AZ_AFFINITY_ALL_NODES (Java and Python) |
| Pipelines and transactions | Batch or ClusterBatch, atomic or not, run with exec(batch, raise_on_error) |
Blocking commands and WATCH | Use a separate client; WATCH state is shared by every caller of one client |
| Request timeout | 250 ms by default, including retries and reconnects |
| Sentinel | Not supported; issue #2761 is open |
Biggest change: the connection model. Code that assumed a private pooled connection, such as BLPOP loops or WATCH blocks, needs its own GLIDE client.
The GLIDE connection management guide explains the trade-off: requests on one client are processed in order on one connection per node, so a large value or a blocking command delays everything behind it. In cluster mode every client also holds a connection to every node, so opening many clients does not scale.
How does AZ-affinity read routing work?
Each GLIDE connection runs INFO during setup when an AZ strategy is configured, and records the node's availability_zone field (update_az_from_info in the 2.5.3 core). Reads then go to nodes whose zone matches the client's client_az. Writes always go to the shard primary, wherever it runs. The read strategy guide defines the three AZ strategies:
| Strategy | Read order within the key's shard | Availability |
|---|---|---|
AZ_AFFINITY | Local replicas, then other replicas, then the primary only if no replica is available | All bindings |
AZ_AFFINITY_REPLICAS_AND_PRIMARY | Local replicas, then the local primary, then other replicas, then the primary | All bindings |
AZ_AFFINITY_ALL_NODES | Local primary and replicas in equal rotation, then all nodes | Java and Python 2.5.3; Node.js and Go planned for 2.6.0 |
AZ-affinity read routing with AZ_AFFINITY
Follow the numbers: zones are learned at connect time, reads stay local, writes do not
Topology decides the result: with one primary and two replicas per shard spread over three zones, one zone holds only the primary. AZ_AFFINITY sends that zone's reads to remote replicas; AZ_AFFINITY_REPLICAS_AND_PRIMARY reads from the local primary instead.
Replica reads can be stale: the GLIDE documentation says a replica read may not observe the write just before it. Keep read-your-own-write paths on PRIMARY, with a second client or explicit routing.
How do you set availability-zone on Valkey nodes?
The availability-zone setting arrived in Valkey 8.0 (PR #700). It defaults to the empty string and can be changed at runtime. In 8.0 and later it appears in INFO server as availability_zone. Valkey 8.1 added it to the HELLO reply, and 9.1 added it to each node in CLUSTER SHARDS and CLUSTER SLOTS, gossiped between cluster nodes. Both replies omit the field when the setting is empty.
# valkey.conf on every node in zone use1-az1
availability-zone "use1-az1"Interpretation: the value is an opaque string that Valkey never checks, so a typo silently sends reads across zones.
# Set it at runtime on one node, then persist it to the config file
valkey-cli -h vk-a1 CONFIG SET availability-zone use1-az1
valkey-cli -h vk-a1 CONFIG REWRITEInterpretation: CONFIG SET lasts only until the next restart unless you also run CONFIG REWRITE or add the line to valkey.conf. Repeat this on every primary and replica. GLIDE reads the zone only when it opens a connection, so a client that connected before the change keeps routing as before until it reconnects. Restart the application pods after the server change.
The client setting and the server fields it must match
Illustrative output for a Valkey 9.1 cluster; hostnames and zone IDs are examples
Exact match: the client string must equal the server string character for character. The read strategy guide warns that a wrong non-empty value matches no node and sends reads across zones.
Choose the naming once. On AWS, zone names such as us-east-1a map to different physical zones in different accounts, while zone IDs such as use1-az1 are the same in every account (AWS: Availability Zone IDs). If the application and the cache live in different accounts, use zone IDs on both sides.
On Amazon ElastiCache for Valkey, the GLIDE README says AZ affinity works from ElastiCache for Valkey 7.2, and the Valkey AZ-affinity post says managed services may set the zone for you. You may not be able to run CONFIG SET there, so run INFO server against a replica to see which string it reports, and set client_az to match. ElastiCache Serverless is different: its read replica guide says the read-optimized endpoint routes reads to a replica in the client's zone when possible, and shows GLIDE with ReadFrom.PREFER_REPLICA, not an AZ strategy.
Valkey 9.1 also changed the CLUSTER SHARDS reply shape, and older strict parsers have broken on it (Valkey issue #3909, for go-redis). If any service still runs an older client while you set zones on 9.1, read the changes in Valkey 8 to 9.2 that break client libraries first.
How do you configure the AZ read strategy in each language?
All AZ strategies need the application's own zone: client_az in Python, clientAZ in Java, clientAz in Node.js, and WithClientAZ in Go. Inject it at deploy time; never hard-code it. Missing values fail at client creation: Python, Node.js, and Go reject an empty value, and Java does so from 2.5.3. Before 2.5.3, a Java client with no clientAZ logged a warning and fell back to PREFER_REPLICA, so check Java services when you upgrade.
import os
from glide import GlideClusterClient, GlideClusterClientConfiguration, NodeAddress, ReadFrom
config = GlideClusterClientConfiguration(
[NodeAddress("vk-a1.internal", 6379)],
read_from=ReadFrom.AZ_AFFINITY_REPLICAS_AND_PRIMARY,
client_az=os.environ["APP_AZ"],
use_tls=True,
)
client = await GlideClusterClient.create(config)Interpretation: one seed address is enough; the client discovers the rest of the cluster.
GlideClusterClientConfiguration config = GlideClusterClientConfiguration.builder()
.address(NodeAddress.builder().host("vk-a1.internal").port(6379).build())
.useTLS(true)
.readFrom(ReadFrom.AZ_AFFINITY_REPLICAS_AND_PRIMARY)
.clientAZ(System.getenv("APP_AZ"))
.build();
GlideClusterClient client = GlideClusterClient.createClient(config).get();Interpretation: in Java 2.5.3, a missing or blank clientAZ throws ConfigurationError from createClient, and surrounding spaces are trimmed.
import { GlideClusterClient } from "@valkey/valkey-glide";
const client = await GlideClusterClient.createClient({
addresses: [{ host: "vk-a1.internal", port: 6379 }],
useTLS: true,
readFrom: "AZAffinityReplicasAndPrimary",
clientAz: process.env.APP_AZ,
});Interpretation: Node.js 2.5.3 accepts "primary", "preferReplica", "AZAffinity", "AZAffinityReplicasAndPrimary", and "allNodes". "AZAffinityAllNodes" is in the 2.6.0 release candidates.
cfg := config.NewClusterClientConfiguration().
WithAddress(&config.NodeAddress{Host: "vk-a1.internal", Port: 6379}).
WithUseTLS(true).
WithReadFrom(config.AzAffinityReplicaAndPrimary).
WithClientAZ(os.Getenv("APP_AZ"))
client, err := glide.NewClusterClient(cfg)Interpretation: the Go constant is AzAffinityReplicaAndPrimary (singular "Replica") in the 2.5.3 config package. Some go-redis migration pages show struct fields such as ReadFrom: config.ReadFromAZAffinity that do not exist in 2.5.3; use the builder methods shown here.
How much cross-AZ traffic does AZ affinity remove?
Only the read traffic that used to leave the zone. Writes still travel to the primary's zone, and replication between zones is unchanged. The result depends on your read share, value sizes, and shard layout, so measure it.
On price, the ElastiCache pricing page, read on 10 November 2026, says node-based clusters have no data transfer charge for traffic in or out of the ElastiCache node across zones in the same Region. You pay for transfer in or out of your application at standard AWS rates, which the page lists as $0.01 per GB within the same Region. Check the page for your Region before you model costs.
# Bytes each client connection has received from this node (Valkey 8.0 and later)
valkey-cli -h vk-b2 CLIENT LIST \
| grep -oE 'addr=[^ ]+|tot-net-out=[^ ]+' | paste - -Interpretation: tot-net-out is the total bytes the node has sent to that connection since it opened. Map each client address to its subnet's zone: bytes sent to other zones are what AZ affinity can move. Repeat after rollout.
A customer guest post on the AWS Database Blog (13 May 2026) reports a 95% cut in inter-AZ data transfer cost and a 49% latency improvement after moving a Java service to GLIDE with AZ_AFFINITY_REPLICAS_AND_PRIMARY. That workload was over 99% reads. Treat it as one team's result, not a forecast for yours.
What does GLIDE not support yet?
Check each service against this list, checked on 10 November 2026, before you schedule it.
- Sentinel. GLIDE connects to standalone servers and clusters only. Issue #2761 is open; on 19 October 2026 the maintainers said they had started a short investigation. A Sentinel deployment either keeps its current client or moves to cluster mode first (see Valkey Cluster vs Sentinel).
CLIENT CAPA redirect. GLIDE does not use the Valkey 8.0 redirect capability for standalone switchovers. Issue #2368 was reopened in July 2026 and is still open.- Newer commands as typed methods.
DELIFEQ(Valkey 9.0) andHGETDEL(Valkey 9.1) are listed under "Pending 2.6" in the GLIDE changelog. Until then, send them withcustom_command; for a command it has no routing entry for, the 2.5.3 core routes by the first argument as the key. AZ_AFFINITY_ALL_NODESin Node.js and Go. It is in the 2.6.0 release candidates, not in 2.5.3.- Alpine images for Python, Java, and Go, as noted above.
- The Jedis compatibility layer is a partial drop-in. Its supported features page lists transactions, pipelining, Pub/Sub, and Lua scripting as unsupported, and offers set and sorted set commands only through
sendCommand(). - Ruby Pub/Sub. The Ruby 1.0.0 release notes remove the Pub/Sub API, along with
MONITORand server-module commands.
What changes in your code when you migrate?
The GLIDE migration guides map commands one by one. Four differences cause most of the work.
One client per process, created once
Delete pool sizing and connection checkout. Create one GLIDE client at startup and share it, plus dedicated clients for blocking commands, heavy Pub/Sub, large values, and WATCH. redis-py's db argument becomes a select call after connecting, and socket_timeout becomes request_timeout in milliseconds.
Options move into the command
Dedicated helpers like SETEX and SETNX become options on set, and multi-key commands take a list.
# redis-py
r.setex("session:42", 1800, payload)
r.delete("k1", "k2")
# GLIDE (Python, async)
from glide import ExpirySet, ExpiryType
await client.set("session:42", payload, expiry=ExpirySet(ExpiryType.SEC, 1800))
await client.delete(["k1", "k2"])Interpretation: GLIDE returns bytes where redis-py with decode_responses=True returned str. In Node.js, the same pattern applies: set takes an expiry options object and del takes an array.
// go-redis: a missing key returns the redis.Nil error
// val, err := rdb.Get(ctx, "session:42").Result()
// GLIDE (Go): a missing key returns a nil Result
res, err := client.Get(ctx, "session:42")
if err != nil {
return err
}
if !res.IsNil() {
use(res.Value())
}
_, err = client.SetWithOptions(ctx, "session:42", payload,
*options.NewSetOptions().SetExpiry(options.NewExpiryIn(30 * time.Minute)))Interpretation: every errors.Is(err, redis.Nil) branch has to become an IsNil() check. Search for redis.Nil before you start.
Pipelines and transactions become batches
GLIDE 2.0 replaced its transaction classes with Batch (standalone) and ClusterBatch (cluster). The first argument says whether the batch is atomic. An atomic batch runs as MULTI/EXEC and, in cluster mode, all its keys must hash to one slot. A non-atomic batch is a pipeline that may span slots and nodes.
from glide import ClusterBatch, ClusterBatchOptions, BatchRetryStrategy
pipe = ClusterBatch(False) # pipeline, may span slots
pipe.set("user:1:name", "a")
pipe.incr("stats:logins")
results = await client.exec(
pipe,
raise_on_error=False,
options=ClusterBatchOptions(retry_strategy=BatchRetryStrategy(retry_server_error=True)),
)Interpretation: with raise_on_error=False, failed commands come back as error objects in the result list, in order. The batch guide warns that retry_connection_error can run commands twice, so leave it off for non-idempotent work. Retry strategies apply only to non-atomic cluster batches.
Some migration pages on glide.valkey.io still show the pre-2.0 Transaction class. Use Batch or ClusterBatch with 2.5.3.
Timeouts and retries
GLIDE's request timeout defaults to 250 ms and covers sending, waiting, and any reconnects or retries inside it. The connection timeout defaults to 2,000 ms (timeouts and reconnect strategy). If a service ran with a longer timeout because of slow SCAN loops or large values, set request_timeout explicitly, or the first deploy will show new timeout errors.
Java teams that cannot change call sites yet can swap the Jedis dependency for io.valkey:valkey-glide-jedis-compatibility. Given the gaps listed above, treat it as a bridge to the native API, not the end state.
How do you roll GLIDE out safely?
Move one service at a time, and keep the old client a configuration flag away until GLIDE has run through a failover in production.
Migration rollout: legacy client to Valkey GLIDE
Eight steps; none needs a production outage, and step 5 forces a failover on staging only
-
Inventory clients and versions
List every service, its client library, and its HA mode. Sentinel services stay on their current client for now.
pip show redis | grep -i version npm ls ioredis go list -m all | grep go-redis -
Set availability-zone on every node
Persist it in valkey.conf, then confirm on each primary and replica.
valkey-cli -h vk-a1 INFO server \ | grep availability_zone -
Choose a read strategy per call path
Read-your-own-write paths stay on PRIMARY. Pick the AZ strategy from your shard layout, not by default.
-
Port one service behind a flag
Wrap the cache layer so a config flag picks the client. Give blocking commands and WATCH their own GLIDE clients.
-
Rehearse a failover on staging
Promote a replica while load runs and watch timeouts, errors, and how fast reads settle on the new topology.
valkey-cli -h vk-b2-replica \ CLUSTER FAILOVER -
Canary in one zone
Turn the flag on for the pods in one zone. Compare error rate, p99 latency, and tot-net-out per client with the other zones.
-
Expand zone by zone
Track progress by library name: GLIDE reports itself through CLIENT SETINFO, for example GlidePy.
valkey-cli -h vk-a1 CLIENT LIST \ | grep -o 'lib-name=[^ ]*' | sort | uniq -c -
Remove the legacy client
Drop the old dependency and the flag after one release that included a real failover.
Rollback point: until step 8, flipping the flag returns a service to its old client. The server setting can stay, but on Valkey 9.1 it adds a field to CLUSTER SHARDS, so older strict parsers must be patched first.
Step 7 works because the 2.5.3 core sends CLIENT SETINFO LIB-NAME on every connection (Python reports GlidePy or GlidePySync, and client_info_tag can append a service name). In cluster mode, run the count on every node.
Frequently asked questions
What Valkey version do I need for GLIDE AZ affinity?
Valkey 8.0 or later for self-managed servers, because that release added the availability-zone setting, which GLIDE reads from INFO at connection time. The GLIDE README says Amazon ElastiCache for Valkey supports it from 7.2. You must set the zone on every node and set client_az on the client, or reads still cross zones.
Does Valkey GLIDE support Redis Sentinel or Valkey Sentinel?
No. As of November 2026, GLIDE 2.5.3 connects to standalone servers and clusters only. The Sentinel feature request, GitHub issue 2761, is open, and in October 2026 the maintainers said they had started investigating it. Keep Sentinel deployments on a client that supports Sentinel, or move them to cluster mode before adopting GLIDE.
What is the difference between AZ_AFFINITY and AZ_AFFINITY_REPLICAS_AND_PRIMARY?
AZ_AFFINITY reads only from replicas, preferring those in the client's zone, and uses the primary only when no replica is available. AZ_AFFINITY_REPLICAS_AND_PRIMARY also uses the primary when it is in the client's zone and no local replica is available. Choose the second when some zones hold only a shard's primary.
Do I need a connection pool with Valkey GLIDE?
Usually not. GLIDE multiplexes all requests from a client over one connection per node, and its documentation recommends one client per process for most workloads. Create separate clients for blocking commands such as BLPOP, for WATCH and transactions, for heavy Pub/Sub, and for very large values, because each of these can hold up other requests.
Is Valkey GLIDE 2.6 released?
Not on GitHub as of 10 November 2026. The latest GitHub release is 2.5.3 from 25 September 2026, and Maven, npm, and the Go proxy carry 2.6.0-rc5. PyPI shows a 2.6.0 upload dated 6 November 2026, so pin exact versions and wait for the GitHub release notes before you rely on 2.6 features.
Key takeaways
- GLIDE puts routing, redirects, reconnection, and AZ-aware reads in one Rust core. Pin 2.5.3 until 2.6.0 appears on GitHub.
- AZ affinity needs
availability-zoneon every node, a matchingclient_azon every client, and a client reconnect after the server change. - Match the strategy to your shard layout.
AZ_AFFINITYleaves the zone wherever the only local node is a primary. - Only reads move. Measure cross-zone bytes per client with
tot-net-outbefore and after, instead of trusting someone else's percentage. - Sentinel and
CLIENT CAPA redirectare not supported. Use separate clients for blocking work, check timeouts, and migrate one service at a time.
Plan your GLIDE migration with JusDB
JusDB's Valkey consulting team can review your client libraries, cluster layout, and availability zone settings. We can help plan a GLIDE migration with read strategies chosen per call path, and a rollout and rollback plan for each service.
Bring your Valkey versions, topology and zone layout, the client library and version for each service, and your current timeout and retry settings. Your team approves every production change.