Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
sqlx-pool-registry
Forked from doublewordai/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
- Explicit routing: Read/write intent stays visible at each database call site
- Backward compatible:
PgPoolimplementsPoolProviderfor seamless integration - Flexible: Use single pool or separate primary/replica pools
- SQLx compatibility: Select SQLx 0.8 (default) or SQLx 0.9 at compile time
- Named pools (optional): Group independent providers by name with the
with-named-poolsfeature - Legacy
Derefcompatibility (optional): RestoreDbPools: Deref<Target = PgPool>temporarily with thewith-dereffeature - Well-tested: Comprehensive test suite with replica routing verification
Installation
SQLx 0.8 is enabled by default:
[]
= "0.2.1"
= { = "0.8", = ["postgres", "runtime-tokio"] }
For SQLx 0.9, disable the default compatibility feature and select 0.9 explicitly:
[]
= { = "0.2.1", = false, = ["with-sqlx-0_9"] }
= { = "0.9", = ["postgres", "runtime-tokio"] }
Exactly one of with-sqlx-0_8 and with-sqlx-0_9 must be enabled. The selected SQLx crate is also available as sqlx_pool_registry::sqlx. Add with-named-pools to either configuration to enable PoolRegistry. The effective minimum Rust version is 1.94 with SQLx 0.8 and 1.94 with SQLx 0.9.
Legacy Deref migration
DbPools does not implement Deref<Target = PgPool> by default. To keep an
existing &*pools call compiling while migrating, add with-deref to the
crate features. This is a temporary compatibility path and will be removed in
the next major version.
[]
= { = "0.2.1", = ["with-deref"] }
&*pools always selects the primary pool and bypasses read routing. Do not use
it for database queries: replace fetch_one(&*pools) with fetch_one(pools.read())
for eligible reads or fetch_one(pools.write()) for writes, locking reads, and
read-after-write operations.
Quick Start
Single Pool (Development)
use PgPool;
use PoolProvider;
async
Read/Write Separation (Production)
use PgPoolOptions;
use ;
async
Named Pools (Optional)
With the with-named-pools feature, one registry can hold independent pool providers for databases such as auth and analytics. Looking up a name returns that provider; it does not select mutable registry-wide state.
use PgPoolOptions;
use ;
async
Use try_get() in functions returning Result; get() returns Option for optional lookups. DbPools::replica() exposes only an explicitly configured replica and may return None. In contrast, read() falls back to the primary, while write() always returns the primary.
Testing with TestDbPools
The crate includes a TestDbPools helper for use with #[sqlx::test] that makes ordinary write-through-read routing mistakes fail during tests:
use PgPool;
use ;
async
Why use TestDbPools?
- Helps catch routing bugs immediately in tests
- No need for an actual replica database in test environment
- Sets
default_transaction_read_only = onon each read-pool connection - PostgreSQL rejects writes to non-temporary tables by default
TestDbPools is a testing aid, not a security boundary. PostgreSQL clients can
override the default for an individual transaction or session, and read-only
transactions do not prohibit every possible write. See PostgreSQL's
SET TRANSACTION
documentation for the exact restrictions.
Generic Programming
Make your types generic over PoolProvider to support both single and multi-pool configurations:
use 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 default test suite is offline: it does not require PostgreSQL or DATABASE_URL. Database-dependent tests are explicitly ignored, so both SQLx configurations can be checked with:
To run only the database-dependent tests, start PostgreSQL and set DATABASE_URL explicitly:
# Start PostgreSQL (using Docker)
# The PostgreSQL role must have permission to create databases.
# Run only tests that require PostgreSQL.
# Clean up
For the release-equivalent full suite, replace -- --ignored with -- --include-ignored. The database tests use #[sqlx::test], which creates isolated test databases; this is why the role in DATABASE_URL needs CREATE DATABASE. An unset URL is fine for the default suite, while an unset or unreachable URL makes an explicit database-test run fail.
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