Mastering Redis Integration in Rust: Singleton & Connection Pool Guide
This article explains how to integrate Redis into a Rust backend by defining resource structs, wrapping the client with enums, implementing a connection pool using r2d2, creating a global singleton with once_cell, and providing example code for querying and storing data.
When developing backend applications, interacting with databases and caches like Redis is common. This guide shows how to query Redis and implement both singleton and connection‑pool patterns in Rust.
Key Crates
The implementation relies on three crates: once_cell – provides a thread‑safe singleton. redis‑rs – the official Redis driver for Rust. r2d2 – a generic connection‑pool library.
Redis Resource Definition
The RedisInstance struct mirrors the configuration file and describes a Redis resource.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Clone)]
#[serde(rename_all = "lowercase")]
pub struct RedisInstance {
#[serde(default = "RedisInstance::urls_default")]
pub urls: Vec<String>,
#[serde(default = "RedisInstance::password_default")]
pub password: String,
#[serde(default = "RedisInstance::instance_type_default")]
pub instance_type: InstanceType,
}Client and Connection Wrappers
The RedisClient enum abstracts single‑node and cluster connections, while RedisConnection represents the concrete connection types.
#[derive(Clone)]
pub enum RedisClient {
Single(redis::Client),
Cluster(redis::cluster::ClusterClient),
}
impl RedisClient {
pub fn get_redis_connection(&self) -> RedisResult<RedisConnection> {
match self {
RedisClient::Single(s) => {
let conn = s.get_connection()?;
Ok(RedisConnection::Single(Box::new(conn)))
}
RedisClient::Cluster(c) => {
let conn = c.get_connection()?;
Ok(RedisConnection::Cluster(Box::new(conn)))
}
}
}
}
pub enum RedisConnection {
Single(Box<redis::Connection>),
Cluster(Box<redis::cluster::ClusterConnection>),
}
impl RedisConnection {
pub fn is_open(&self) -> bool {
match self {
RedisConnection::Single(sc) => sc.is_open(),
RedisConnection::Cluster(cc) => cc.is_open(),
}
}
pub fn query<T: FromRedisValue>(&mut self, cmd: &redis::Cmd) -> RedisResult<T> {
match self {
RedisConnection::Single(sc) => sc.as_mut().req_command(cmd).and_then(|v| from_redis_value(&v)),
RedisConnection::Cluster(cc) => cc.req_command(cmd).and_then(|v| from_redis_value(&v)),
}
}
}Connection Pool with r2d2
To enable pooling, a manager implementing r2d2::ManageConnection is defined.
#[derive(Clone)]
pub struct RedisConnectionManager {
pub redis_client: RedisClient,
}
impl r2d2::ManageConnection for RedisConnectionManager {
type Connection = RedisConnection;
type Error = RedisError;
fn connect(&self) -> Result<RedisConnection, Self::Error> {
let conn = self.redis_client.get_redis_connection()?;
Ok(conn)
}
fn is_valid(&self, conn: &mut RedisConnection) -> Result<(), Self::Error> {
match conn {
RedisConnection::Single(sc) => redis::cmd("PING").query(sc)?,
RedisConnection::Cluster(cc) => redis::cmd("PING").query(cc)?,
}
Ok(())
}
fn has_broken(&self, conn: &mut RedisConnection) -> bool {
!conn.is_open()
}
}Generating the Pool
The gen_redis_conn_pool function builds a pool based on configuration values such as maximum size, minimum idle connections, and timeout.
pub fn gen_redis_conn_pool() -> Result<Pool<RedisConnectionManager>> {
let config = get_config()?;
let redis_client = config.redis.instance.to_redis_client()?;
let manager = RedisConnectionManager { redis_client };
let pool = r2d2::Pool::builder()
.max_size(config.redis.pool.max_size as u32)
.min_idle(Some(config.redis.pool.mini_idle as u32))
.connection_timeout(Duration::from_secs(config.redis.pool.connection_timeout as u64))
.build(manager)?;
Ok(pool)
}Global Singleton
A static GLOBAL_REDIS_POOL is created with OnceCell to hold the pool.
pub static GLOBAL_REDIS_POOL: OnceCell<r2d2::Pool<RedisConnectionManager>> = OnceCell::new();The initializer populates the cell, panicking if pool creation fails.
fn init_global_redis() {
GLOBAL_REDIS_POOL.get_or_init(|| {
match gen_redis_conn_pool() {
Ok(it) => it,
Err(err) => panic!("{}", err.to_string()),
}
});
}Using the Pool
Example code shows how to obtain a connection from the global pool and execute a SET command.
pub fn put(kv: KV) -> Result<()> {
let conn = GLOBAL_REDIS_POOL.get();
match conn {
Some(c) => {
c.get()?.query(redis::cmd("set").arg(kv.Key).arg(kv.Value))?;
Ok(())
}
None => Err(anyhow!("redis pool not init")),
}
}The full repository ( fullstack-rs ) contains the complete HTTP server flow that writes to Redis, though the HTTP layer is not covered here.
With these components, you can efficiently manage Redis connections in a Rust backend, leveraging both singleton access and a configurable connection pool.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JD Cloud Developers
JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
