Watermark (Event-Time Processing)
Practical example
A Flink job aggregating clickstream events per 1-minute window uses a bounded out-of-orderness watermark strategy (max timestamp minus 10 seconds); one Kafka partition goes idle, so withIdleness() excludes it from the min-watermark calculation, preventing the entire pipeline's watermark — and thus all downstream windows — from stalling indefinitely.
Watermarks decouple event-time (when something actually happened) from processing-time (when the system observes it). Without them, a windowed aggregation has no principled way to decide when a window is ‘done’ — you’d either wait forever or arbitrarily flush and drop late data. A watermark generator, typically attached per-partition/per-shard at the source, emits `W(t)` based on either a periodic strategy (heuristic, e.g. max observed timestamp minus a bounded out-of-orderness slack) or a punctuated strategy (an explicit marker embedded in the source protocol, e.g. Kafka headers or a sentinel record). The processor’s global watermark for a given operator is computed as the minimum of the watermarks across all upstream input partitions — a single stalled or skewed partition holds back the entire downstream watermark, which is the most common source of unbounded state growth in production pipelines.
- Idleness detection: if a partition stops producing data (not just late data, but zero data), naive min-watermark logic stalls forever. Systems like Flink require explicit
withIdleness()markers so idle sources are excluded from the min-watermark calculation. - Allowed lateness / grace periods: because watermarks are heuristics, not guarantees, engines expose a secondary lateness bound. Events arriving after the watermark but within this bound trigger window recomputation (retraction + re-emission); events beyond it are routed to a
side outputor dropped, with metrics emitted for observability. - Watermark holds: in engines like Beam/Dataflow, a stateful transform can explicitly withhold advancing its output watermark until buffered state is flushed, preventing downstream stages from prematurely closing windows that depend on that state.
- Checkpoint coupling: watermark position is checkpointed alongside operator state so that on failure recovery, exactly-once semantics hold — replaying the log must reconstruct the same watermark progression, not just the same data.
The architectural tension watermarks expose is latency vs. completeness: a tight out-of-orderness bound closes windows quickly but increases the volume of late-arriving retractions; a loose bound reduces retractions but inflates end-to-end latency and operator state retention (since windows must stay open longer). This is functionally the streaming analogue of the CAP/PACELC tradeoff — you’re choosing how much you’re willing to wait for a bounded but unknowable amount of disorder before committing to an answer.
Multi-stage DAGs compound this: watermarks propagate downstream only after being recomputed at each shuffle boundary, so a deep pipeline with several keyed re-partitions accumulates latency proportional to the sum of each stage’s out-of-orderness slack. Poorly tuned watermark strategies are a leading cause of both OOM-driven task failures (unbounded window state) and silent data loss (aggressive lateness bounds dropping legitimate stragglers) — and because the failure mode is a missing or malformed result rather than an exception, it is notoriously difficult to catch without dedicated watermark-lag dashboards and per-partition skew alerting.