Build your own mini stream processor · Stage 4 of 8
Tumbling/sliding windows with triggers
Windows turn an unbounded keyed stream into finite aggregations. In event time, a window fires when the watermark passes its end; events that arrive after that (beyond an allowed-lateness grace) are late and go to a side output.
New API this stage
TumblingWindows(size) # .assign(ts) -> [Window]
SlidingWindows(size, slide) # .assign(ts) -> [Window] (ts may be in many)
Window(start, end) # end exclusive
keyed.window(assigner).aggregate(agg_fn, allowed_lateness=0) -> WindowResult
run_windows(WindowResult) -> Fired # list of (key, Window, result); .late side output
This stage's task
TumblingWindows(size).assign(ts)→ one window[start, start+size),start = ts - (ts % size).SlidingWindows(size, slide).assign(ts)→ every window containingts(stepping start down byslide; windows may start before 0, matching Flink).run_windows: bucket each in-time event's value into a pane keyed by(key, window). When the watermark passes a window's end, fire it — emit(key, Window, agg_fn(values))— in(start, key)order, and clear it.- Drop events late beyond
allowed_latenesstoresult.late. - After all watermarks, the fired result equals a batch grouping of in-time events into windows (the oracle).
Constraints
- Event-time triggers only (watermark-driven), never processing time or sleeps.
- Deterministic fire order. Earlier stages still pass. Suite < 60 s.
Further reading
- Flink 2.x — Windows (assigners, triggers, allowed lateness): https://nightlies.apache.org/flink/flink-docs-release-2.2/docs/dev/datastream/operators/windows/
Run it
de start FLK-BYO/04
de test
Hints
Hint 1
TumblingWindows(size).assign(ts) returns the single window [start, start+size) where start = ts - (ts % size). SlidingWindows(size, slide).assign(ts) returns EVERY window [s, s+size) that contains ts, where s steps down by slide from the latest aligned start; windows may start before 0 (Flink does this).
Hint 2
run_windows drives the chain, bucketing each event's value into panes keyed by (key, window). A window FIRES when the watermark passes its end (window.end <= current_watermark): emit (key, Window, agg_fn(values)) and drop the pane. Fire ready windows in (window.start, key) order for determinism.
Hint 3
Lateness: an event is late if max(window ends it belongs to) + allowed_lateness <= current_watermark at arrival — route those to the .late side output instead of a pane. The streaming result after all watermarks must equal a batch grouping of in-time events (the oracle).
uv run de start FLK-BYO/04-windows-triggers
uv run de test