Retaining late-data state is part of the answer's contract
Separate watermark-based window cleanup from processing-time TTL, estimate retained payload, and define how expired or late corrections are handled.
On this page
The question for today
What you’ll understand
- Explain why an emitted event-time result can still need retained state.
- Distinguish allowed lateness from a generic processing-time state TTL.
- Calculate a retention payload estimate and identify its excluded costs.
Emitting an answer does not necessarily make its history disposable. A late measurement might revise a window, an old event might bridge sessions, or a retry might require remembered identity. Retention determines which corrections remain possible and how much state the system carries. Choose that boundary as part of the result contract, then decide what to do with events that arrive after the boundary rather than silently treating memory cleanup as mathematical completeness.
Initial firing and final cleanup are different events
An event-time window can emit an initial result when progress passes its trigger and still retain state for allowed late arrivals. Flink 2.1's allowed-lateness setting is attached to event-time window behavior, with cleanup tied to window timestamps and the configured lateness. A later eligible event can cause an updated result. Downstream consumers need a replacement, retraction, or delta contract to avoid counting every correction as a fresh independent result. Once the window state is removed, the ordinary incremental update path no longer has the history needed to produce the same corrected answer.
Allowed lateness is not simply a per-record rule saying watermark minus event timestamp must be below one threshold. It is related to the retained window's cleanup boundary under the operator's rules. A very old event can still belong to a retained window, while a seemingly similar event in an already expired window cannot use its vanished state. Discrete timestamp maxima and exact comparison rules matter near the edge. Keep illustrative examples away from equality unless teaching the specific implementation's boundary, and test that boundary explicitly in production code.
A generic TTL may use another clock entirely
Flink 2.1's managed State TTL support uses processing time, with configurable update and visibility behavior. That is different from an event-time window's allowed lateness. A replay that runs slowly or quickly can therefore encounter different processing-time expiry even for identical event timestamps. State visibility after expiration and physical cleanup timing are also separate: hiding an expired value does not prove its bytes have already disappeared from every backend file or retained checkpoint. Read the selected backend and TTL policy before using a memory graph as evidence that a semantic retention promise was met.
For an application-level join or deduplication set, define why state can expire and which clock establishes that reasoning. An arbitrary shorter TTL can make previously matchable events fail to join or old duplicates appear new. A longer TTL can increase state cost without improving an answer if the source no longer retains the needed replay data. Align event-time policy, source retention, checkpoint availability, and downstream correction horizon. Expired data may need an audit side output or a batch reconciliation path, but that path must be designed and monitored rather than assumed to happen automatically.
Expose what the system forgets
Measure retained keys or events, cleanup lag, late arrivals accepted, late arrivals rejected, correction outputs, and reconciliation backlog. Report these by source or key population where skew matters. A job can meet low output latency by dropping late records while quietly reducing completeness for a slow input. Tests should restore from a checkpoint, replay old event timestamps, and compare the expected retained-state policy with actual visibility. Explicitly test policy changes across upgrades; increasing a TTL cannot reconstruct already deleted state, and changing its semantics can alter results even when code still deserializes.
The cache lab illustrates forgetting through capacity, not time. Reset, set BOTH C=4 and s=4, predict twelve hits among N=16 requests at t=0, then Play or Step. Reset and re-set BOTH C=3 and s=4; predict zero hits because each four-identity replay follows eviction. The request count is capped at min(4096,16+floor(2t)); edits retain t and recompute from empty. There is no TTL, watermark, event-time join, or late correction. Explain why forgotten identities can change replay behavior without claiming that this LRU trace measures a real retention horizon.
Work it through
Choose a late-arrival policy before deleting history
A fictional event-time window covers [0,10) seconds and retains state until roughly event-time progress 15 seconds under its stated policy. Compare an event at t=8 arriving when the watermark is 12 with the same event arriving at watermark 16. Avoid exact cleanup-boundary equality.
At watermark 12, the initial window result may already exist, but the window's state remains within the stated retention horizon. The event can update it under this fictional allowed-late-data policy.
At watermark 16, the cleanup horizon has passed and the ordinary window state is gone. The application must use its defined expired-data path rather than reconstruct an answer from nonexistent state.
If raw events arrive at 200 per second and are retained for 15 seconds at 100 bytes each, the simple payload estimate is 200×15×100=300,000 bytes, before window duplication and all overhead.
Retention preserves a defined correction opportunity at a measurable cost. It is not proof that all meaningful events have already arrived.
The living notebook
Explore the model
Interactive lab · cache
Forgetting changes later interpretation
For EACH run Reset, re-set BOTH C and s, predict, then Play or Step. At t=0/N=16, (4,4) yields twelve hits and (3,4) yields zero. N=min(4096,16+floor(2t)); edits retain t/recompute from empty. This is capacity-driven LRU eviction, not TTL, event-time cleanup, or a join/late-data simulator.
- Hits / requests
- 0 / 16
- Hit rate
- 0%
- Address-stream period
- 16 addresses
A fully associative LRU cache starts empty and requests address (i·s) mod 16. Hits move an address to the newest position; misses evict the oldest when full. Playback starts with 16 requests and adds two per second, capped at 4096 explicitly simulated requests. This toy stream is not a processor-cache benchmark. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.
Look forA system cannot rely on forgotten history for the same incremental answer; its expiry and reconciliation contract must be explicit.
Pause & reason
Check your understanding
Your turn
Try it yourself
A retain-every-event buffer receives 500 events per second, keeps 40 seconds, and stores 80 payload bytes per event. Estimate payload and explain whether doubling retention necessarily doubles a compact running sum's state.
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Use the raw-event model for the first calculation, then question whether the second representation retains every event.
Compare with the solution
The raw payload estimate is 500×40×80=1,600,000 bytes. Doubling that raw retention doubles this model's payload. A compact running sum can instead keep a fixed-size value per retained key, so its state need not follow the same law; correction and deletion requirements determine whether such compaction is valid.
Keep the idea
Take it with you
- Early output can coexist with retained state for corrections.
- Event-time lateness, processing-time TTL, and physical cleanup have different meanings.
- Forgetting requires an explicit expired-data and reconciliation policy.
Read further
Original sources and deeper explanations to follow at your own pace.
Optional progress, saved in this browser when available. Revisit any lesson at any time.
Lesson not marked complete.