A mobile client goes through a tunnel, and the count for the 10:00 minute is
computed before its event exists.
Rows are in arrival order. Watermark = (max event time seen) - 2 minutes,
so it is advanced by data, never by the wall clock.
event_time processing_time watermark what the pipeline sees
afterwards
---------- --------------- ---------- -----------------------------------
10:00:12 10:00:13 09:58:12 window 10:00, now 1 event, open
10:00:41 10:00:42 09:58:41 window 10:00, now 2 events, open
10:01:03 10:01:04 09:59:03 window 10:01 opens. 10:00 still
open: watermark is short of 10:01:00
10:02:20 10:02:21 10:00:20 window 10:02. 10:00 still open
10:03:05 10:03:06 10:01:05 watermark passes 10:01:00, so
window 10:00 CLOSES and emits 2
10:04:10 10:04:11 10:02:10 window 10:04
10:00:55 10:07:30 10:02:10 LATE. The tunnel event arrives
unchanged 6.5 min after its own event time.
Window 10:00 closed 4 min ago, and
this event cannot raise the maximum,
so the watermark does not move
Windowing by processing time instead:
window 10:00 = 2 events window 10:07 = 1 event <- wrong minute
Processing-time windows are always internally consistent and always wrong about
the world: the tunnel event is attributed to 10:07, so a traffic graph shows a
dip at 10:00 and a spike seven minutes later that no user caused. Event-time
windows are right about the world and force you to decide when to stop waiting.
The watermark is that decision, expressed as a claim: "I believe I have seen
everything up to time T." Read the watermark column carefully, because this is
where the two clocks get conflated even by people who can define both. The
watermark here is derived from the maximum event time seen minus two minutes,
so it advances only when a newer event arrives — not when two minutes of wall
clock pass. The 10:00 window does not close two minutes after 10:00:41; it
closes when an event stamped 10:03:05 or later turns up, because only then does
the watermark reach 10:01:00.
That distinction has a consequence worth stating out loud: on a stream that goes
quiet, windows do not close at all. If the last event in this table had been the
one at 10:01:03, the watermark would have stalled at 09:59:03 and the 10:00
window would still be open tonight, holding its state and emitting nothing.
Production pipelines deal with this by emitting an idle-source watermark after a
period of silence, which is a deliberate decision to trust the wall clock in the
absence of data, and it is the one place where the two clocks are allowed to
meet.
What happens to the late event is the part candidates skip, and there are only
three honest options. Drop it and record the count of drops as a metric, which is
fine for approximate telemetry and unacceptable for billing. Emit a correction —
keep window state around for an allowed lateness and re-emit an updated count,
which requires every downstream consumer to handle a retraction. Or route it to a
side output for a batch process to reconcile later, which is the pragmatic answer
when the streaming path feeds a dashboard and a batch path owns the ledger.
The tuning tension is direct: a longer watermark delay catches more late data and
delays every result, and it holds window state in memory for longer. There is no
setting that is both complete and immediate, and saying so is the answer.