Rust Redis has the redis crate (the official, sync + async via the aio module), bb8-redis and deadpool-redis (the connection pool options), and fred (the async-first, type-safe alternative). The right answer is redis + bb8-redis for a sync service, fred for a fully async service. The mistake every team makes: the team uses the redis crate’s sync client in an async runtime, the team blocks the event loop on every Redis call, the team’s async performance is destroyed.
Table of contents
- The redis-rs crate — the official client
- The connection pool — bb8-redis and deadpool-redis
- The async story — the aio module and fred
- The one mistake that blocks the event loop
- The pipeline — the right answer for multiple commands
- The pub/sub — the right answer for fan-out
- The Lua scripting — the right answer for atomicity
- How this fits the rest of the stack
- FAQ
The redis-rs crate — the official client
The redis crate is the official Rust client for Redis. The crate supports sync and async (the aio module), the crate has a connection pool (r2d2 integration), the crate has a typed API for every Redis command. The right answer is redis for a Rust service that needs to talk to Redis.
use redis::Commands;
let client = redis::Client::open("redis://127.0.0.1/")?;
let mut con = client.get_connection()?;
let value: Option<String> = con.get("key")?;
let _: () = con.set_ex("key", "value", 60)?;
The connection pool — bb8-redis and deadpool-redis
The redis crate’s get_connection() returns a single connection, not a pool. The right answer is to add a connection pool — bb8-redis (a bb8 adapter for redis) or deadpool-redis (a deadpool adapter). The right answer for a sync service is bb8-redis or deadpool-redis, the right answer for an async service is the async client with a pool.
The async story — the aio module and fred
The redis crate’s aio module provides an async client. The right answer is the async client for a Tokio-based async service. The gotcha: the redis crate’s async client wraps a single connection, the team’s async service needs a pool of async connections — use bb8-redis with the async manager, or use fred (a separate, async-first crate).
The fred crate is an async-first, type-safe alternative. The right answer is fred for a service that needs the typed API and the async performance. The wrong answer is fred for a sync service — the team is paying for the async overhead.
The one mistake that blocks the event loop
The mistake: the team uses the redis crate’s sync client (get_connection()) inside an async function. The sync call blocks the event loop, the team’s async performance is destroyed. The right answer is to use the async client (redis::aio::Connection) or fred, the wrong answer is to use the sync client in an async function.
The right answer is to wrap the sync call in tokio::task::spawn_blocking if the team absolutely must use the sync client in an async context. The wrong answer is to use tokio::task::spawn_blocking for every Redis call — the team is paying for the thread context switch.
The pipeline — the right answer for multiple commands
The right answer for multiple commands in a single round-trip is the pipeline. The redis crate has pipe() for the sync pipeline, redis::aio::Pipeline for the async pipeline. The right answer is the pipeline for any code that sends multiple commands, the wrong answer is to send each command individually (the team’s RTT is multiplied by the number of commands).
The pub/sub — the right answer for fan-out
The right answer for pub/sub is the pubsub module on the client. The team’s async code subscribes with client.get_async_pubsub(), the team receives messages via the stream. The right answer is pub/sub for a fan-out pattern (one producer, multiple consumers, fire-and-forget), the wrong answer is pub/sub for a durable message queue.
The Lua scripting — the right answer for atomicity
The right answer for atomicity is Lua scripting. The team writes a Lua script, the script runs on the Redis server atomically, the team’s operations are not interleaved with other clients. The right answer is Lua for a check-then-set (the team’s ‘set if not exists’ or ‘increment if not exists’ pattern), the wrong answer is a transaction (MULTI/EXEC) for the same — Lua is faster and more flexible.
How this fits the rest of the stack
The infrastructure question is a small piece of a larger pattern: the team’s runtime, storage, database, secret store, logs, and deployment platform are all parts of the same platform. The right answer is to model the full stack before the project ships, not after. The RunxBuild hosting calculator is the right place to do that exercise — pick the runtime, the memory tier, the storage, the secret store, and the egress, and the calculator shows what the deploy actually costs at the team’s actual usage.
Useful related references:
FAQ
What is the Rust Redis client?
The redis crate is the official Rust client. The crate supports sync and async (the aio module), the crate has a typed API for every Redis command.
How do I connect to Redis in Rust?
redis::Client::open("redis://127.0.0.1/") and client.get_connection(). The right answer is to use a pool for any service that handles concurrent requests.
Should I use async or sync Redis in Rust?
Async for an async service (Tokio, async-std), sync for a sync service. The wrong answer is the sync client in an async service — it blocks the event loop.
What is the connection pool for Rust Redis?
bb8-redis or deadpool-redis are the standard options. The right answer is a pool for any service that handles concurrent requests.
What is the fred crate?
An async-first, type-safe alternative to the redis crate. The right answer is fred for a service that needs the typed API and the async performance.
How do I do a pipeline in Rust Redis?
pipe() for the sync pipeline, redis::aio::Pipeline for the async. The right answer is the pipeline for any code that sends multiple commands.
How do I subscribe to a Redis channel in Rust?
Use the pubsub module on the client. client.get_async_pubsub().await? for async, the team receives messages via the stream.
How do I run a Lua script in Rust Redis?
Use the Script type: let script = redis::Script::new("return redis.call('GET', KEYS[1])"); script.key("key").invoke_async(&mut con).await?