How PostgreSQL Handles CDC Replay Safety
May 2026
PostgreSQL replay safety depends on a staged apply path that checks row freshness before every upsert.
Short Answer
PostgreSQL handles CDC replay safety with a staging-then-apply pattern. Skippr loads incoming changes into a staging table and then uses INSERT ... ON CONFLICT with a WHERE clause that only updates the target row when the staging _skippr_order_token is newer than the one already stored.
Deletes are protected through companion tombstone tables. A replayed insert for a deleted business key only wins if its order token is newer than the delete token that was already recorded, which blocks ghost resurrection after retries.
Why Teams Struggle with This
This matters because PostgreSQL is familiar enough that teams sometimes assume normal upserts are sufficient. They are not. A plain ON CONFLICT without a freshness check can happily replace current state with an older replay if the same key arrives again.
- Staging is part of the correctness model, not disposable plumbing.
- A replay-safe upsert needs both a business key and an order-token comparison.
- Delete history has to stay visible somewhere or replayed inserts become hard to reason about.
- The destination table should be the stable object that downstream SQL reads.
How Skippr Handles It
Skippr makes the PostgreSQL contract explicit by creating the _skippr_order_token TEXT column and the skippr_tombstones companion automatically for CDC-managed tables. The staging table exists to bulk load efficiently while preserving the newer-wins rule during apply.
A concrete example: inventory sku = A1 is updated from quantity = 4 to quantity = 6, then a retry replays the older quantity = 4 change. The ON CONFLICT apply step compares tokens and leaves quantity = 6 in place because that row is newer.
- PostgreSQL CDC uses a staging table plus INSERT ... ON CONFLICT apply pattern.
- The update branch is guarded by _skippr_order_token comparison.
- Tombstone tables prevent older inserts from reviving deleted rows.
- Bulk loading and replay safety live in the same destination flow.
What the First Useful Version Looks Like
PostgreSQL replay safety is less about a special feature name and more about disciplined apply logic.
If the staging path, key choice, and newer-wins comparison are clear, retries stop being dangerous.
