How DBX Handles Connection Pooling for MySQL, PostgreSQL, and Redis
DBX centralizes connection pooling in an AppState struct that maintains a thread-safe HashMap of driver-specific pools, lazily initializing MySQL, PostgreSQL, and Redis connections via the get_or_create_pool async method.
The t8y2/dbx repository implements a unified connection management layer that abstracts database-specific pooling implementations behind a single registry. By storing active pools in a central HashMap<String, PoolKind> within AppState, DBX enables efficient connection reuse across heterogeneous backends while handling driver-specific nuances like TLS cancellation contexts and Redis cluster topology.
Centralized Pool Registry in AppState
At the core of DBX's pooling strategy is the AppState struct defined in crates/dbx-core/src/connection.rs. This state object maintains a thread-safe mapping of pool keys to driver-specific pool instances:
// Conceptual structure from connection.rs
pub struct AppState {
connections: RwLock<HashMap<String, PoolKind>>,
configs: RwLock<HashMap<String, ConnectionConfig>>,
postgres_cancel_contexts: RwLock<HashMap<String, TlsCancelContext>>,
}
The PoolKind enum distinguishes between the three supported database types, wrapping mysql_async::Pool for MySQL, deadpool_postgres::Pool for PostgreSQL, and a custom RedisConnection type for Redis.
When a component requests a connection, it invokes get_or_create_pool, which implements a lazy-initialization pattern:
- Derives a unique pool key using
base_pool_key_forcombined withsession_scoped_pool_key_forto account for connection IDs, database names, and optional client-session isolation. - Checks the existing pool map—if a matching pool exists and is fresh, it updates the activity timestamp and returns the existing key.
- Creates a driver-specific pool via the appropriate constructor if no match exists, then inserts it into the central map.
MySQL Connection Pooling Implementation
For MySQL, DBX utilizes the mysql_async crate's Pool type, wrapped in the PoolKind::Mysql(MySqlPool, MysqlMode) variant. The implementation distinguishes between metadata pools and bare pools depending on whether schema introspection requires a connection without a default database selected.
Key implementation details in crates/dbx-core/src/connection.rs:
- Pool Limits: Per-session connection limits are enforced via
mysql_pool_max_connections_for_session, preventing a single client session from exhausting the global pool. - OceanBase/Oracle Compatibility: When connecting to OceanBase or Oracle-mode MySQL, DBX executes
SET ob_query_timeoutimmediately after establishing the connection viaoceanbase_mysql_setup_queries. - TLS Support: Optional TLS configuration is applied during pool creation in
connect_mysql_metadata_pool.
The following Tauri command demonstrates acquiring a MySQL pool and executing a query:
#[tauri::command]
pub async fn mysql_list_tables(state: State<'_, Arc<AppState>>, conn_id: String, db: String) -> Result<Vec<String>, String> {
// Acquire (or create) a MySQL pool for the requested database
let pool_key = state.get_or_create_pool(&conn_id, Some(&db)).await?;
// Obtain the pool from the central map
let pool = state.connections.read().await.get(&pool_key)
.ok_or("Pool disappeared")?;
// The pool is of type `PoolKind::Mysql`
if let PoolKind::Mysql(ref mysql_pool, _) = pool {
let mut conn = mysql_pool.get_conn().await.map_err(|e| e.to_string())?;
let rows: Vec<mysql_async::Row> = conn
.query("SHOW TABLES")
.await
.map_err(|e| e.to_string())?;
Ok(rows.iter()
.map(|r| r.as_string("Tables_in_".to_owned() + &db).unwrap_or_default())
.collect())
} else {
Err("Not a MySQL connection".into())
}
}
PostgreSQL Connection Pooling with Deadpool
DBX implements PostgreSQL pooling using the deadpool_postgres crate, storing pools as PoolKind::Postgres(deadpool_postgres::Pool). This provides Tokio-based connection management with built-in bounds checking.
A notable DBX-specific extension is the TLS cancel context mechanism. When creating a PostgreSQL pool, DBX optionally stores a cancel context via build_postgres_cancel_context:
// From connection.rs (simplified)
DatabaseType::Postgres => {
let pg_pool = db::postgres::connect(&url, connect_timeout).await?;
// TLS cancel context for query cancellation
if let Some(ctx) = db::postgres::build_postgres_cancel_context(&url) {
state.postgres_cancel_contexts.write().await.insert(pool_key.clone(), ctx);
}
PoolKind::Postgres(pg_pool)
}
This context allows cancelled queries to reconstruct the TLS connection stream when terminating long-running operations.
Example usage within a Tauri command:
#[tauri::command]
pub async fn pg_query_scalar(state: State<'_, Arc<AppState>>, conn_id: String, sql: String) -> Result<serde_json::Value, String> {
let pool_key = state.get_or_create_pool(&conn_id, None).await?;
let pool = state.connections.read().await.get(&pool_key)
.ok_or("Missing pool")?;
if let PoolKind::Postgres(ref pg_pool) = pool {
let client = pg_pool.get().await.map_err(|e| e.to_string())?;
let row = client.query_one(&sql, &[]).await.map_err(|e| e.to_string())?;
// Convert the first column to JSON
Ok(serde_json::to_value(row.get::<usize, i64>(0)).unwrap())
} else {
Err("Not a Postgres connection".into())
}
}
Redis Connection Pooling for Cluster and Sentinel
Redis connections in DBX are represented by PoolKind::Redis(RedisConnection), where RedisConnection is an enum supporting three deployment modes:
- Cluster: Uses
connect_redis_clusterto build a client aware of all cluster nodes, enabling automatic slot routing. - Sentinel: Uses
connect_redis_sentinelto discover the current master via Sentinel before creating a direct client. - Standalone: Uses
connect_standalonefor simple single-instance Redis, respecting TLS and authentication settings.
The connection object wraps either a Cluster variant or a Direct variant containing a tokio::sync::Mutex around the client:
// From connection.rs (lines 144-158)
DatabaseType::Redis => {
let con = if db_config.uses_redis_cluster() {
db::redis_driver::RedisConnection::Cluster(
self.connect_redis_cluster(connection_id, &db_config).await?)
} else if db_config.uses_redis_sentinel() {
db::redis_driver::RedisConnection::Direct(tokio::sync::Mutex::new(
self.connect_redis_sentinel(connection_id, &db_config).await?))
} else {
db::redis_driver::RedisConnection::Direct(tokio::sync::Mutex::new(
db::redis_driver::connect_standalone(&db_config, &host, port, connect_timeout).await?))
};
PoolKind::Redis(con)
}
Accessing a Redis value requires matching on the RedisConnection variant:
#[tauri::command]
pub async fn redis_get(state: State<'_, Arc<AppState>>, conn_id: String, db: u32, key: String) -> Result<Option<String>, String> {
let pool_key = state.get_or_create_pool(&conn_id, None).await?;
let pool = state.connections.read().await.get(&pool_key)
.ok_or("Missing pool")?;
if let PoolKind::Redis(ref redis_conn) = pool {
match redis_conn {
db::redis_driver::RedisConnection::Direct(ref mutex) => {
let client = mutex.lock().await;
let value: Option<String> = client.get(key).await.map_err(|e| e.to_string())?;
Ok(value)
}
db::redis_driver::RedisConnection::Cluster(ref cluster) => {
let value: Option<String> = cluster.get(key).await.map_err(|e| e.to_string())?;
Ok(value)
}
}
} else {
Err("Not a Redis connection".into())
}
}
Summary
- Unified Registry: DBX maintains all connection pools in a central
HashMap<String, PoolKind>withinAppState, enabling thread-safe access and reuse across the application. - Lazy Initialization: The
get_or_create_poolmethod checks for existing pools using derived keys (base_pool_key_for+session_scoped_pool_key_for) before creating new ones, minimizing redundant connections. - Driver-Specific Optimization: MySQL pools support OceanBase-specific timeouts and session limits; PostgreSQL pools include TLS cancel contexts for safe query termination; Redis pools abstract Cluster, Sentinel, and Standalone modes behind a single interface.
- Type Safety: The
PoolKindenum ensures compile-time differentiation betweenmysql_async::Pool,deadpool_postgres::Pool, andRedisConnectionvariants.
Frequently Asked Questions
How does DBX determine when to reuse an existing pool versus creating a new one?
DBX derives a unique pool key using base_pool_key_for combined with session_scoped_pool_key_for, incorporating the connection ID, database name, and optional client-session ID. Before creating a pool, get_or_create_pool checks if this key exists in the connections HashMap. If found, it updates the activity timestamp via touch_pool_activity and returns the existing pool, ensuring efficient reuse while preventing connection leaks.
What is the difference between MySQL metadata pools and bare pools in DBX?
Metadata pools are created when DBX needs to perform schema introspection without assuming a specific default database, using connect_bare_metadata_pool or connect_mysql_metadata_pool. These are used for operations like listing databases or tables. Bare pools refer to connections established without a default database context, often used internally when the target database is specified per-query rather than in the connection string.
How does DBX handle query cancellation for PostgreSQL connections?
DBX implements a TLS cancel context mechanism via build_postgres_cancel_context. When establishing a PostgreSQL pool, it stores the TLS parameters needed to reconstruct the connection stream in postgres_cancel_contexts. If a query needs to be cancelled, DBX can use this context to establish a separate cancellation connection that respects the original TLS configuration, allowing safe termination of long-running queries without corrupting the connection pool.
Does DBX support Redis Cluster and Sentinel deployments?
Yes. The RedisConnection enum in crates/dbx-core/src/db/redis_driver.rs explicitly supports three deployment modes. Cluster mode uses connect_redis_cluster for automatic slot routing across nodes. Sentinel mode uses connect_redis_sentinel to discover the current master dynamically. Standalone mode uses connect_standalone for direct single-node connections. All three modes respect TLS and authentication settings from the connection configuration.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →