What is windowing in stream processing?

Updated Feb 20, 2026

Short answer

Windowing slices an unbounded stream into finite chunks so that aggregations can produce results. A stream never ends, so 'count the events' has no answer without a boundary — a window supplies one. The main types are tumbling, sliding, and session windows, and the hard part is deciding when a window is complete given that events arrive late and out of order.

Deep explanation

TypeScript
events: a b c d e f g
Tumbling (60s) [ a b c ][ d e ][ f g ] fixed, non-overlapping
Sliding (60s/30s)[ a b c ]
[ b c d ] fixed, overlapping
[ c d e ]
Session (30s gap)[ a b c ] [ d e ][ f g ] boundary = inactivity
  • Tumbling — fixed size, no overlap. Every event belongs to exactly one window. Use for periodic reporting: revenue per hour, errors per minute.
  • Sliding — fixed size, advancing by a smaller step, so windows overlap and an event appears in several. Use for rolling metrics: "average latency over the last 5 minutes, updated every 30 seconds".
  • Session — no fixed size; the window closes after a gap of inactivity. Use for user-behaviour analysis, where the natural unit is a visit rather than a clock interval.

The genuinely hard part is time and lateness. Event time is when the event actually happened; processing time is when your system saw it. A phone that was offline for an hour delivers events with an event time long past. Windowing on processing time is simple but wrong — it puts that event in the current window rather than the one it belongs to.

Systems resolve this with watermarks: an assertion that no event older than time T is expected any more. A window closes when the watermark passes its end.

Python
stream \
.assign_timestamps_and_watermarks(
WatermarkStrategy.for_bounded_out_of_orderness(Duration.of_seconds(30)))
.key_by(lambda e: e.user_id) \
.window(TumblingEventTimeWindows.of(Time.minutes(1))) \
.sum('amount')

The watermark delay is a direct trade-off: longer means more late events are included but every result is delayed; shorter means faster results and more data dropped. Anything arriving after the watermark is late data, and you must choose explicitly — discard it, send it to a side output, or re-open the window and emit a correction.

Real-world example

Fraud detection needs "more than 5 transactions from one card in 60 seconds":

Python
transactions \
.key_by(lambda t: t.card_id) \
.window(SlidingEventTimeWindows.of(Time.seconds(60), Time.seconds(10))) \
.apply(lambda key, window, events:
Alert(key) if len(events) > 5 else None)

Sliding rather than tumbling is essential here. With tumbling 60-second windows, three transactions at 0:59 and three at 1:01 land in different windows and neither trips the threshold — despite six transactions in two seconds. Sliding windows advancing every 10 seconds catch the burst wherever it falls.

Common mistakes

  • - Windowing on processing time when the question is about event time, so delayed events are attributed to the wrong period and results silently drift.
  • - Using tumbling windows for threshold detection, where an event burst straddling a boundary is split and missed.
  • - Setting the watermark delay too aggressively and dropping legitimate late data with no visibility into how much was lost.
  • - Ignoring late data entirely rather than routing it to a side output — you cannot fix what you never recorded.
  • - Using session windows without a state time-to-live, so keys that never see another event retain state forever and leak memory.
  • - Forgetting that sliding windows multiply state: a 60s window advancing every 1s keeps 60 windows open per key at once.

Follow-up questions

  • What is a watermark and how do you choose its delay?
  • What are the options for handling late data?
  • How do session windows differ from the other types?
  • Why do sliding windows cost more state than tumbling ones?

More Data Processing interview questions

View all →