Partitions¶
The events table is range-partitioned by timestamp on a monthly cadence. This was introduced in migration 0013 to handle high-ingestion workloads.
Why partition?¶
| Benefit | Mechanism |
|---|---|
| 10–100× faster queries | Partition pruning skips irrelevant months |
| Instant retention | DROP TABLE events_2026_01 instead of DELETE |
| Smaller indexes | Indexes are per-partition |
| Better vacuum | Tuned autovacuum per partition |
Layout¶
CREATE TABLE events (
id BIGSERIAL,
timestamp TIMESTAMPTZ NOT NULL DEFAULT now(),
… ,
PRIMARY KEY (id, timestamp)
) PARTITION BY RANGE (timestamp);
Monthly partitions:
The pipeline creates 13 future partitions ahead of time during migration and on startup, so there is no lag when the month rolls over.
Partition Pruning¶
The API always bounds queries by time (default: last 30 days). Example access path:
EXPLAIN SELECT * FROM events
WHERE timestamp >= '2026-08-01' AND timestamp < '2026-09-01'
AND severity = 'ERROR';
Only events_2026_08 is scanned — no full-table scan.
Composite Primary Key¶
Because the partition column must be part of the primary key, the PK is (id, timestamp) instead of (id). Lookups by id alone therefore also filter on time.
Retention / Purging¶
Retention scripts (backend/scripts/retention.py) list partitions and drop those older than the retention window.
Auto-Partition Maintenance¶
- Migration
0013pre-creates 13 months - A startup task in the pipeline recreates/extends the horizon if needed
autovacuum_vacuum_scale_factor = 0.01per partition keeps tables healthy under load
Monitoring¶
-- Current partitions
SELECT child.relname, pg_size_pretty(pg_total_relation_size(child.oid))
FROM pg_inherits JOIN pg_class child ON inhrelid = child.oid
WHERE inhparent = 'events'::regclass
ORDER BY child.relname;
Related¶
- Indexes — indexes inherited per partition
- Migrations — revision 0013 implementation