Asyncio-based PostgreSQL load generator supporting pluggable workloads, YAML/JSON configuration, and real-time metrics (throughput & latency percentiles).
- Async execution using
asyncpg - Concurrency & (optional) rate limiting
- Pluggable workload modules
- Latency histogram & percentile calculation (p50, p90, p95, p99, max)
- Graceful shutdown on CTRL+C
- CLI with config file or flags
- Partitioned table workload (
partition_insert) & pruning inspection command
Create & activate a virtual environment, then install:
pip install -e .[dev]Example config in config/example.yaml:
dsn: postgresql://user:password@localhost:5432/postgres
workload: sample_insert
concurrency: 8
duration_seconds: 30
target_rate: 0 # 0 means unlimited
report_interval: 2
table: loadgen_events
payload_size: 128Run:
pgloadgen run --config config/example.yamlOr override via flags:
pgloadgen run --dsn postgresql://user:pass@localhost/db --workload sample_insert --concurrency 4 --duration 10Before launching a large multi-million row run, execute a brief trial to verify connectivity, table creation, and metrics output:
pgloadgen run --dsn postgresql://user:password@localhost:5432/postgres \
--workload sample_insert --concurrency 2 --duration 5You should see periodic lines with ops, throughput, and latency percentiles, then a summary.
Workloads live under pgloadgen/workloads/. Implement BaseWorkload and register with @register_workload("name").
fk_chain_insert creates 50 tables named {table}_00 .. {table}_49.
Each table has:
- a
BIGSERIALprimary key (id) - a
DATEfield (event_date) - 5-10 columns (this workload uses 8)
- a deferrable FK using either a straight chain or a hub-and-spoke (star)
Each operation inserts one row into each table inside a single transaction.
Topologies:
chain(default):{table}_01references{table}_00,{table}_02references{table}_01, ...star: ~10% of tables are hubs; remaining tables reference a hub (hub sizes vary randomly)
Example:
pgloadgen run --dsn postgresql://user:password@host:5432/postgres \
--workload fk_chain_insert --concurrency 4 --duration 10 \
--table loadgen_fk --payload-size 256Star topology example:
pgloadgen run --dsn postgresql://user:password@host:5432/postgres \
--workload fk_chain_insert --fk-topology star --concurrency 4 --duration 10 \
--table loadgen_fk --payload-size 256If you change topology after tables already exist, Postgres will keep the old FK constraints. Use a new --table prefix or pass --fk-reset to drop/recreate the 50 tables.
partition_insert creates monthly RANGE partitions on event_date over a configurable span and inserts wide rows (10 columns) with random distribution. Optionally create a second table (--second-table) to split load across two parents.
Columns: id, event_date, created_at, category, region, user_id, amount, status, payload, extra.
Key options:
--operations N– precise total rows (use for 65,000,000 target)--start-date YYYY-MM-DD&--partition-span-days D– define partition range--second-table– also produce{table}_b
Example (65M rows, two tables, 6 months, 16 workers):
pgloadgen run --dsn postgresql://user:pass@localhost/db \
--workload partition_insert --concurrency 16 \
--operations 65000000 --table events_range --payload-size 64 \
--start-date 2025-01-01 --partition-span-days 180 --second-tableUse the helper command:
pgloadgen partition-test --dsn postgresql://user:pass@localhost/db \
--table events_range --from 2025-02-01 --to 2025-03-01 --filter-category 3Prints truncated JSON plan and an estimate of partitions scanned. operations: 0 # set >0 to stop after N operations instead of duration
Latency recorded in microseconds into a fixed histogram. Percentiles derived on demand; overhead is low for high throughput.
Run tests:
pytest- CSV/JSON export of summary
- Prometheus metrics endpoint
- More workload templates (read/update/mixed)
MIT