Skip to content

Broker Config

from horsies import PostgresConfig
broker = PostgresConfig(
database_url="postgresql+psycopg://user:password@localhost:5432/mydb",
)
FieldTypeDefaultDescription
database_urlstrrequiredSQLAlchemy connection URL
session_database_urlstr | NoneNoneDirect/session-capable URL for schema setup and LISTEN/NOTIFY
pgbouncer_transaction_modeboolFalseDisable prepared statements for transaction-pooled PgBouncer
pool_sizeint5Connection pool size (raise for high-throughput producers)
max_overflowint10Additional connections beyond pool_size
worker_pool_sizeint | None3Worker coordinator pool size; None inherits pool_size
worker_max_overflowint | None2Worker coordinator overflow; None inherits max_overflow
worker_child_pool_min_sizeint0Minimum connections kept by each child worker process
worker_child_pool_max_sizeint2Maximum connections allowed per child worker process
worker_child_pool_checkboolTrueHealth-check child pool connections on checkout; see Remote PostgreSQL
pool_timeoutint30Seconds to wait for connection
pool_pre_pingboolTruePre-ping connections before use; see Remote PostgreSQL
tcp_keepalivesboolTrueEnable libpq TCP keepalives on broker and child-process connections; see Remote PostgreSQL
tcp_keepalives_idleint30Idle seconds before the first keepalive probe (libpq keepalives_idle)
tcp_keepalives_intervalint10Seconds between keepalive probes (libpq keepalives_interval)
tcp_keepalives_countint3Unacknowledged probes before the connection is dropped (libpq keepalives_count)
echoboolFalseEcho SQL statements to logs
pool_recycleint1800Recycle connections after N seconds
postgresql+psycopg://user:password@host:port/database

Components:

  • postgresql+psycopg - Driver (psycopg3)
  • user:password - Credentials
  • host:port - Server location (default port: 5432)
  • database - Database name
PostgresConfig(
database_url="postgresql+psycopg://postgres:postgres@localhost:5432/horsies_dev",
)
PostgresConfig(
database_url="postgresql+psycopg://app:secret@db.example.com:5432/production",
pool_size=10,
max_overflow=20,
pool_timeout=30,
pool_recycle=3600,
)
import os
PostgresConfig(
database_url=os.environ["DATABASE_URL"],
)

Use a split configuration when the main SQL URL points to PgBouncer transaction pooling. database_url is used for ordinary SQL work. session_database_url is used for schema setup and PostgreSQL LISTEN/NOTIFY, which need persistent session semantics.

from horsies import AppConfig, PostgresConfig
config = AppConfig(
broker=PostgresConfig(
database_url=os.environ["DATABASE_URL_POOLED"],
session_database_url=os.environ["DATABASE_URL_DIRECT"],
pgbouncer_transaction_mode=True,
),
)

Some managed providers, including PlanetScale Postgres, expose separate pooled and direct connection strings. Treat provider-specific ports and hostnames as an example, not a Horsies requirement. Check your provider dashboard or current provider docs and copy the pooled transaction URL into database_url and the direct/session-capable URL into session_database_url.

Rules:

  • Workers require session_database_url when pgbouncer_transaction_mode=True.
  • Syce requires the session URL for real-time updates.
  • PgBouncer-only worker deployment is unsupported.
  • Horsies does not infer PgBouncer mode from port numbers.

The broker uses SQLAlchemy’s async connection pool:

  • pool_size: Base number of persistent connections
  • max_overflow: Additional connections created under load (temporary)
  • pool_timeout: How long to wait if all connections are busy
  • pool_recycle: Close and recreate connections after this many seconds

pool_size and max_overflow size the general broker used by producers, web processes, schedulers, and app-level APIs. Workers use the smaller worker_pool_size and worker_max_overflow profile by default so an idle or moderately loaded worker does not reserve a web-sized pool.

Deploymentpool_sizemax_overflow
Development2-55
Small production5-1010-20
High traffic10-2020-40

For direct PostgreSQL connections, budget a worker roughly as:

worker_pool_size + worker_max_overflow
+ 2 session/LISTEN connections
+ processes * worker_child_pool_max_size
+ task-code database connections

With the defaults and --processes=8, the framework ceiling is about 3 + 2 + 2 + 8 * 2 = 23 direct connections, before application task code. Idle child processes keep no database connection by default because worker_child_pool_min_size=0; connections are opened lazily when task lifecycle work starts. Set worker_child_pool_max_size=1 only after testing heartbeat and task lifecycle latency for your workload; 2 leaves one connection for task lifecycle work and one for heartbeat overlap.

When using PgBouncer transaction pooling, those client-side pool slots are multiplexed onto fewer server backends. Connections are still not safe to share between OS processes; Horsies creates child pools after worker processes start and uses a non-inheriting start method for replacement executors after startup.

The broker creates two connection types:

  1. Async engine (SQLAlchemy): For queries, inserts, updates
  2. LISTEN/NOTIFY (psycopg): For real-time notifications

By default both use database_url. With split PgBouncer configuration, ordinary SQL uses database_url, while schema setup and LISTEN/NOTIFY use session_database_url.

The broker includes automatic health checks:

  • Proactive connection monitoring
  • Auto-reconnect on connection loss
  • Exponential backoff for reconnection attempts

On first use, the broker creates required tables:

  • horsies_tasks - Task storage
  • horsies_heartbeats - Liveness tracking
  • horsies_worker_states - Worker monitoring
  • horsies_schedule_state - Scheduler state

Uses PostgreSQL advisory locks to prevent race conditions during schema creation.