PostgreSQL Integration Guide

PostgreSQL is the recommended backend for production, multi-instance, and high-concurrency deployments — a real server with connection pooling and strong durability. For time-series-heavy workloads, see the TimescaleDB guide, which reuses this same config.

Build

PostgreSQL is behind a cargo feature (adds the sqlx Postgres driver):

cargo build -p reflow_tracing --features postgres
# or every durable backend at once:
cargo build -p reflow_tracing --features all-backends

Run a database

docker run -d --name reflow-pg \
  -e POSTGRES_DB=traces -e POSTGRES_USER=reflow -e POSTGRES_PASSWORD=secret \
  -p 5432:5432 postgres:16

Configure

Set storage.backend to postgres (or postgresql) and a [storage.postgres] block. All fields:

[storage]
backend = "postgres"

[storage.postgres]
connection_url       = "postgresql://reflow:secret@localhost:5432/traces"
max_connections      = 20
min_connections      = 5
acquire_timeout_secs = 5

Schema

Created automatically on startup (the traces table + indexes) — no manual DDL. Each FlowTrace is one row: the full trace as a (zstd-compressed when large) JSON BYTEA blob, plus denormalized columns:

columntypepurpose
trace_id (PK)TEXTidentity
flow_id, execution_id, statusTEXTquery filters
start_time, end_timeBIGINTtime-range queries
event_countBIGINTstats without loading the blob
data, compressed, size_bytesBYTEA/BOOL/BIGINTpayload

Stores are synchronous with an INSERT … ON CONFLICT (trace_id) DO UPDATE upsert (idempotent re-finalize), and indexes cover flow_id, execution_id, status, start_time.

Verify

reflow_tracing            # logs: "Initialized storage backend: postgres"

psql "$CONN" -c '\dt'                          # traces
psql "$CONN" -c 'SELECT count(*) FROM traces;'

Run the bundled integration test against a live instance:

REFLOW_TEST_POSTGRES_URL="postgresql://reflow:secret@localhost:5432/traces" \
  cargo test -p reflow_tracing --features postgres --test storage_backends \
  postgres_store_query_delete_roundtrip

Operations

  • Pooling: size max_connections to your collector concurrency; sqlx manages the pool with acquire_timeout_secs.
  • Retention: schedule DELETE FROM traces WHERE start_time < EXTRACT(EPOCH FROM now() - INTERVAL '30 days'), or use TimescaleDB for native retention.
  • Backups: standard pg_dump / streaming replication / PITR.
  • Scale: read replicas, partitioning. If your queries are dominated by time ranges, TimescaleDB's hypertable is a better fit.

Troubleshooting

  • PostgreSQL backend not compiled in — rebuild with --features postgres.
  • PostgreSQL storage config missingbackend = "postgres" but no [storage.postgres] block.
  • Connection refused / auth failed — check connection_url, that the server is reachable, and credentials.