Learn Apache Flink - Event Time, Watermarks, & Windowing
Episode 5 of 23

Learn Apache Flink - Event Time, Watermarks, & Windowing

This episode dissects the most important time concept in Flink: event time versus processing time. You'll generate watermarks to handle late-arriving data, get to know the tumbling, sliding, and session window types, and configure window triggers, allowed lateness, and side outputs for late events.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Episode 4 taught you the basic transformations and touched on the window operator. Episode 5 raises your accuracy level: most streaming analytics depend on time, and Flink offers a far more sophisticated time model than just the machine's clock. Without this understanding, your reports could be wrong simply because some events arrived late.

Topics we'll dissect: the difference between event time and processing time, how to generate watermarks to handle lateness, the three main window types — tumbling, sliding, and session — plus triggers, allowed lateness, and side outputs for late events. After this episode, you'll be able to build accurate time-based analytics even when data arrives late or out of order.

Event Time vs Processing Time

Two Different Clocks

Processing time is the time when the processing machine handles the event. Event time is the time recorded inside the data itself, usually a timestamp created at the source. For analytics that must reflect when the actual event happened — for example, what time a transaction was made — event time is the only correct answer.

Two definitions of time in one line
event time (time inside the data) vs processing time (machine time while processing)

Timestamps Inside the Data

For event time to work, every event is expected to carry a timestamp field:

Event with a timestamp
{
  "userId": "u-1001",
  "action": "purchase",
  "amount": 250000,
  "eventTs": "2026-08-10T08:15:00+07:00"
}

The code above uses eventTs as the event time. You'll point to this field when setting up the watermark strategy.

Watermark Generation

Marking Event Time Progress

A watermark is a marker that tells Flink: all events with an event time before this point are considered arrived. The most common strategy is bounded out-of-orderness — assuming events can arrive up to a certain number of seconds late:

Watermark with 5-second lateness
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import java.time.Duration;
 
WatermarkStrategy<Order> strategy =
    WatermarkStrategy.<Order>forBoundedOutOfOrderness(Duration.ofSeconds(5))
        .withTimestampAssigner((event, timestamp) -> event.getEventTs());

forBoundedOutOfOrderness sets the lateness bound, and withTimestampAssigner tells Flink which field is the event time. The larger this bound, the more accurate the results, but the longer windows wait before firing.

Attaching the Strategy to the Source

The strategy is attached when the source is read, so the whole pipeline understands event time ordering:

Assign timestamps and watermarks to the stream
DataStream<Order> orders = env.fromSource(
    kafkaSource,
    strategy,
    "kafka-orders");

Without this step, event-time-based windowing won't work — Flink can't assess lateness without a clear baseline.

Window Types

Tumbling Window

A tumbling window divides the stream into fixed-size windows that don't overlap. Every event belongs to exactly one window:

One-minute tumbling window
orders.keyBy(Order::getUserId)
    .window(TumblingEventTimeWindows.of(Time.minutes(1)))
    .sum("amount");

Sliding Window

Sliding windows can overlap — a new window opens every slide step. Suitable for computing moving averages:

10-minute sliding window with a 5-minute slide
orders.keyBy(Order::getUserId)
    .window(SlidingEventTimeWindows.of(Time.minutes(10), Time.minutes(5)))
    .sum("amount");

Session Window

A session window groups nearby events and closes itself after no event arrives for a given gap. Ideal for analyzing user sessions:

Session window with a 30-minute gap
orders.keyBy(Order::getUserId)
    .window(EventTimeSessionWindows.withGap(Time.minutes(30)))
    .sum("amount");

The choice of window type determines the shape of the aggregation: tumbling for periodic counts, sliding for moving trends, and session for activity patterns.

Window Triggers, Allowed Lateness, and Late Events

Triggers Decide When a Window Is Processed

A trigger is the logic that decides when window results are emitted. Flink's built-in default is adequate for most cases: the window fires when the watermark passes its end boundary.

Allowed Lateness and Side Outputs

Even with watermarks configured, very late events can still arrive. Configure how long a window still accepts late events, then route the rest to a side output:

Allowed lateness with a side output
OutputTag<Order> lateTag = new OutputTag<Order>("late-orders") {};
 
SingleOutputStreamOperator<Order> windowed =
    orders.keyBy(Order::getUserId)
        .window(TumblingEventTimeWindows.of(Time.minutes(1)))
        .allowedLateness(Time.minutes(2))
        .sideOutputLateData(lateTag)
        .sum("amount");
 
DataStream<Order> lateOrders = windowed.getSideOutput(lateTag);
lateOrders.print();

.allowedLateness extends the window's lifetime two minutes after the watermark passes it, and .sideOutputLateData separates events that arrive after that so they don't pollute the main aggregation.

Balancing Latency and Accuracy

The rule of thumb is simple: the larger the allowed lateness, the more complete the results, but the longer windows wait. Simulate this behavior by running a job and observing the results:

Run the windowing job
./bin/flink run -d target/window-job.jar
./bin/flink list -a

The ./bin/flink run -d command submits the job in detached mode, and ./bin/flink list -a shows its status. From the web dashboard, you can see when each window fires and how many late events go into the side output.

Conclusion

Episode 5 gave you full control over time: distinguishing event time from processing time, generating watermarks with forBoundedOutOfOrderness, choosing the right window type, and configuring triggers, allowed lateness, and side outputs for late events. These skills are the foundation of almost all correct streaming analytics.

The key takeaways:

  • Event time reflects when the actual event occurred; processing time is just the machine clock.
  • Watermarks mark event time progress and handle bounded lateness.
  • Tumbling windows don't overlap, sliding windows stack, and session windows follow activity gaps.
  • Allowed lateness extends a window's lifetime; side outputs capture events that are too late.
  • Always balance accuracy and latency when choosing a lateness value.

In the next episode, episode 6, we'll discuss state management and checkpointing — stateful operators and key/value state, ValueState, ListState, MapState, and AggregatingState, checkpointing configuration and its consistency guarantees, plus restart strategies and fault tolerance. This is the key to building reliable Flink jobs that don't lose data in the event of a failure.