Structured Streaming: Intermediate
Checkpointing
You can now explain what lives in a checkpoint and why a stream without one is lost on restart.
| Checkpoint entry | What it records | When it is written |
|---|---|---|
| offsets/ | The exact source range batch N will cover | Before batch N runs |
| commits/ | Proof that batch N finished end to end | After batch N's output is written |
| metadata | The query's identity | Once, at first start |
| state/ | Carried data for stateful operations | Every batch, for stateful queries |
What the checkpoint is married to
Exactly-Once as a Contract
You can now state the exactly-once contract: replayable source, checkpointed offsets, idempotent or transactional sink.
The Dup After Restart
You can now diagnose duplicate rows after a stream restart and name the missing half of the contract.
| Step | What happens | Where it lands |
|---|---|---|
| 1 | offsets/N written: batch N will cover this slice | Checkpoint |
| 2 | Batch N output written to the sink | Sink, durably |
| 3 | Crash, before commits/N is written | Nothing records step 2 happened |
| 4 | Restart: offsets N without commits N, replay batch N | Correct engine behavior |
| 5 | Same rows written to the sink again | Duplicates, unless the sink recognizes batch N |
The imposter with the same symptom
A stream restarted after a crash and the downstream table now holds each row from one micro-batch twice. Which part of the exactly-once contract was missing?
Output Modes
You can now pick the legal output mode for a given query and sink, and explain why the others are rejected.
| Mode | What each batch delivers | Legal for | Sink it fits |
|---|---|---|---|
| append | Only rows that will never change again | Stateless queries; aggregations with a watermark | Plain appending sinks, files |
| update | Rows changed since the last batch | Aggregations without append's finality | Sinks that can upsert by key |
| complete | The whole result table, every batch | Aggregations only, small result cardinality | Sinks that can be wholly replaced |
Stateless vs Stateful
You can now split any streaming query into its stateless and stateful parts and predict the operational cost of each.
- filter, select, map, parse: rows forgotten on sight
- Checkpoint holds offsets and commits, kilobytes
- Restarts are instant, nothing to rebuild
- Append mode, naturally: every row is final
- Aggregations, dedup, windows, stream-stream joins
- State store written to the checkpoint every batch
- Restarts rebuild state before resuming
- Needs watermarks to keep state from growing forever
Why the split is the first question
- Classify every streaming query stateless or stateful before estimating its cost or its restart behavior.
- Give every stateful operator a bound: a watermark, a key space you can defend, or both.
- Keep the stateless portion of a pipeline stateless: push filters and parsing ahead of the first stateful operator.
- Check the checkpoint's state directory size in incident triage; unbounded growth there is a diagnosis.
- Don't add dropDuplicates to a stream casually; unbounded, it is a slow memory leak with correct output.
- Don't assume a stream-static join costs what a stream-stream join costs; only one of them carries state.
- Don't let an unbounded aggregation run in update mode just because the engine accepts it; growth arrives on schedule.
- Don't price a stateful stream by its line count; price it by its keys.
> Monday morning, finance flags that weekend revenue in the warehouse is inflated. The ingestion stream crashed on a spot reclaim Saturday night and auto-restarted cleanly, say the logs. The duplicated rows all carry timestamps from a 12-minute window around the crash.
Every stream dies eventually. The good ones come back without writing twice.
- Category
- Spark
- Difficulty
- intermediate
- Duration
- 14 minutes
- Challenges
- 13 hands-on challenges
Topics covered: Checkpointing, Exactly-Once as a Contract, The Dup After Restart, Output Modes, Stateless vs Stateful
Lesson Sections
- Checkpointing (concepts: paStreamProcessing)
When a streaming query restarts, everything in memory is gone: the driver that was pacing the loop, the executors that were mid-task, the engine's knowledge of which offsets it had processed. If that knowledge lived only in memory, a restarted stream would face two bad options: start from the beginning of the source and reprocess everything, or start from the end and silently skip whatever arrived during the outage. The checkpoint is the third option: a durable record of the stream's position, w
- Exactly-Once as a Contract (concepts: paStreamProcessing)
Delivery semantics come in three grades, and naming them precisely keeps every later argument short. At-most-once: an event affects the output zero or one times, so failures lose data. At-least-once: one or more times, so failures duplicate data. Exactly-once: the output ends up as if each event were processed precisely once, no losses, no doubles. Note the careful phrasing on the last one: it is a claim about the observable result, not about the machinery never retrying. Retries happen constant
- The Dup After Restart (concepts: paStreamProcessing)
The incident report is always the same shape. A stream ran fine for weeks, restarted for some ordinary reason, and now the downstream table has duplicates: the same order events twice, revenue double-counted for a 20-minute window, an analyst asking why Tuesday looks so good. Nobody changed the code. The restart is blamed, and the restart is innocent. The duplicates were latent in the pipeline's design from the day it shipped; the restart merely collected the debt. Walk the timeline with the che
- Output Modes (concepts: paStreamProcessing)
The beginner tier named the output mode as a writeStream setting and moved on. Here is what it actually decides: of the result table's rows, which ones does the sink receive at the end of each micro-batch? For a query that only transforms, the question sounds trivial, but the moment a query aggregates, the result table contains rows that change as new data folds in, and delivering a changing row is a genuinely different act from delivering a finished one. The three modes are three answers, and e
- Stateless vs Stateful (concepts: paMicroBatchVsTrue)
Every streaming query answers one question before any other: does producing correct output require remembering anything from previous batches? If no, the query is stateless. A filter, a select, a per-row transform, a parse: each row arrives, is processed on the spot, and is forgotten. Batch N needs nothing from batch N minus one. If yes, the query is stateful: a running count per region must carry the counts so far; a deduplication must remember which keys it has seen; a windowed aggregate holds