Session Window
How to group events naturally clustered in activity bursts, without alignment to a fixed calendar.
Problem
Tumbling and sliding windows align to external clocks (every full hour, every 30 seconds). Many real activities do not follow a regular tempo: a user clicks 12 times in 2 minutes then disappears for 4 hours; an EDI partner sends 200 invoices in 5 minutes then nothing overnight. For these cases, we want to group events close in time into distinct sessions — unique business or user behaviour — separated by inactivity periods (gaps).
Forces
- No temporal alignment — each key has its own sessions with variable start and end.
- Gap definition: too short = fragmented sessions; too long = artificially merged sessions.
- Session merging: a late event may merge two previously-closed sessions — complex logic.
- State: maintain one open session per key, release after gap.
- High cost on very large cardinality — each key carries its session.
Solution
Define a gap G (inactivity duration after which the session is considered ended). An event at time t opens a session if none is active for that key. A subsequent event at t' < t+G extends the session. When the watermark passes t_last + G without a new event, the session closes and the aggregate is emitted. To handle late events, Flink allows merging: if an event arrives and falls between two already-closed sessions separated by less than G, both sessions are merged retroactively. Flink implementation: stream.keyBy(partnerId).window(EventTimeSessionWindows.withGap(Time.minutes(30))).aggregate(new BatchSizeAggregator()).
Structure
Partner A timeline (gap = 30 min):
Time: 09:00 09:05 09:08 09:15 ...... 11:45 11:50 11:52
Event: ● ● ● ● ● ● ●
└─── Session 1 (4 events) ─┘ └ Session 2 (3) ─┘
gap < 30min 4h gap, > 30min
→ previous session closed
Partner B timeline:
Time: 09:30 09:50 10:25
Event: ● ● ●
└─ Session 1 ─┘
all gaps < 30min → single session of 3 events
Late event arriving at 09:20 for partner A:
Falls between sessions 1 (09:00-09:15) and an earlier session?
→ If yes and gap allows, merge sessions retroactively
→ If no, may create a new singleton session EDI implementation
Typical EDI cases. (1) Partner batch detection: group all INVOIC received from the same partner separated by less than 30 minutes into one "send session", to compute the average batch size and timing — useful to optimise acknowledgement SLA (send one APERAK grouped per session instead of one per message). (2) Long-running saga workflow: an ORDERS/ORDRSP/DESADV/RECADV/INVOIC cycle may span several days but with activity bursts; a session window with 7-day gap per purchase-order number groups the events of one business saga. (3) Attack detection: if a partner sends an abnormally short session with many errors, it is typically a bad deployment or a replaying robot; alert on the pattern. For these cases, do not forget to purge state on session close — without that, RocksDB blows up within weeks.
Anti-patterns
- Gap too short (e.g. 30 seconds for human-driven messages) — fragments real sessions into dozens of pieces.
- Gap too long (e.g. 4h for web sessions) — merges two days of use into a single session.
- No post-close TTL — state of a key inactive for 6 months remains forever.
- Per-event expensive aggregate computation instead of incremental — every arrival recomputes everything.
- Ignoring merging — a late event can break already-emitted aggregates.
Related patterns
- Tumbling Window — fixed time alignment.
- Sliding Window — continuous refreshed metric.
- Watermarks — close sessions.
- Aggregator (EIP 268) — the cousin EIP without a time concept.
Sources
- Akidau T. et al. — The Dataflow Model, VLDB 2015. research.google
- Apache Flink — Session Windows. nightlies.apache.org/flink
- Spark Structured Streaming — Session window. spark.apache.org
- Akidau T., Chernyak S., Lax R. — Streaming Systems, O'Reilly 2018, ch. 4.
- Kafka Streams — Session windows. kafka.apache.org