Snapshots
A snapshot is a point-in-time copy of a stream table's contents, stored as an ordinary PostgreSQL table. Snapshots let you back up derived state, bootstrap a replica, build deterministic test fixtures, or compare two refresh runs without having to re-derive the data.
Available since v0.27.0
Why snapshots?
A stream table's contents are derived — pg_trickle can always recompute them from the source tables. But recomputation is not free, and there are operational situations where having a frozen copy is cheaper, safer, or simpler:
- Replica bootstrap. When you stand up a new read replica or a fresh environment, you can restore from a snapshot in seconds instead of waiting for an initial full refresh that may take minutes or hours on a large dataset.
- Point-in-time forensics. Take a snapshot before a risky migration or a suspicious incident; compare it to the live stream table later.
- Test fixtures. Snapshot a stream table from a representative environment and check it into a test database.
- Cheap rollback. If a defining-query change goes wrong, restore from the most recent snapshot while you investigate.
Quickstart
Take a snapshot
SELECT pgtrickle.snapshot_stream_table('order_totals');
-- pgtrickle.snapshot_order_totals_1735689421000
The function returns the fully-qualified name of the new snapshot
table. By default snapshots live in the pgtrickle schema and are
named snapshot_<table>_<epoch_ms>.
You can choose your own name with the optional second argument:
SELECT pgtrickle.snapshot_stream_table(
'order_totals',
'archive.order_totals_2026_q1'
);
List snapshots
SELECT * FROM pgtrickle.list_snapshots();
Or filter to a single stream table:
SELECT * FROM pgtrickle.list_snapshots('order_totals');
Restore from a snapshot
SELECT pgtrickle.restore_from_snapshot(
'order_totals', -- stream table to restore into
'pgtrickle.snapshot_order_totals_1735689421000' -- snapshot table
);
After a restore, pg_trickle reinitialises the stream table's frontier so that the next refresh reads only changes that occurred after the snapshot was taken.
Drop an old snapshot
SELECT pgtrickle.drop_snapshot('pgtrickle.snapshot_order_totals_1735689421000');
What's in a snapshot
The snapshot table is a plain PostgreSQL heap table with the same
columns as the stream table, including the hidden __pgt_row_id
column. That is what allows a restore to map snapshot rows back to
their stable identities.
Because the snapshot is an ordinary table, you can:
- Back it up with
pg_dump, copy it elsewhere withpg_dump -t, or move it across databases with\copy. - Inspect it freely with regular SQL.
- Add indexes for read-side workloads (the snapshot is independent of the live stream table).
Operational patterns
Periodic archival
-- Every night, snapshot a slowly-changing dimension
SELECT pgtrickle.snapshot_stream_table(
'customer_360',
format('archive.customer_360_%s', to_char(now(), 'YYYY_MM_DD'))
);
-- Keep only the last 30 days
SELECT pgtrickle.drop_snapshot(snapshot_table)
FROM pgtrickle.list_snapshots('customer_360')
WHERE created_at < now() - interval '30 days';
Replica bootstrap
-- On the source: pg_dump the snapshot
pg_dump -t pgtrickle.snapshot_order_totals_1735689421000 mydb > snap.sql
-- On the replica: restore the snapshot, then reattach
psql replicadb < snap.sql
SELECT pgtrickle.restore_from_snapshot(
'order_totals',
'pgtrickle.snapshot_order_totals_1735689421000'
);
Disaster recovery
Combine snapshots with regular pg_dump of the source tables.
After a restore, pg_trickle's frontier tracking ensures the stream
table will catch up correctly when CDC resumes.
Caveats
- Snapshots are not coordinated across multiple stream tables. If you need a consistent view across several stream tables, take them inside a single transaction and rely on PostgreSQL's MVCC isolation.
- Snapshots do not freeze the source tables. The "as-of" time is determined by the most recent refresh of the stream table at the moment you take the snapshot.
- A restore reinitialises the frontier — if you want the stream table to replay changes between the snapshot time and now, the source CDC slots / change buffers must still hold those entries. Otherwise, expect a full refresh on the next cycle.
See also: Backup & Restore · Replica Bootstrap & PITR Alignment (Patterns) · SQL Reference – Lifecycle