Processing a stream in flight

Lesson 4 of 5 in Streaming at Scale.

An unbounded stream has no last record, so any computation over it is a snapshot of a thing still arriving. That forces a question batch never has to answer: which records count?

The answer is a window. A tumbling window is a fixed, non-overlapping slice — "orders per minute", where each record belongs to exactly one window. A sliding (hopping) window advances by less than its length, so records belong to several — "the five-minute average, updated every minute". A session window groups records separated by less than a gap of inactivity, so it has no fixed length at all.

Then comes the question that separates a correct answer from a plausible one: which clock?

Event time is when the thing happened. Processing time is when your code saw it. They differ whenever arrival is delayed, and the difference is not cosmetic. A mobile client goes through a tunnel and uploads twenty minutes of buffered events at once. Windowed by processing time, all of them land in the current minute and that minute reports a spike that never occurred. Windowed by event time, each goes to the window its timestamp belongs to and the numbers are right — but the earlier windows had already closed and reported, so the system must either keep them open longer or correct itself.

There is no free option, and the deciding phrase is usually in the requirement. An operational dashboard counting what the system is doing now legitimately uses processing time. Billing, auditing, session analysis or anything reconciled against another system must use event time, because the other system knows when the event happened.

Two sets of horizontal bars comparing event counts per minute under two windowing clocks, for a scenario in which a device loses connectivity between 10:04 and 10:20 and uploads sixteen minutes of buffered readings at 10:22. Under processing time, the minutes 10:04 through 10:20 each show zero or one event while 10:22 shows nine events — a spike with no physical cause, since the device was emitting steadily throughout. Under event time, the same ten events distribute across 10:04 to 10:20 at roughly one per two minutes, matching what the device actually did, and 10:22 shows one event, which is the only reading genuinely captured in that minute. The figure is illustrative with round numbers chosen to make the contrast legible. The point is that processing-time windowing is internally consistent and reports a physically false shape, while event-time windowing reports the true shape and requires the system either to hold windows open for the allowed lateness or to publish a correction after the fact.

The same ten events, bucketed by two clocks. Processing time invents a spike at 10:22 that never happened; event time puts every event where it belongs, at the cost of windows that must stay open. (illustrative — source: Amazon Managed Service for Apache Flink developer guide — event time and watermarks)

Event-time windowing needs one more mechanism, and it is the one worth understanding properly: a watermark.

An event-time window faces an impossible question — when may it emit a result, given that a late record could always turn up? A watermark answers it by fiat. The engine tracks the maximum event timestamp it has seen, subtracts an allowed lateness, and declares that windows ending before that point are complete and may fire.

So the watermark is a tuning knob on a correctness-versus-latency trade, not a fact about the data. Generous allowed lateness means accurate windows that report late and operator state held in memory longer. A tight one means fast results and dropped stragglers. Apache Flink lets you route late records to a side output rather than discarding them, which is how a pipeline can be both prompt and eventually correct: emit on the watermark, then reconcile the side output into the stored result.

And here is the judgement call a stem is usually testing. If a small percentage of events arrive hours late and must still be counted, that is not a windowing problem — hours of allowed lateness is not operable, because the state would be enormous and every result delayed. It is a two-path design: stream for the prompt approximate answer, batch recomputation over the raw landing zone for the authoritative one. Recognising that the stated lateness exceeds what a watermark can sensibly absorb is the whole question.

Which processing component should read this stream?

Interactive decision tree — outcomes:

  • Amazon Data Firehose

    No state, a supported destination, and nothing to run. Set buffering hints for the file size you want and add format conversion if the destination is a lake. Remember what it cannot do: no replay, no fan-out, no ordering promise.

  • AWS Lambda on an event source mapping

    Per-record logic with no cluster. The controls that matter live on the mapping, not in the function: batch size, a batching window, parallelization factor up to 10 per shard, and the error handling — BisectBatchOnFunctionError, MaximumRetryAttempts, an on-failure destination — without which one poison record blocks its shard until the data ages out.

  • MSK Connect with a sink connector

    If the estate is Kafka and a connector exists for the target, MSK Connect replaces code you would otherwise write and own. Its ceiling is single-message transforms — renames, routing, masking — so it is a moving tool rather than a computing one.

  • Lambda with a tumbling window on the event source mapping

    The underused middle option. A Kinesis event source mapping supports a tumbling window of up to 15 minutes, passing aggregate state between invocations per partition key — so per-key counting, summing or last-value tracking needs no stream processor at all.

    Its limits are the window length and the state size, and crossing either is the signal to move to Flink.

  • A Glue streaming job (Spark Structured Streaming)

    Worth choosing when the team already writes Spark and wants one engine, one language and one set of libraries for batch and streaming. You get micro-batch semantics with a trigger interval, the Glue catalogue integration, and a serverless runtime.

    What you give up against Flink: true record-at-a-time processing, and the maturity of Flink's event-time and state-backend story for large keyed state.

  • Amazon Managed Service for Apache Flink

    The right answer when the stem says windows, joins between streams, sessionisation, event-time semantics or exactly-once state. You get checkpoints and savepoints, side outputs for late data, and a real state backend.

    You take on: an application to deploy and version, a snapshot strategy (an incompatible code change cannot resume from an old savepoint), a checkpoint interval to tune, and capacity in Kinesis Processing Units. The honest check before committing: if the "state" is one counter per key over five minutes, the Lambda tumbling window above is the cheaper correct answer.

Key terms: Tumbling window, Event time vs processing time, Watermark, Checkpointing and savepoints, Amazon Managed Service for Apache Flink, Exactly-once processing