Structured Streaming checkpoints are the foundation that persists a streaming query's progress, enabling restart after failures and Exactly-once processing. Get the design wrong and you will hit duplicates, data loss, or OOMs from runaway state. This article covers checkpoint internals, design rules, failure recovery procedures, and the conditions for Exactly-once from a practical operations standpoint.
The checkpoint directory contains four subdirectories. Each one manages a different aspect of the streaming query's state.
| Directory | Contents | Role |
|---|---|---|
| offsets/ | Read offsets per batch | Records how far the source has been read; a batch ID -> offset mapping |
| commits/ | Committed batch IDs | Records which batches finished writing to the sink |
| sources/ | Source-specific metadata | Processed-file list for file sources; not used for Kafka |
| state/ | Intermediate state for stateful operations | Persists state for watermarks, aggregations, joins, and so on |
/mnt/checkpoints/orders-pipeline/
├── offsets/
│ ├── 0 # Batch 0: {"kafka-topic": {"0": 100, "1": 150}}
│ ├── 1 # Batch 1: {"kafka-topic": {"0": 200, "1": 300}}
│ └── 2
├── commits/
│ ├── 0 # Batch 0: sink write committed
│ └── 1 # Batch 1: sink write committed
├── sources/
│ └── 0/
└── state/
└── 0/
└── delta/ # State store snapshotsRecovery on restart works as follows. If a batch has an entry in offsets/ but no matching entry in commits/, it is treated as 'read but not committed' and gets reprocessed. Batches with a commit entry are skipped, which prevents double writes.
The single most important checkpoint rule is: assign one unique checkpoint path to one streaming query.
/mnt/checkpoints/{pipeline-name}/{query-name}/# Give every query its own checkpointLocation
orders_query = (
orders_df.writeStream
.format("delta")
.outputMode("append")
.option("checkpointLocation", "/mnt/checkpoints/orders-pipeline/orders-to-silver")
.toTable("silver.orders")
)
payments_query = (
payments_df.writeStream
.format("delta")
.outputMode("append")
.option("checkpointLocation", "/mnt/checkpoints/orders-pipeline/payments-to-silver")
.toTable("silver.payments")
)| Location | Pros | Cautions |
|---|---|---|
| DBFS | Easy to use within Databricks | Lost when the workspace is deleted; not recommended for production |
| Cloud storage (S3/ADLS/GCS) | High durability and the recommended choice | Network latency (usually negligible) |
| Unity Catalog Volumes | Governance integration and access control | Only available in UC-enabled environments |
Use cloud storage or UC Volumes in production. Restrict DBFS checkpoints to development and validation.
Structured Streaming's Exactly-once semantics only hold when both source replayability and sink idempotency are in place.
| Element | Condition | Exactly-once Guarantee |
|---|---|---|
| Source | Replayable (Kafka, files, Delta) | Offsets are tracked in the checkpoint |
| Sink | Idempotent writes (Delta, foreachBatch + transactions) | Prevents duplicates on reprocessing |
| Checkpoint | Stored on durable storage; one path per query | Accurate progress tracking |
When the sink is a Delta table, Delta's transaction log and the Structured Streaming checkpoint cooperate to deliver Exactly-once. When you write to an external system through foreachBatch, you need to manage idempotency keys using the batch ID.
def process_batch(batch_df, batch_id):
# Example of idempotent processing using the batch ID
batch_df.write \
.format("delta") \
.mode("append") \
.saveAsTable("silver.events")
(source_df.writeStream
.foreachBatch(process_batch)
.option("checkpointLocation", "/mnt/checkpoints/events-pipeline/events-to-silver")
.start()
)Here are the recovery patterns for streaming jobs that have stopped due to a failure.
Restart the job with the same checkpointLocation. The query resumes from the batch after the last committed one. This is enough when nothing in the code has changed.
# 1. Move the corrupted checkpoint aside
dbutils.fs.mv(
"/mnt/checkpoints/orders-pipeline/orders-to-silver",
"/mnt/checkpoints/_archived/orders-to-silver-corrupted-20260327"
)
# 2. Check the last timestamp written to the sink table
last_ts = spark.sql("""
SELECT MAX(event_timestamp) FROM silver.orders
""").collect()[0][0]
# 3. Restart with a new checkpoint (use MERGE to prevent duplicates)
(source_df.writeStream
.foreachBatch(lambda df, id: merge_deduplicate(df, id))
.option("checkpointLocation", "/mnt/checkpoints/orders-pipeline/orders-to-silver")
.start()
)Windowed aggregations and stream-stream joins accumulate state in the checkpoint's state/ directory. Without a watermark, state grows without bound and eventually triggers an OOM.
from pyspark.sql.functions import window, col
(orders_df
.withWatermark("event_time", "10 minutes")
.groupBy(window("event_time", "5 minutes"), "product_id")
.agg({"amount": "sum"})
.writeStream
.format("delta")
.outputMode("append")
.option("checkpointLocation", "/mnt/checkpoints/orders-pipeline/windowed-agg")
.toTable("gold.orders_5min_agg")
)Data Engineer Associate / Professional
問題 1
A data engineer runs a pipeline that reads from Kafka via Structured Streaming and writes to a Delta table. After a failure, the job is restarted with the same checkpointLocation and data is processed without duplicates. Which option correctly explains this behavior?
正解: A
Structured Streaming checkpoints identify the reprocessing range from the diff between offsets/ (read) and commits/ (write-completed). The Delta sink guarantees idempotency through transactional writes. Structured Streaming does not use Kafka's consumer group offsets — the checkpoint replaces them. There is no SELECT-based matching or MERGE-based deduplication going on.
What happens if I delete the checkpoint?
Deleting the checkpoint causes the streaming query to lose all progress information (processed offsets and state). On the next start, the query restarts from scratch and reprocesses the entire source. If you are writing to a Delta sink in append mode, duplicates will appear. Delete a checkpoint only as a planned operation during a stream rebuild driven by a logic change, and reset the sink table at the same time.
Can a single checkpoint be shared across multiple streaming queries?
No. The rule is one checkpoint path per query. If multiple queries point at the same checkpoint path, offset management collides and you will see data loss, duplicates, and unexpected errors. Always assign a unique checkpointLocation to each query.
When is Exactly-once not guaranteed?
Exactly-once typically breaks in three cases: (1) the sink does not support idempotent writes (external API calls, non-transactional databases, etc.); (2) foreachBatch does not group multiple writes into a single transaction; (3) you reset the checkpoint without clearing the sink data and then reprocess. Using a Delta table as the sink guarantees transactional writes, which is the safest way to achieve Exactly-once.
Practice with certification-focused question sets
無料で問題を解いてみるNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
Databricks Certifications: All 7 Exams, Difficulty & Study Plan (2026)
Complete guide to all 7 Databricks certifications — Data Eng...
Databricks Exam Difficulty Ranking: All 7 Certs Compared (2026)
Every Databricks certification ranked by difficulty, with st...
Databricks Study Guide: Fastest Pass Route & Time Estimates (2026)
How to pass Databricks certifications efficiently. Official ...
Databricks Data Engineer Associate: Complete Guide (2026)
Domain-by-domain breakdown of the Databricks Certified Data ...
Databricks Data Engineer Professional: Complete Guide (2026)
Tactics for the Databricks Certified Data Engineer Professio...