← All cheatsheets
Data Engineer · #028 · August 20, 2026 · 3 min read

How do you make a data pipeline safe to rerun?

One network timeout, one automatic retry, and every order loaded twice. Why plain INSERT is a loaded gun, and the three patterns (MERGE, partition overwrite, run-id dedupe) that make reruns free.

Get the free PDF

One page, print-ready, free to share. No signup needed.

Download the PDF

02:59, run 1 inserts the orders, ok. 03:00, a timeout, retrying. 03:04, run 2 inserts the orders, ok. The table went from 1,204 rows to 2,408: every order loaded twice, and if anything downstream charges customers, so did the charges.

A retry should be boring. Here is why this one was not, and the three patterns that fix it.

The crime scene

Nothing here is broken. That is the problem: it works twice.

-- runs every night at 03:00
INSERT INTO orders
SELECT * FROM staging_orders;

-- timeout after commit, before ack.
-- the scheduler does what
-- schedulers do: it retries.

The retry is not the bug. Retries are normal, networks time out, schedulers reschedule. A pipeline that cannot survive one retry is the bug.

Idempotent, in one picture

Run it once, twice, or five times: the table ends up identical.

run 1run 2run 3
naive INSERT1,204 rows2,408 rows3,612 rows
idempotent load1,204 rows1,204 rows1,204 rows

Formally: f(f(x)) = f(x). Practically: reruns are free, and 3am pages stop.

Fix 1: MERGE on a key

Let the natural key decide: update if it exists, insert if it does not.

MERGE INTO orders o
USING staging_orders s
  ON o.order_id = s.order_id
WHEN MATCHED THEN UPDATE
  SET amount = s.amount
WHEN NOT MATCHED THEN
  INSERT VALUES (s.*);

A rerun matches every row and changes nothing. Postgres spells it INSERT ... ON CONFLICT DO UPDATE. Same idea, same safety.

Fix 2: overwrite the slice

Delete the day, reload the day. The rerun rewrites the same slice.

BEGIN;
DELETE FROM orders
 WHERE order_date = '2026-08-18';
INSERT INTO orders
SELECT * FROM staging_orders
 WHERE order_date = '2026-08-18';
COMMIT;

The warehouse version is INSERT OVERWRITE PARTITION. One transaction, or you traded one bug for another: a crash between the DELETE and the INSERT would leave the day empty.

A third variant for streams and event tables: stamp every load with a run id and dedupe on it downstream. Same property, different layer.

The rerun checklist

Before you ship a pipeline, ask it these four questions:

  • Natural key? Something stable to merge on. Not the auto-increment id.
  • Scoped runs? A run owns a slice (a date, a batch id) it can rewrite.
  • Safe retries? Run it twice in staging. Diff the row counts.
  • Late data? Yesterday reruns when stragglers arrive. The same property saves you.

If you cannot rerun yesterday without fear, you do not have a pipeline. You have a ritual.

The takeaway

Plain INSERT plus a retry is how 1,204 orders become 2,408 at 3am. MERGE on a natural key, overwrite the partition, or dedupe on a run id: any of the three makes reruns boring, which is the whole point. All three patterns and the rerun checklist fit on the print-ready PDF above. Pin it near the pager.

Frequently asked questions

What does idempotent mean in data engineering?
A pipeline is idempotent when running it once, twice or five times leaves the table in the identical state. Formally f(f(x)) = f(x); practically, reruns are free and duplicate-data incidents stop. A naive INSERT is the opposite: every retry adds the same rows again.
Why did my pipeline load duplicate data after a retry?
The classic sequence: the load commits, the acknowledgment times out on the network, and the scheduler does what schedulers do and retries. The second run inserts the same staging rows again, so 1,204 orders become 2,408. The retry is not the bug; a pipeline that cannot survive one is the bug.
How does MERGE make a pipeline idempotent?
MERGE matches incoming rows against the target on a natural key like order_id: if the row already exists it updates, if not it inserts. A rerun matches everything and changes nothing. Postgres spells the same idea INSERT ... ON CONFLICT DO UPDATE.
What is the partition overwrite pattern?
Each run owns a slice of the data, typically a date: delete that day's rows and reload them inside one transaction, so a rerun rewrites the same slice instead of appending to it. Warehouses spell it INSERT OVERWRITE PARTITION. The transaction matters: without it you traded one bug for another.

Get the free PDF

One page, print-ready, free to share. No signup needed.

Download the PDF

More cheatsheets