A near real-time engine that runs the same Spark DataFrame code over an unbounded stream, giving you exactly-once results via checkpoints.
Structured Streaming is a near real-time processing engine that runs a query incrementally over an unbounded table, offering end-to-end fault tolerance with exactly-once guarantees using familiar Spark APIs.
You write a streaming job exactly like a batch DataFrame query — Spark turns it into an incrementally updated result and handles fault tolerance, state, and exactly-once delivery for you. It is the engine under Auto Loader and Lakeflow Declarative Pipelines (← Delta Live Tables) and the default for incremental ingestion on Databricks.
The stream is processed as a series of small batch jobs; the default trigger runs as fast as possible (~equivalent to processingTime='0 seconds'). Continuous processing is experimental and not recommended.
Read with readStream (Delta, Auto Loader, Kafka, Kinesis, rate); write with writeStream to Delta, files, Kafka, or arbitrary sinks via foreachBatch.
The checkpoint directory stores offsets, commits, state, and the query ID; with a replayable source and idempotent sink this delivers exactly-once. Each query needs its own location.
append (only new rows), update (changed rows), complete (full result table each batch). The legal mode depends on the query's aggregations.
processingTime for fixed-interval micro-batches; availableNow=True for incremental batch then stop (replaces deprecated Trigger.Once on DBR 11.3 LTS+).
withWatermark sets an event-time column plus a lateness threshold so Spark can bound state and drop old state for windowed aggregations and stream-stream joins.
PySparkstream = (spark.readStream.format("delta").table("main.bronze.events")
.withWatermark("event_time", "10 minutes") # bound state for late data
.groupBy(window("event_time", "5 minutes"), "id").count())
(stream.writeStream.outputMode("append")
.option("checkpointLocation", "/Volumes/main/bronze/_ckpt/agg") # exactly-once recovery; unique per query
.trigger(availableNow=True) # incremental batch, then stop
.toTable("main.silver.events_5min"))
checkpointLocation — never share one across queries, and deleting it forces a full reprocess.foreachBatch gives at-least-once, not exactly-once — make the batch function idempotent (e.g. MERGE) yourself.complete mode keeps the full result in state every batch — it ignores the watermark and does not scale to large key spaces.withWatermark) tell Spark how long to wait for late data so it can bound and evict state in windowed aggregations and stream-stream joins."Verified against docs.databricks.com · June 2026