A high-performance, developer-friendly PostgreSQL connector for Rust. This crate provides a streamlined API for managing database connections with built-in pooling and dedicated support for read/write cluster split architectures.
Built on top of tokio-postgres and bb8, it ensures your application scales efficiently while keeping your code clean.
- 🏎️ High Performance: Asynchronous I/O powered by
tokioandtokio-postgres. - 🔋 Robust Pooling: Intelligent connection management using
bb8. - ⚖️ Read/Write Splitting: Native support for clusters with separate read and write endpoints.
- 🚰 Streamlined API: One unified trait (
SqlExpose) for all your SQL operations. - 🛠️ Transaction Support: Full async implementation for PostgreSQL transactions.
- 🛡️ Errors Made Easy: Specific, actionable error types for connection and pooling issues.
Add this to your Cargo.toml:
[dependencies]
pg_connector = "1.0.0"
tokio = { version = "1.40", features = ["full"] }
bb8-postgres = "0.9" # Optional: if you need custom TLS or typesuse pg_connector::{PgConnector, SqlExpose, ClusterDefine, PoolManager};
use bb8_postgres::tokio_postgres::NoTls;
use bb8::QueueStrategy;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Setup Pool Managers for Write (Primary) and Read (Replica)
let manager_write = PoolManager::new(
"primary.db.ptech.software",
5432,
"postgres",
"mypassword",
Some("mydb"),
0,
5,
NoTls
);
let manager_read = PoolManager::new(
"replica.db.ptech.software",
5432,
"postgres",
"mypassword",
Some("mydb"),
0,
5,
NoTls
);
// 2. Initialize Read and Write Pools
let pool_write = manager_write.get_pool(5, 20, true, 5000, None, QueueStrategy::Fifo).await?;
let pool_read = manager_read.get_pool(10, 50, true, 5000, None, QueueStrategy::Fifo).await?;
// 3. Create the Connector
let connector = PgConnector::new(pool_write, pool_read);
// 4. Run Queries (Reads from Replica, Writes to Primary)
let rows = connector.query(
ClusterDefine::READ,
"SELECT id, name FROM users WHERE active = $1",
&[&true]
).await?;
for row in rows {
let id: i32 = row.get(0);
let name: &str = row.get(1);
println!("User {}: {}", id, name);
}
Ok(())
}pg_connector makes transactions simple. The SqlExpose trait is also implemented for Transaction, allowing you to use the same methods inside a transaction block.
let mut conn = connector.get_write().await?;
let transaction = conn.transaction().await?;
// Use the same trait methods!
transaction.execute(ClusterDefine::WRITE, "UPDATE accounts SET balance = balance - 100 WHERE id = 1", &[]).await?;
transaction.execute(ClusterDefine::WRITE, "UPDATE accounts SET balance = balance + 100 WHERE id = 2", &[]).await?;
transaction.commit().await?;For repeated queries, use prepare or prepare_typed for maximum performance.
let stmt = connector.prepare(ClusterDefine::READ, "SELECT * FROM large_table WHERE val = $1").await?;
let results = connector.query(ClusterDefine::READ, &stmt.query_string(), &[&42]).await?;Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contributions are welcome! Please feel free to submit a Pull Request.
- Add feature to get detailed error information from Postgres.