Redis API
HydraCache can expose an optional Redis-compatible RESP edge for clients that already speak Redis.
This surface is a compatibility facade over HydraCache client-surface commands. It is not a promise that HydraCache is a full Redis replacement, and it does not add SQL/query semantics. Use it when Redis client interoperability is useful but the application still wants HydraCache ownership of cache data, TTLs, tags, diagnostics, and invalidation intent.
Crates
| Crate | Role |
|---|---|
hydracache-redis-compat | RESP2/RESP3 parsing, command translation, Redis-style responses, resource limits, and HydraCache extension commands. |
hydracache-server | Optional TCP listener that serves the Redis RESP facade in the standalone daemon. |
Application crates that only use the embedded HydraCache runtime do not need these crates.
Enable The Listener
The server disables the Redis facade by default. Enable it explicitly and keep it on a separate address from HTTP, admin, and cluster listeners.
$env:HYDRACACHE_REDIS_API_ENABLED = "true"
$env:HYDRACACHE_REDIS_ADDR = "127.0.0.1:6379"
For authenticated local or staging use:
$env:HYDRACACHE_REDIS_AUTH_REQUIRED = "true"
$env:HYDRACACHE_REDIS_AUTH_USERNAME = "default"
$env:HYDRACACHE_REDIS_AUTH_TOKEN_FILE = "C:\secrets\hydracache-redis-token.txt"
For rediss://, enable server TLS and the Redis TLS facade together:
$env:HYDRACACHE_TLS_ENABLED = "true"
$env:HYDRACACHE_TLS_CERT_PATH = "C:\certs\server.crt"
$env:HYDRACACHE_TLS_KEY_PATH = "C:\certs\server.key"
$env:HYDRACACHE_TLS_CA_PATH = "C:\certs\ca.crt"
$env:HYDRACACHE_REDIS_REDISS_ENABLED = "true"
HydraCache rejects rediss startup without complete TLS material.
Keyspace notifications are a separate off-by-default 0.68 capability:
$env:HYDRACACHE_REDIS_KEYSPACE_EVENTS_ENABLED = "true"
$env:HYDRACACHE_REDIS_MAX_EVENT_SUBSCRIPTIONS_PER_CONNECTION = "64"
$env:HYDRACACHE_REDIS_MAX_EVENT_SUBSCRIPTION_BYTES_PER_CONNECTION = "65536"
Use SUBSCRIBE/PSUBSCRIBE for __keyspace@0__:* or
__keyevent@0__:*; use the matching unsubscribe command to release the live
connection state. Admission is bounded by both unique subscription count and
retained channel/pattern bytes. The event mode supports only cache-mutation
notifications, not arbitrary Pub/Sub or PUBLISH.
Observe a native backend write from a Redis client
The server also projects successful native writes into this notification
stream. Use the native typed namespace named redis: its physical
redis:<key> prefix is stripped before the Redis channel is rendered. This
explicit namespace fence prevents unrelated native cache entries from being
exposed to Redis subscribers. Values never enter the event message or the RESP
client-surface store, so a native-write notification does not make Redis GET
return that native value.
This stream is at-most-once. Removing the final subscription releases the native receiver, and writes made while no subscription exists are not replayed after resubscription. Slow subscribers are disconnected on a detectable gap; they must reconnect, resubscribe, and repair state with ordinary reads. RESP/HC2 and native events retain their own source order, but no global order is claimed between those independent sources.
The example below is compiled by the documentation build. It starts the real
RESP TCP listener, subscribes with redis-rs, writes with the ordinary native
HydraCache API, and verifies the received Redis key event.
let cache = HydraCache::local().build();
let resp = Arc::new(
RedisRespServer::new(
Arc::new(ClientSurfaceState::new(ClientSurfaceLimits::default())?),
RedisListenerConfig {
keyspace_events: RedisKeyspaceEventConfig {
enabled: true,
..RedisKeyspaceEventConfig::default()
},
..RedisListenerConfig::default()
},
)?
.with_native_cache_events(cache.clone()),
);
let listener = TcpListener::bind("127.0.0.1:0").await?;
let address = listener.local_addr()?;
let accepting = tokio::spawn(async move {
while let Ok((stream, _)) = listener.accept().await {
let resp = Arc::clone(&resp);
tokio::spawn(async move {
let _ = resp.serve_connection(stream).await;
});
}
});
// This is a normal Redis client using a normal keyspace subscription.
let client = redis::Client::open(format!("redis://{address}/"))?;
let mut pubsub = client.get_async_pubsub().await?;
pubsub.subscribe("__keyevent@0__:set").await?;
// The backend writes through the native HydraCache API. The `redis`
// typed namespace is the explicit, non-leaking bridge to Redis key bytes.
cache
.typed::<String>("redis")
.put("user:42", "Ada".to_owned(), CacheOptions::new())
.await?;
let message = tokio::time::timeout(Duration::from_secs(2), pubsub.on_message().next())
.await?
.ok_or("Redis subscription closed before the event")?;
assert_eq!(message.get_channel_name(), "__keyevent@0__:set");
assert_eq!(message.get_payload::<String>()?, "user:42");
drop(pubsub);
accepting.abort();
Supported Shape
The facade targets Redis string/cache-client interoperability:
- connection and introspection basics such as
PING,ECHO,HELLO,AUTH,CLIENT SETNAME,COMMAND,INFO,SELECT, andTYPE; - string and key operations such as
GET,SET,MGET,MSET,DEL, andEXISTS; - TTL operations such as
EXPIRE,PEXPIRE,PERSIST,TTL, andPTTL; - lock-oriented
EVAL,EVALSHA,SCRIPT LOAD, andSCRIPT EXISTSfor the supported compare-value lock scripts; - bounded
SUBSCRIBE,UNSUBSCRIBE,PSUBSCRIBE, andPUNSUBSCRIBEfor off-by-default keyspace notifications; and - HydraCache extension commands such as
HC.STATS,HC.DIAGNOSTICS,HC.INVALIDATE,HC.NAMESPACE,HC.TAG,HC.SETTAGS, andHC.INVALIDATE_TAG.
Unsupported Redis data structures or broad server features should be treated as outside the facade. For example, hash commands are not the goal of this surface.
Client Examples
Any Redis client can use the facade for the supported command subset.
PING
SET user:42 Ada PX 60000
GET user:42
MSET user:43 Grace user:44 Linus
MGET user:42 user:43 user:44
TTL user:42
DEL user:44
HydraCache-specific tags are available through extension commands:
HC.SETTAGS user:42 user:42 users
HC.INVALIDATE_TAG users
HC.STATS
HC.DIAGNOSTICS
Use tags when Redis-facing clients should participate in the same invalidation model as native HydraCache code.
Boundaries
The Redis API is best for interoperability, migration seams, smoke tests, and operational probes. Prefer the native Rust API when application code can call HydraCache directly because native calls preserve typed values, loader boundaries, single-flight behavior, and compile-time policy structure.
Review these boundaries before enabling the facade:
- RESP resource limits protect frame size, array length, bulk string size, read buffer size, and idle connections.
- AUTH is optional but should be required for any shared environment.
- The listener address must not conflict with HTTP, cluster, or admin addresses.
- Redis keys map into the Redis namespace of the HydraCache client surface.
- Keyspace notifications are node-local, metadata-only, at-most-once hints. A lagging client is disconnected and must reconnect, resubscribe, and repair state with ordinary reads.
- Redis clients send bytes, not typed Rust values; typed decoding remains a native API concern.
Use Redis compatibility to meet existing clients where they are. Use HydraCache semantics to decide what should be cached and invalidated.