Broker Config
Basic Usage
Section titled “Basic Usage”use horsies::PostgresConfig;
let broker = PostgresConfig::from_url( "postgresql://user:password@localhost:5432/mydb");Fields
Section titled “Fields”| Field | Type | Default | Description |
|---|---|---|---|
database_url | String | required | PostgreSQL runtime connection URL |
session_database_url | Option<String> | None | Direct/session-capable URL for schema initialization and LISTEN/NOTIFY |
pgbouncer_transaction_mode | bool | false | Disable SQLx named prepared-statement caching for PgBouncer transaction pools |
pool_size | u32 | 30 | Runtime SQL connection pool size |
max_overflow | u32 | 30 | Additional runtime SQL connections beyond pool_size |
retain_rerun_input_default | bool | false | Keep canonical input for later task rerun |
pool_timeout | u32 | 30 | Seconds to wait for connection |
pool_pre_ping | bool | true | Pre-ping connections before use |
echo | bool | false | Echo SQL statements to logs |
pool_recycle | u32 | 1800 | Recycle connections after N seconds |
Connection URL Format
Section titled “Connection URL Format”postgresql://user:password@host:port/databaseComponents:
postgresqlorpostgres- Scheme (both accepted)user:password- Credentialshost:port- Server location (default port: 5432)database- Database name
Note: The Python version uses postgresql+psycopg:// (SQLAlchemy driver prefix). In Rust, use plain postgresql:// or postgres://.
Examples
Section titled “Examples”Local Development
Section titled “Local Development”PostgresConfig::from_url( "postgresql://postgres:postgres@localhost:5432/horsies_dev")Production with Connection Pool
Section titled “Production with Connection Pool”PostgresConfig { database_url: "postgresql://app:secret@db.example.com:5432/production".into(), session_database_url: None, pgbouncer_transaction_mode: false, pool_size: 10, max_overflow: 20, retain_rerun_input_default: false, pool_timeout: 30, pool_recycle: 3600, pool_pre_ping: true, echo: false,}From Environment Variable
Section titled “From Environment Variable”PostgresConfig::from_url( std::env::var("DATABASE_URL").expect("DATABASE_URL must be set"))Connection Pooling
Section titled “Connection Pooling”The broker manages an async connection pool:
pool_size: Base number of persistent connectionsmax_overflow: Additional connections created under load (temporary)pool_timeout: How long to wait if all connections are busypool_recycle: Close and recreate connections after this many seconds
Sizing Guidelines
Section titled “Sizing Guidelines”| Deployment | pool_size | max_overflow |
|---|---|---|
| Development | 2-5 | 5 |
| Small production | 5-10 | 10-20 |
| High traffic | 10-20 | 20-40 |
Consider: pool_size + max_overflow should not exceed PostgreSQL’s max_connections (divided by number of worker instances).
Each new Horsies connection runs SET jit = off. Horsies statements are short, and PostgreSQL’s JIT compile time exceeds their run time when a cost estimate passes jit_above_cost. The runtime pool in PgBouncer transaction mode is the exception: its server sessions are shared, so Horsies does not change session settings there. For that pool, set JIT off on the server side, for example ALTER ROLE horsies_role SET jit = off;.
PgBouncer Transaction Pooling
Section titled “PgBouncer Transaction Pooling”PgBouncer transaction pooling can be used for normal task and workflow SQL, but it cannot preserve session state for LISTEN/NOTIFY. Horsies therefore needs two URLs:
database_url: runtime SQL URL, which may point at the transaction-pool endpoint.session_database_url: direct or session-pooled URL used for schema initialization andLISTEN/NOTIFY.
let broker = PostgresConfig::from_pgbouncer_urls( std::env::var("DATABASE_URL").expect("DATABASE_URL must be set"), std::env::var("SESSION_DATABASE_URL").expect("SESSION_DATABASE_URL must be set"),);With pgbouncer_transaction_mode=true, Horsies disables SQLx’s named prepared-statement cache for the runtime pool. Schema initialization, workers, result listeners, and workflow listeners use session_database_url. When session_database_url is different from database_url, Horsies caps that direct/session-capable pool at 4 connections instead of inheriting the runtime pool size.
Different providers expose direct and pooled Postgres endpoints differently. Some publish separate ports, some publish separate hostnames, and those details can change; check your provider’s current connection documentation instead of copying an example port.
app.check_live() is a cheap runtime-pool SELECT 1 liveness check. Workers run a bounded LISTEN + NOTIFY delivery probe once at startup in PgBouncer transaction mode. If the session URL is accidentally transaction-pooled, worker startup fails with a message indicating that session_database_url cannot preserve LISTEN/NOTIFY state.
Multiple Components
Section titled “Multiple Components”The broker creates two connection types:
- Query pool: For queries, inserts, updates
- LISTEN/NOTIFY: For real-time notifications
By default both use database_url. When session_database_url is configured, schema initialization and LISTEN/NOTIFY use the session URL while normal SQL uses database_url.
Schema Initialization
Section titled “Schema Initialization”When you use high-level Horsies entry points such as task sends, workflow starts, app.run_worker(), app.run_scheduler(), app.get_broker(), or app.check_live(), the library ensures the schema automatically on first use.
Schema initialization uses a fast path when the embedded migrations are already current. Fresh or upgraded databases still run the migration path under a PostgreSQL advisory lock, double-check migration state after acquiring the lock, and retry PostgreSQL deadlocks.
Use an explicit broker call when you want a preflight step outside those paths, or when you’re working with PostgresBroker directly:
horsies_tasks- Live task storagehorsies_task_history- Partitioned terminal task storagehorsies_task_attempts- Live per-attempt rows before terminal archivalhorsies_heartbeats- Liveness trackinghorsies_worker_states- Worker monitoringhorsies_schedule_state- Scheduler state
let broker = PostgresBroker::connect_with(config).await?;broker.ensure_schema_initialized().await?;