sehaj/learn
System design/11 Counting and analytics

11.02 · concept · 10 min

Time-window aggregation

Counting per minute sounds simple until events arrive late and out of order. Tumbling, sliding and session windows, event time versus processing time, watermarks that decide when a window is done, and what to do with stragglers.

The problem: which minute does a click belong to?

Snip's chart shows clicks per minute for each link. A click happened at 14:02:58 on a phone in a train tunnel. The phone sends it at 14:09:40, when it gets signal. The CDN's edge logs (06.07 CDNs in depth) arrive in batches every few minutes. A consumer was restarted and replays two minutes of events.

So the click must be counted in the 14:02 bucket, not 14:09, and the 14:02 bucket can't be called final at 14:03, because clicks for it may still be on their way. Windowing is the art of answering "how many per minute?" when data is late and out of order.

Event time versus processing time

  • Processing time: when the system sees the event. Easy, always moving forward, and wrong for analytics: a backlog or a replay shifts clicks into the wrong minute.
  • Event time: when the event actually happened, carried in the event. Right for analytics, but events arrive in any order.

Snip counts by event time. The redirect stamps each click with the server's time when it served the 302, so phones' clocks don't matter, but delays in the pipeline still do.

Three shapes of window

WindowDefinitionSnip use
TumblingFixed size, no overlap: 14:00–14:01, 14:01–14:02 …Clicks per minute for the chart
SlidingFixed size, moving by a smaller step: the last 5 minutes, every minute"Trending now": clicks in the last 5 minutes
SessionActivity separated by gaps of inactivity; no fixed start or endA visitor's burst of clicks on one owner's links

Apache Flink's documentation puts it precisely: tumbling windows have a fixed size and don't overlap; sliding windows overlap when the slide is smaller than the window; session windows close when no elements arrive for a period.

Watermarks: when is a window done?

A watermark is the stream's own estimate of event-time progress. In Flink's definition, a watermark at time t declares that event time has reached t: there should be no more events with timestamps at or before t. When the watermark passes the end of a window, the window can be emitted.

The watermark is a judgement call. Snip sets it to "the latest event time seen, minus 5 seconds", because measured delays through the pipeline are almost always under 5 seconds:

  • Too eager (watermark close to the newest event): results appear fast, but more events arrive after their window was emitted.
  • Too cautious (watermark minutes behind): fewer late events, but every chart is minutes behind.

Google's Dataflow paper frames this as a three-way trade-off you can't avoid with unbounded, out-of-order data: correctness, latency and cost.

Late events

Some events still arrive after the watermark. Choose what happens to them:

  1. Update the window. Keep each window's state for an allowed lateness after it closes, and emit a corrected count if a late event arrives. Downstream must accept updates, which per-minute rows in a database do naturally.
  2. Drop, but count the drops. Past the allowed lateness, discard the event from the live windows, increment a "too late" metric, and let the nightly batch over raw events fix the numbers (11.06 Raw events vs derived data).
ts
type Click = { code: string; at: number }; // event time, ms

export class MinuteCounts {
  counts = new Map<number, number>(); // window start -> clicks
  dropped = 0;
  private maxSeen = 0;
  constructor(readonly allowedLatenessMs = 120_000) {}

  get watermark() {
    return this.maxSeen - 5_000; // assume events are at most 5 s out of order
  }

  add(c: Click) {
    this.maxSeen = Math.max(this.maxSeen, c.at);
    const window = Math.floor(c.at / 60_000) * 60_000;
    if (window + 60_000 + this.allowedLatenessMs <= this.watermark) {
      this.dropped++; // too late even for an update: count it elsewhere
      return;
    }
    this.counts.set(window, (this.counts.get(window) ?? 0) + 1); // a late event updates an already-emitted window
  }

  /** Windows whose end is behind the watermark are complete enough to show. */
  closed() {
    return [...this.counts].filter(([w]) => w + 60_000 <= this.watermark);
  }
}

Tested with ten minutes of clicks, one a second, shuffled by up to a few seconds, plus one click from a phone that arrived about nine and a half minutes late: nine windows closed with about 60 clicks each, the jittered clicks were counted in their correct minutes, and the very late click was dropped and counted as dropped (600 of 601 counted), leaving the nightly batch to account for it.

Seen in production

Google

2015

The Dataflow Model

Akidau and colleagues at Google argue that for massive-scale, unbounded, out-of-order data, one can never fully optimise along all of correctness, latency and cost, and present a model that lets practitioners choose the trade-offs for each use case, with windowing by event time, watermarks and triggers that decide when results are emitted and refined. They argue that batch, micro-batch and streaming systems can provide equal correctness, rather than an execution engine dictating semantics.

What we foundWith unbounded, out-of-order data you can't fully optimise correctness, latency and cost at once; the model lets each pipeline choose.

The Dataflow Model: A Practical Approach to Balancing Correctness, Latency, and Cost in Massive-Scale, Unbounded, Out-of-Order Data Processing (VLDB 2015)vldb.org ↗

Apache Flink

Docs

Windows and watermarks

Flink distinguishes processing time, the system time of the machine running an operation, from event time, the time each event occurred on its producing device. A watermark at time t declares that event time has reached t, meaning there should be no more elements with timestamps at or before t; elements arriving after that are late. Its windows documentation defines tumbling, sliding and session windows, and keeps a window's state until the watermark passes its end plus the allowed lateness.

What we foundEvent time is when it happened; a watermark says no older events should still arrive; anything after it is late.

Timely Stream Processing (Apache Flink documentation)nightlies.apache.org ↗
Predict first

Snip's per-minute chart uses processing time. After a 20-minute consumer outage, the backlog is processed in 3 minutes. What does the chart show?

Commit to an answer before you read on.

Recap

Interview questions

SeniorfreqGood to knowConcept

How do you compute per-minute counts from a stream where events can arrive late and out of order?

Report a mistakeEdit this page

Used in