How Valkey Client-Side Caching Works and When to Use It
Updated: May 3, 2025
By: Joseph Horace
Table of Contents
Introduction
Valkey is a powerful system designed to optimize caching and key management across distributed clients. One of the key features of Valkey is client-side caching, which allows clients to cache data locally and only receive updates when necessary. This reduces latency and network overhead, making applications more efficient and responsive.
In this tutorial, we’ll explore how client-side caching works in Valkey, how to enable it, and why it's a valuable tool for improving performance in distributed systems. We’ll also discuss how Valkey efficiently handles cache invalidation, ensuring that clients are always working with up-to-date data.
What Is Client-Side Caching?
In a traditional client-server interaction, applications fetch data directly from the server each time it's needed. For example, a GET foo request goes from the client to Valkey, and the server replies with the current value. This works but introduces latency, consumes bandwidth, and increases server load—especially when the same keys are accessed frequently.
To improve performance, applications often introduce a local memory cache—a layer that stores recently fetched keys in the client itself. When the same key is requested again, the client can serve the value directly from local memory, avoiding a network round-trip.
However, this creates a fundamental problem: data freshness. Without coordination, the server has no way to notify the client when a cached key is modified elsewhere. This means clients may serve stale data, violating correctness.
Valkey solves this by offering built-in client-side caching support, which combines:
- Tracking: Clients can tell Valkey which keys they are interested in.
- Invalidation messages: Valkey notifies clients when those keys are modified.
- Broadcast mode: For pub/sub-style invalidations without client-specific tracking.
By combining local caching with server-side invalidation tracking, Valkey enables high-performance, low-latency access without sacrificing consistency.
Key Invalidation
At the heart of Valkey’s client-side caching lies key invalidation. When a client reads a key and caches it locally, the server keeps track of that access. If another client modifies that key later (e.g., via SET, DEL, or EXPIRE), Valkey sends an invalidation message to notify the original client that the data is stale.
Client-Side Caching Modes
Valkey offers two primary modes for client-side caching: the default mode and broadcasting mode. These modes determine how the server manages and invalidates cached data on the client side, balancing between server memory usage and client notification efficiency.
1. Default Mode (Tracking Mode)
In this mode, the server tracks which keys each client accesses. When a key is modified, the server sends an invalidation message to clients that might have cached that key. This approach ensures that only relevant clients are notified, but it requires the server to maintain memory for each client's accessed keys.
Enabling tracking mode (Example)
CLIENT TRACKING on REDIRECT 1234 OPTIN
2. Broadcasting Mode
Here, the server doesn't track individual client accesses. Instead, clients subscribe to key prefixes (e.g., user:) and receive notifications whenever any key matching that prefix is modified. This mode reduces server memory usage but may lead to unnecessary notifications for clients that don't have the affected keys cached.
Enabling Broadcasting Mode (Example)
CLIENT TRACKING on REDIRECT 10 BCAST PREFIX object: PREFIX user:
All clients subscribed to those prefixes receive all invalidations matching those patterns.
The client itself decides whether to act on the invalidation based on its local cache.
RESP2 vs. RESP3: Protocol Differences
Valkey’s behavior differs depending on whether the client uses RESP2 vs RESP3.
RESP3 (Recommended)
used for regular commands. This design simplifies implementation and ensures that messages arrive in the correct order. As a result, RESP3-based caching is more robust, reliable, and easier to manage.
RESP2
In contrast, RESP2 requires a more complex setup:
- Clients must maintain two separate connections:
- One for sending regular commands (e.g., GET, SET).
- Another dedicated connection for receiving invalidation messages.
Because of the additional complexity and potential for race conditions, RESP3 is generally preferred for implementing client-side caching.
Common Problems with client-cache and their solutions
1. Race Conditions
When using separate connections for data and invalidation messages—common in RESP2-based setups—there’s a risk of race conditions. For example, a client may send a GET foo on the data connection while an invalidation for foo arrives on the invalidation connection before the GET response does. This results in caching stale data: the client receives the invalidation, but still stores the now-outdated value returned afterward. To prevent this, clients can use a placeholder strategy. When a command is issued, the cache temporarily stores a “caching-in-progress” marker. If an invalidation arrives before the response, the client clears the placeholder. When the actual reply arrives, it is only stored if the placeholder is still present. This ensures that stale values are never cached. Notably, this race condition does not occur in RESP3, where both data and invalidations are sent over the same connection, preserving message order and simplifying cache consistency.
2. Invalidation Connection Loss
Losing the invalidation connection means missing updates, leading to stale caches.
Solutions:
- Periodically ping the invalidation connection.
- Flush the local cache if the connection is lost.
Caching best practices (what to cache)
What to Cache
- Avoid caching frequently changing keys. Continuously updated keys (like a global counter using INCR) generate a high volume of invalidation messages and are unlikely to yield performance gains.
- Avoid caching rarely accessed keys. If a key is not requested often, caching it provides little benefit while still consuming client memory.
- Cache keys that are accessed frequently and change at a reasonable rate. These offer the best return on investment for caching—higher hit rates with lower invalidation overhead.
How to Cache
- Handle TTLs carefully. If you want to cache keys that have an expiration (TTL) in Valkey, you should also retrieve and apply the TTL in your local cache. This ensures local entries expire consistently with the server-side keys.
- Set a maximum TTL for all keys, even if they don’t have one. Applying a default TTL protects against issues like network partitions or client crashes that could otherwise leave stale data lingering indefinitely in the local cache.
- Enforce memory limits on the client. Clients must have a policy for evicting old entries when memory usage grows—whether based on time since last access, least recently used (LRU), or other custom strategies. Without this, local caches can balloon uncontrollably, causing performance degradation or system instability.
Advanced Options: OPTIN
OPTIN
With this option, tracking is off by default; the client has to explicitly specify which commands to cache. It requires more bandwidth when caching new objects but reduces the amount of data the server has to remember and the number of invalidation messages received by the client.
Here is how to enable it
CLIENT TRACKING on REDIRECT 1234 OPTIN
After this, before sending the command that we want to cache, we have to send an extra command first, like:
CLIENT CACHING YES
After that, we can send the command we want to cache.
Conclusion
Valkey’s client-side caching offers a powerful way to reduce latency and server load by allowing clients to locally cache frequently accessed data—while still ensuring consistency through server-driven invalidation. By understanding the available caching modes, choosing the right RESP protocol, and following best practices for cache management, developers can build faster, more reliable applications with minimal risk of serving stale data. When used thoughtfully, client-side caching in Valkey becomes a key optimization tool for high-performance distributed systems.
References
Background References
About the Author
Joseph Horace
Horace is a dedicated software developer with a deep passion for technology and problem-solving. With years of experience in developing robust and scalable applications, Horace specializes in building user-friendly solutions using cutting-edge technologies. His expertise spans across multiple areas of software development, with a focus on delivering high-quality code and seamless user experiences. Horace believes in continuous learning and enjoys sharing insights with the community through contributions and collaborations. When not coding, he enjoys exploring new technologies and staying updated on industry trends.