Archiving Large Postgres Tables
Archive time-partitioned PostgreSQL tables into Parquet and Apache Iceberg storage with pg_mooncake, dropping cold partitions from the rowstore while retaining full history in the columnstore.
pg_mooncake’s columnstore mirror writes data as compressed Parquet files (on local disk or S3 / Cloudflare R2 object storage), providing cost-effective, durable history while PostgreSQL heap storage only maintains hot recent partitions.
Architecture Pattern
The partition archiving pattern follows three rules:
- Create a columnstore mirror (
CALL mooncake.create_table) of a time- or range-partitioned PostgreSQL table. - Periodically
DROPorTRUNCATEold partitions from the PostgreSQL rowstore once they age out of your hot operational window. - Retain complete history in the columnstore mirror—
pg_mooncakeintentionally ignoresDROP TABLEandTRUNCATEevents on child partitions of a partitioned source table, so all historical rows remain queryable in the columnstore!
Step-by-Step Walkthrough
Below is a complete example using a range-partitioned transactions table.
Step 1: Create a Partitioned Rowstore Table
Step 2: Create a Columnstore Mirror
Step 3: Add First Partition & Insert Data
Step 4: Drop the Old Partition from Rowstore
When transactions_p1 is cold and you want to reclaim PostgreSQL disk space immediately:
Step 5: Query Historical Data in the Columnstore
Even though transactions_p1 was dropped from the rowstore, all three rows remain intact in transactions_archive:
Step 6: Continue with New Partitions
Best Practices
- Never re-insert overlapping keys into dropped partition ranges: After dropping or truncating a rowstore partition, do not recreate a partition covering the same key range and insert conflicting rows, as the historical rows from the dropped partition are still retained in the columnstore.
- Always use declarative partitioned tables for archiving:
pg_mooncakesafely ignores partition drops and truncates on partitioned tables. - Pair with S3 / R2 object storage: Set
mooncake.default_bucket = 's3://...'before creating the mirror so archived partitions are stored directly in low-cost cloud object storage. See Iceberg & Object Storage.