sqlx-pool-router
A lightweight Rust library for routing database operations to different SQLx PostgreSQL connection pools based on whether they're read or write operations.
This enables load distribution by routing read-heavy operations to read replicas while ensuring write operations always go to the primary database.
Features
- Zero-cost abstraction: Trait-based design with no runtime overhead
- Type-safe routing: Compile-time guarantees for read/write pool separation
- Backward compatible:
PgPoolimplementsPoolProviderfor seamless integration - Flexible: Use single pool or separate primary/replica pools
- Well-tested: Comprehensive test suite with replica routing verification
Installation
Add this to your Cargo.toml:
[]
= "0.1"
= { = "0.8", = ["postgres", "runtime-tokio"] }
Quick Start
Single Pool (Development)
use PgPool;
use sqlx-pool-PoolProvider;
async
Read/Write Separation (Production)
use PgPoolOptions;
use sqlx-pool-;
async
Testing with TestDbPools
The crate includes a TestDbPools helper for use with #[sqlx::test] that enforces read/write separation in your tests:
use PgPool;
use sqlx-pool-;
async
Why use TestDbPools?
- Catches routing bugs immediately in tests
- No need for an actual replica database in test environment
- Enforces
default_transaction_read_only = onon the read pool - PostgreSQL will reject any write operations on
.read()
Generic Programming
Make your types generic over PoolProvider to support both single and multi-pool configurations:
use sqlx-pool-PoolProvider;
// Works with both PgPool and DbPools!
let repo_single = Repository ;
let repo_multi = Repository ;
When to Use Each Method
.read() - For Read Operations
Use for queries that:
- Don't modify data (SELECT without FOR UPDATE)
- Can tolerate slight staleness (eventual consistency)
- Benefit from load distribution
Examples: user listings, analytics, dashboards, search
.write() - For Write Operations
Use for operations that:
- Modify data (INSERT, UPDATE, DELETE)
- Require transactions
- Need locking reads (SELECT FOR UPDATE)
- Require read-after-write consistency
Examples: creating records, updates, deletes, transactions
Architecture
┌─────────────┐
│ DbPools │
└──────┬──────┘
│
┌────┴────┐
↓ ↓
┌─────┐ ┌─────────┐
│Primary│ │ Replica │ (optional)
└─────┘ └─────────┘
Real-World Use Cases
This library is used in production by:
- outlet-postgres - HTTP request/response logging middleware
- fusillade - LLM request batching daemon
- dwctl - Observability and analytics platform
License
This project is 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.
Running Tests
The test suite requires a PostgreSQL database:
# Start PostgreSQL (using Docker)
# Set the DATABASE_URL (use 'postgres' database for sqlx::test to create isolated test DBs)
# Run tests
# Clean up
&&
Note: The tests use #[sqlx::test] which automatically creates isolated test databases for each test, so you don't need to worry about test pollution.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Commit Convention
This project uses Conventional Commits. Please format your commits as:
feat:New featuresfix:Bug fixesdocs:Documentation changestest:Test additions or modificationsrefactor:Code refactoringperf:Performance improvementschore:Build process or tooling changes
Example: feat: add support for connection timeout configuration