A Kafka broker stores and transports records. A stream processor decides how records update state, combine across inputs and become results. Those decisions add correctness and capacity requirements that partition order alone cannot satisfy.
Scope: Time and state semantics here use Kafka Streams 4.0.x unless another engine is named. A different processing engine may have different watermark, checkpoint and sink guarantees. The examples are explanatory, not executed benchmarks.
Choose which clock answers the question
| Time | Meaning | Question it can answer |
|---|---|---|
| Event time | When the source says the event occurred. | What happened during the customer’s five-minute interval? |
| Ingestion time | When Kafka appended the event under an append-time policy. | When did the broker accept this record? |
| Processing time | When application code processes it. | How much work did this running instance do in the last minute? |
| Kafka Streams stream time | Data-driven progress derived from observed record timestamps in the relevant task/operator context. | Has enough timestamp progress occurred to close this window’s lateness allowance? |
A record can have old event time and recent ingestion/processing time. Replaying yesterday’s data does not turn it into today’s business activity unless the application deliberately uses processing-time semantics.
Kafka Streams extracts record timestamps according to its configured extractor and the source timestamp policy. Stream time advances with records, not merely because wall-clock time passes. Idle inputs and future-dated records can therefore affect result timing. A grace period is not a wall-clock sleep, and Kafka Streams stream time should not be casually equated with every framework’s watermark protocol.
The Streams core concepts define the timestamp, window and state model. Record how each source creates timestamps and how invalid clock values are handled before choosing a window size.
A window needs a late-event policy
Checkpoint: the event belongs to the window—why was it dropped?
Given: an event-time sum for one account, a tumbling window [12:00, 12:05) and two minutes of grace. Other records in the relevant processing context advance stream time. We trace logical aggregate state; caching may coalesce emitted updates.
Predict the final sum: 12 or 15?
- Place the first event. A value of 5, timestamped 12:01, arrives while the window is open. The sum becomes 5.
- Compute the lateness boundary. Window end 12:05 plus two minutes of grace gives 12:07 in stream time.
- Accept a delayed event. Other input has advanced stream time to 12:06. A value of 7 timestamped 12:04 still belongs to this window and is within grace. The sum becomes
5 + 7 = 12. - Reject a later arrival. Stream time advances to 12:08. A value of 3 timestamped 12:03 also belongs to the window, but arrives beyond its grace boundary. It is discarded from this aggregation; the sum stays 12.
Two tests must pass: the timestamp places the event in this window, and processing progress still permits an update. An older arriving timestamp does not rewind stream time and reopen the window.
Change one assumption: input becomes idle at stream time 12:06 while wall clock reaches 12:20. Wall-clock passage alone does not close this stream-time-driven window. The allowance is tied to observed timestamp progress, not elapsed waiting time.
The documented late-record condition is stream time greater than window end plus grace. Exact timestamp boundaries matter; avoid describing this as a vague “wait two minutes after receiving the event.”
A longer grace period admits more delayed data, but retains state longer and postpones finality. A shorter period lowers those costs while excluding more late events. Monitor late-record drops and define correction/reconciliation behavior for business-critical events excluded from the main result.
Early results and final results are different products. Emitting an updated count repeatedly reduces visible latency but requires downstream consumers to interpret updates rather than add every count as a new contribution. Suppressing until closure can give a final window result at the cost of latency and buffering; configure the state/buffer limits explicitly.
Joins require matching data and time semantics
A stream-stream join needs an allowed time relationship between events. The window and grace determine how long a counterpart can arrive and still match. Wider allowances increase state and potential latency.
A stream-table join often enriches an event with reference state. “Latest customer tier when processing” differs from “customer tier when the purchase occurred.” An ordinary current-value table does not reconstruct arbitrary historical state. Versioned stores support some timestamp-based lookups, with documented history and out-of-order limitations; choose them based on the required business meaning, not just the API name.
Partitioning is also part of join correctness. Inputs joined by a key generally need compatible key serialization, partitioning and partition counts for co-partitioned processing. Changing a key can require a repartition topic. Verify the topology rather than assuming a map followed by a join carries the right distribution automatically.
A GlobalKTable offers a different trade-off: each application instance holds a copy of the table. It can simplify certain enrichment joins but multiplies storage and bootstrap work. Broadcast state is not free simply because the application code is short.
Stateful processing adds another storage system
Input topic → task → local state → output topic │ └── changelog topic → restore / standby task Key changes may add a repartition topic between processing stages.
The local store serves processing state. A changelog supports recovery. Repartition topics move data to the owners required by the topology. Standby replicas can reduce recovery work but consume more state storage and processing/network capacity.
A broker retention calculation covers none of these automatically. Inventory input/output topics, changelogs, repartition topics, local state, standby copies and temporary restore/compaction workspace. For windows, active keys, event rate per key, window overlap, grace and retained history all affect size.
A rough restore estimate is:
restore time ≈ state bytes to reconstruct / effective restoration bytes per second
Effective restoration rate is constrained by broker reads, network, decoding and state-store writes, while live work competes for resources. A warm standby may have less state to catch up; a cold new instance may have to restore almost everything. Test both.
Scaling out can temporarily reduce useful processing as assignments move and state restores. A lag-only autoscaler that repeatedly adds and removes instances can create persistent restore work. Include assignment stability, hot keys, restoration progress and destination capacity in scaling decisions.
Processing guarantees stop at defined effects
processing.guarantee=exactly_once_v2 coordinates Kafka Streams’ managed state, output and input progress. It does not make an arbitrary HTTP request inside processing transactional. The transaction chapter gives the external-effect failure cases.
Deterministic transformations are easier to replay. If processing reads the current wall clock or a changing external API, retrying the same record can produce a different result. Record the relevant input/version when business correctness depends on reproducing the decision.
Backpressure also spans the topology. A slow output or state store reduces task throughput and creates input lag. Increasing the fetch or application buffer only hides the mismatch for a while. Bound memory and derive the recovery margin needed to process both new events and accumulated work.
Choose the processing runtime from the responsibility
| Need | Reasonable starting point | Check before committing |
|---|---|---|
| Kafka-centric keyed transformations and local state | Kafka Streams in application instances. | State distribution, restore time, Java operations model and Kafka transaction scope. |
| Simple projection into an existing database | A consumer or connector with an explicit destination contract. | Deduplication, offset/effect atomicity, backpressure and schema handling. |
| Complex event-time coordination or heterogeneous sources/sinks | Evaluate a dedicated processing engine. | Its exact watermark, checkpoint, restart and sink-commit semantics; Kafka EOS alone is not the answer. |
Keep broker transport, processing state and destination effects distinct in the architecture diagram. This makes it possible to say which component restores each piece of state and which failure can produce repeated effects.
Before deployment, feed the same logical events in different arrival orders, include one beyond grace, pause an input, inject a future timestamp, and restore from a cold state store. Check both the business result and the time until it becomes usable. Then add that restore and lateness budget to pipeline sizing.
Whiteboard: recover the result and its meaning
Guided variant. Order analytics sums values in window [10:00, 10:10), with three minutes of grace. The existing sum is 3. Stream time is 10:12 when value 4 timestamped 10:09 arrives; later stream time is 10:14 when value 2 timestamped 10:08 arrives. Fill the close boundary __, the resulting sum __, and explain why wall-clock arrival order is not the window-membership test.
Reveal guided solution
The boundary is 10:13 in stream time. Value 4 belongs to the window and arrives within grace, so the sum becomes 7. Value 2 also belongs by event timestamp but is beyond grace when processed, leaving the sum at 7. Window membership and whether an update remains admissible are separate tests.
Independent problem. The analytics owner asks for eight minutes of grace instead of three. Reevaluate the delayed value 2 above and explain the state/finality cost without inventing an exact storage multiplier. Then recover a failed processor: it must reconstruct 96 GB of state at an effective 80 MB/s, and only afterward starts processing. Input continues at 50,000 records/s; processing then sustains 100,000/s. Use decimal units, assume the rates already include resource contention, and ignore fixed startup overhead. Draw restore, accumulated input and drain as separate intervals. Can the application be fully caught up within 20 minutes? State how downstream interprets revised aggregate outputs.
Reveal independent solution
With eight minutes of grace the boundary becomes 10:18; value 2 at stream time 10:14 is admitted, giving logical sum 9. State must remain updateable longer, and final output may be delayed; exact storage depends on keys, windows, retained data and the implementation, not grace alone.
Restore takes 96,000 MB / 80 MB/s = 1,200 seconds, or 20 minutes. During that interval 60 million input records accumulate. Once processing starts, net drain is 50,000/s; clearing them requires another 20 minutes. Catch-up takes approximately 40 minutes under these assumptions, so the 20-minute objective fails before startup overhead is added.
A warm standby or a smaller/faster restore may change the result, but quantify its remaining restore work and resource cost. Adding replicas does not prove the new recovery deadline. Downstream should treat aggregate outputs as keyed replacements/upserts or use a defined change/retraction protocol; adding every emitted total as a new contribution double-counts updates. Final-only output trades timeliness for waiting and buffering.
Pass criterion: preserve event-time meaning and account for both state restoration and input catch-up. Carry both costs into the final architecture budget.