DE//AGENT
Explore
Tutorial Lesson 3 of 10 · The Streaming Series community intermediate

Make the sink idempotent so replays never duplicate rows

The core producer-side reliability skill for streams: you cannot stop replays, so the sink must converge. Deduplicate by a business key and event time within the watermark, MERGE on the key, and rank change events by (lsn, ts_ms) — never by arrival time.

At a glance

When Kafka → Delta/Iceberg/warehouse; Any replay-prone stream
Where Streaming
Why You cannot stop replays, so the sink must converge to the same state no matter how many times a batch arrives.

Learning objectives

  • Make the sink idempotent rather than trying to make the source exactly-once
  • Deduplicate by a business key + event time within the watermark
  • Rank change events deterministically by (lsn, ts_ms), never by arrival time

What you need

  • A stream writing into Delta/Iceberg or a warehouse table with a stable business key
  • [object Object]

Steps

  1. Name the failure mode — streaming sinks either duplicate on replay or lose rows on failure. (from pattern-streaming-dedup-upsert )
  2. Make the sink idempotent rather than trying to make the source exactly-once. (from pattern-streaming-dedup-upsert )
  3. Deduplicate by a business key + event time within the watermark, then MERGE (upsert) on the key — a replay recomputes the same rows and merges to the same state, idempotent by construction. (from pattern-streaming-dedup-upsert )
  4. Order by event time, not arrival time. (from pattern-streaming-dedup-upsert )
  5. For change streams, rank by `(lsn, ts_ms)` — never by ingestion time; ranking by LSN makes re-running the same micro-batch produce the same table. (from skill-cdc-merge-designer )
  6. Deduplicate before merging — keep the latest event per primary key with a window function, then merge once. (from skill-cdc-merge-designer )
  7. Plan for checkpoint loss — checkpoint lost means duplicates, and the fix is making the sink idempotent. (from project-kafka-spark-delta )

Check yourself

  • Replaying the same micro-batch twice produces an identical table — the merge is idempotent by construction.
  • The dedupe key is a business key plus event time within the watermark, not an offset and not an arrival timestamp.

Worked example

The pattern in one line — from pattern-streaming-dedup-upsert

stream ─▶ [windowed dedupe by key] ─▶ MERGE (upsert on key) ─▶ table

Ordering and idempotency rules — from skill-cdc-merge-designer

Events arrive out of order. Rank by `(lsn, ts_ms)` — never by ingestion time.

Re-running the same micro-batch must produce the same table. Ranking by LSN
guarantees this; ranking by arrival time does not.

Sourced from

Security scan

Last sweep 2026-10-24: 0 finding(s), 0 marked as examples.

Common gotchas

  • Ranking by arrival time breaks idempotency — rank by (lsn, ts_ms) or event time.
  • The pattern requires a stable business key and event time; state cost is the price.
  • A pure append-only event log with no dedup need does not need this pattern.

Production considerations

  • Correct under replay with no reliance on perfect exactly-once — that is the goal, and it is achievable.
  • MERGE aggregates on the window key so replays converge.

Diagram

idempotent sink
   |
   |- stream arrives (at-least-once, replays included)
   |
   |- windowed dedupe by business key + event time
   |
   |- MERGE (upsert on key) --> table converges
   |
    --> replay the same batch --> same rows --> same state

Registry stats

Verification community · Oct 21, 2026
Completeness 81
Trust multiplier ×1.25
Composite 34.15 · adequate
Freshness 3 days since last source check
Difficulty intermediate
Version 0.1.0
License CC-BY-4.0
Security flags none