Structured Streaming on the AWS Lakehouse: Triggers, Checkpoints, foreachBatch and Exactly-Once
Operate production Structured Streaming on Databricks-on-AWS: the full trigger menu (default 3-5 s processingTime, fixed intervals, availableNow with its per-source DBR floors, realTime mode under one second, and the unsupported OSS continuous trigger with the three-continuous disambiguation), checkpoint anatomy on S3-backed Unity Catalog volumes (offsets, commits, state, metadata) and the restart-change matrix, foreachBatch MERGE with txnAppId/txnVersion idempotency, change-data-feed fan-out with readChangeFeed and table_changes, watermark-bounded state with dropDuplicatesWithinWatermark, RocksDB changelog checkpointing, schema evolution locks on streams, Kafka/MSK rate controls and backlog metrics, streaming observability, DBSQL streaming tables and dashboard-cache reality, a worked MSK-to-silver-to-gold job, five PySpark worked examples, a checkpoint-state mermaid, a comparative verdict on raw notebooks versus Lakeflow pipelines, Burberry/Shell/Asurion case studies on AWS, an October 2026 update box and fourteen practice questions.
A streaming query on Databricks is an unbounded table and a batch-style plan that the engine re-runs over the next slice of input. That model is why "converting existing batch jobs into streaming jobs requires minimal code changes" (Databricks blog, 2026-04-20), and it is also why every production incident in this space comes back to four objects: the trigger that decides when a micro-batch runs, the checkpoint that remembers what was read and what was committed, the sink contract that decides whether a replayed batch duplicates rows, and the watermark that decides when state may be dropped. Get those four right and the lakehouse fills itself in near-real-time; get one wrong and you reprocess a week, duplicate a silver table, or watch state grow until the cluster dies.
This lesson is the streaming half of the course. Lesson 05 taught you how data arrives (Auto Loader, MSK auth, file events); this lesson teaches how it keeps arriving correctly: triggers and their AWS cost physics, checkpoint hygiene on S3-backed volumes, foreachBatch MERGE with txnAppId/txnVersion, change-data-feed fan-out, watermark-bounded state, exactly-once end to end, and the production posture (Lakeflow Jobs, not all-purpose clusters) that Databricks documents today.
Every fact below comes from Databricks' AWS documentation: the triggers (footer 2026-10-08), checkpoints (2026-09-11), foreach, Delta streaming (both 2026-08-24), CDF, Kafka (2026-09-11), api-options (2026-10-05), stream-monitoring (2026-09-23), watermarks (2026-09-11), stateful-streaming (2026-10-09), production, schema-evolution (2026-08-05) and Databricks SQL streaming/dashboards pages: plus dated Databricks blogs: all accessed or dated October 2026. Platform scope is Databricks on AWS: Amazon MSK, S3, Kinesis and Unity Catalog volumes are the substrate; Azure Event Hubs does not exist here and is out of scope.
=============================================================
STRUCTURED STREAMING ON DATABRIKS/AWS - SHAPE (Oct 2026)
-------------------------------------------------------------
Triggers (docs .../triggers, footer 2026-10-08)
default ~3-5 s latency; processingTime 0;
checks storage every few ms (API cost!)
processingTime='10 seconds' the cost-balancing default
availableNow=True drains everything available, then exits;
replaces deprecated Trigger.Once (11.3+);
only trigger supported on serverless
(with deprecated Once); min DBRs: files
9.1 LTS, Delta/AL/Kafka 10.4 LTS,
Kinesis 13.1
realTime='5 minutes' sub-second mode; string = micro-batch
length (default 5 min); <1 s at tail,
~300 ms common, as low as 5 ms E2E
continuous='1 second' NOT SUPPORTED (OSS experimental);
use real-time mode instead
Checkpoint (one per query, S3 via UC volume, no lifecycle)
offsets | commits | state | metadata
Guarantees
Delta sink -> exactly-once (transaction log)
foreachBatch -> at-least-once; exactly-once via
txnAppId + txnVersion (= batchId)
Kafka sink -> at-least-once; idempotent producer DBR 13.3+
State
watermark bounds state; dropDuplicatesWithinWatermark
(DBR 13.3 LTS+, needs watermark); RocksDB + changelog
checkpointing DBR 13.3+ recommended for ALL stateful
queries; transformWithState DBR 16.2+
=============================================================In this lesson you will:
- configure every supported trigger and defend the choice against latency, cost and serverless constraints: including why the OSS
continuoustrigger is dead on Databricks and which of the three different "continuous" features people confuse; - dissect a checkpoint (offsets, commits, state, metadata), place it on a Unity Catalog volume path on S3, and recite the restart-change matrix: what you may alter on a live query versus what silently forces a fresh start;
- write the headline streaming job: Amazon MSK → parse → watermark +
dropDuplicatesWithinWatermark→foreachBatchMERGE into a Delta silver table withtxnAppId/txnVersion, plus a dead-letter path; - stream change data feed into gold:
readChangeFeed=true,_change_typesemantics,startingVersion, and thetable_changes()batch replay for backfill and audit; - bound stateful aggregation with watermarks: append vs update vs complete, the mandatory watermark on outer joins, the global-min multiple-watermark policy, and the state-bloat failure mode with its RocksDB remedy;
- watch a stream like an operator: progress fields (
numInputRows,batchDuration,stateOperators), the Spark UI Streaming tab,StreamingQueryListener, anddf.observe; - build the near-real-time dashboard stack honestly: DBSQL streaming tables on serverless pipelines, and why a Lakeview cache up to 24 hours old is not "real-time";
- study three named AWS customer streaming stories (Burberry, Shell, Asurion), read the 2026 update box, and practise with 14 exam-style questions plus four interactive checks.
1. The micro-batch model and the trigger menu
Structured Streaming plans your DataFrame transformations once and executes them incrementally. In the default micro-batch engine, the driver repeatedly asks the source "what is new since the last offset?", plans a batch, runs it on the cluster, commits the offset and the sink write, and checkpoints progress. Real-time mode is Databricks' separate, supported low-latency execution path: not the OSS continuous-processing experiment. Batch and streaming share one API surface; only the reader, the trigger and the checkpoint differ. That is the unification lesson 05 used to run the same bronze code as a nightly job and as a low-latency stream.
1.1 The verified trigger table (docs, footer 2026-10-08)
Trigger | How to set it | Documented behaviour | Use when |
|---|---|---|---|
Unspecified default | omit .trigger(...) | "General-purpose streaming with 3 to 5 second latency. Equivalent to processingTime trigger with 0 ms intervals." Checks for data every few milliseconds: "can generate a high volume of cloud storage API calls per day and result in unexpected charges" | Prototyping only; never a cost-conscious production default |
Fixed interval | .trigger(processingTime='10 seconds') | A micro-batch per interval; latency bounded by the interval | The cost-balancing default for near-real-time ETL on classic compute |
Available-now | .trigger(availableNow=True) | "Processes as much data as is available at the time the streaming job is triggered", across as many micro-batches as rate limits require, then exits. Replaces Trigger.Once, deprecated in DBR 11.3 LTS+. Minimum DBRs: file sources 9.1 LTS; Delta / Auto Loader / Kafka 10.4 LTS; Kinesis 13.1 | Scheduled near-real-time ingestion (hourly/daily jobs); the only trigger story on serverless besides deprecated Once ("On serverless compute, only Trigger.AvailableNow() and Trigger.Once() are supported. Databricks recommends Trigger.AvailableNow()") |
Real-time mode | .trigger(realTime='5 minutes') | "Ultra-low latency operational workloads requiring sub-second processing"; "'5 minutes' indicates the length of a micro-batch". Latency "as low as five milliseconds" end-to-end (concepts page, 2026-09-11); "under 1 second at the tail, and in common cases around 300 ms" (triggers, 2026-10-08). Needs dedicated compute | Fraud blocking, real-time personalization: the page's own examples |
Continuous processing | .trigger(continuous='1 second') | "Not supported. This is an experimental feature included in Spark OSS. Use real-time mode instead." "Databricks doesn't support or recommend this mode." | Never, on Databricks |
1.2 Worked example E1: choosing a trigger, with the AWS bill attached
Scenario: an S3 landing zone drops files in bursts; the product spec says "near-real-time, minutes OK". Three candidate configurations, priced on two axes: compute (jobs cluster) and S3 request cost (the default trigger's hidden multiplier).
# E1 - three ways to run the same bronze Auto Loader query
# (checkpoint and schema paths on UC volumes; S3-backed, no lifecycle policy)
# A. Default trigger - fastest to write, most expensive to operate
(brz.writeStream.format("delta")
.option("checkpointLocation", "/Volumes/lake/ops/ckpt/orders_bronze")
.toTable("lake.bronze.orders")) # implicit processingTime = 0
# B. Fixed interval - the cost-balancing production default
(brz.writeStream.format("delta")
.option("checkpointLocation", "/Volumes/lake/ops/ckpt/orders_bronze")
.trigger(processingTime="10 seconds")
.toTable("lake.bronze.orders"))
# C. Available-now as a scheduled job - cheapest shape for hourly arrival
(brz.writeStream.format("delta")
.option("checkpointLocation", "/Volumes/lake/ops/ckpt/orders_bronze")
.trigger(availableNow=True) # DBR 10.4 LTS+; serverless-recommended
.toTable("lake.bronze.orders"))Compute sketch (ours on documented semantics): the hourly availableNow run drains in 20 minutes on a 4-node jobs cluster: 12 DBU/h × 0.33 h = 4 DBU/run; at Jobs Compute $0.15/DBU (AWS Premium list shape as of Oct 2026, same documentary basis as lesson 05's E7: always re-check system.billing.list_prices in your account) that is $0.60/run → $14.40/month. A processingTime='10 seconds' stream on the same cluster runs continuously: 720 h/month × 12 DBU = 8,640 DBU ≈ $1,296/month, whether or not data arrives. The default trigger (checks "every few milliseconds") multiplies S3 LIST/GET API volume against the same $0.005-per-1,000-LIST rate lesson 05 computed: on a 2M-file prefix, directory-listing mode at second-level triggers cost $2,592 to $25,920/month in LIST alone versus $28.80 at a 15-minute availableNow. The trigger is a cost decision, not a latency preference.
- 📚 Did you know?
Trigger.Onceis deprecated since DBR 11.3 LTS, "UseTrigger.AvailableNowfor all incremental batch processing workloads" (triggers, 2026-10-08), butOncestill appears in the serverless support sentence because old notebooks still call it.AvailableNowis the strict upgrade: same drain-then-exit semantics, plus rate limiting across micro-batches (maxFilesPerTrigger,maxOffsetsPerTrigger), whichOncelacked. Any tutorial that teachesOnceas current is teaching a 2022 API.
2. Checkpoints: the query's memory, on S3
2.1 Anatomy
"A checkpoint directory contains Offsets, Commits, State, and Metadata" (checkpoints, 2026-09-11):
Entry | What it stores | Why it matters |
|---|---|---|
Offsets | Source offsets processed in each micro-batch | Resume point after failure; the replay boundary |
Commits | Which micro-batches have committed to the sink | The exactly-once ledger: a batch is "done" only when both sink commit and commit-log entry land |
State | Stateful-operator metadata, state schema, state-store contents | Watermark windows, dedup keys, aggregation state; RocksDB-backed on modern DBRs |
Metadata | Unique query ID and configuration | Identity of the query for the UI and restart checks |
On AWS the checkpoint lives on durable storage: the documented example writes checkpointLocation under a Unity Catalog volume path (/Volumes/catalog/schema/volume/path), which lands on S3 through your storage credential. "Give each query its own checkpoint directory in durable cloud storage" (production page). Never local DBFS /tmp, never the landing prefix, never a lifecycle-policy path.
2.2 The restart-change matrix
Not every code change is restart-safe on an existing checkpoint. The checkpoints page's rules, compressed:
Change across restart | Allowed? |
|---|---|
Filters, projections, trigger-interval changes, rate limits ( maxFilesPerTrigger, maxOffsetsPerTrigger) | Generally safe |
Adding/removing/reordering sources, changing source count or type | No: new checkpoint required (DBR 18.2+ allows named-source evolution via .name() + spark.sql.streaming.queryEvolution.enableSourceEvolution, irreversible per checkpoint) |
Changing the state schema (different aggregation keys, different watermark column type) | No: new checkpoint |
Changing sink type (e.g. Kafka sink → file sink not allowed; file → Kafka allowed) | Matrix-dependent; many sink swaps are one-way |
Changing the state-store backend (HDFS ↔ RocksDB) | No: cannot be swapped between restarts |
Changing shuffle partition count on a stateful query | No on older DBRs (frozen at checkpoint creation); DBR 18 LTS+ can repartition via on-demand state repartitioning without a new checkpoint |
Changing compute access mode with dropDuplicates/dropDuplicatesWithinWatermark present | Can fail a state-schema compatibility check: pin the access mode for the life of the query |
⚠️ Deleting a checkpoint is a one-way door, and state bloat is the slow-motion version of the same disaster. "When you delete the files in a checkpoint directory or change to a new checkpoint location, the next run of the query begins fresh" (checkpoints doc): Kafka replays per startingOffsets (default latest: the backlog you accumulated may be skipped), Auto Loader re-lists the prefix, the watermark resets, and every dedup/aggregation key is rebuilt from zero. Worse, if the query writes Delta with idempotency markers, a fresh checkpoint restarts batch IDs at 0, and Delta will silently drop the replayed batches unless you also change txnAppId (docs.../structured-streaming/delta-lake, 2026-08-24). The opposite failure is state bloat: no watermark on a dedup or aggregation means stateOperators.numRowsTotal and memoryUsedBytes climb forever until the executor dies, and in complete output mode "aggregation state isn't dropped" by design. Checkpoints also must never sit under a cloud object lifecycle policy: Auto Loader's production page is blunt: if lifecycle rules clean the checkpoint, "the stream state is corrupted", and you restart from scratch. Treat /_checkpoints (the underscore survives VACUUM) and UC volume paths as production data with an owner, an access policy and a no-delete IAM statement.
3. Sources on AWS: Kafka/MSK deep, Delta CDF in
Lesson 05 covered Auto Loader end to end; this section adds what a streaming operator needs: Kafka rate controls, the Delta table as a source, and CDF as the recommended change path.
3.1 Kafka / Amazon MSK: the four options that decide your week
Minimal read is spark.readStream.format("kafka") with kafka.bootstrap.servers and subscribe, and then the options that matter (api-options, 2026-10-05; Kafka doc, 2026-09-11):
Option | Default | Behaviour |
|---|---|---|
startingOffsets | latest (streaming) / earliest (batch) | Applies only to a fresh query; "Resumed queries always use the checkpoint." A silent-backlog-loss footgun on first run |
maxOffsetsPerTrigger | none | Max records per micro-batch, "distributed proportionally across topic partitions": the catch-up rate limiter |
minOffsetsPerTrigger + maxTriggerDelay | none / 15m | Batch up small traffic; when maxTriggerDelay is reached the micro-batch runs regardless |
failOnDataLoss | true | Fails on deleted topics or truncated offsets instead of silently skipping; "Databricks estimates conservatively… this might cause false alarms" |
The reader returns key and value as binary, always cast/from_json/from_avro explicitly. Auth on AWS, in Databricks' recommended order: Unity Catalog service credentials (databricks.serviceCredential, DBR 16.1+, preferred for shared clusters and serverless, do not also set sasl.* yourself), then instance-profile MSK IAM (AWS_MSK_IAM + shaded shadedmskiam.* classes, SASL_SSL, port 9098), then role-based JAAS (awsRoleArn=), then SASL/PLAIN/SCRAM, then mTLS with material in a UC volume and passwords in a secret scope. MSK security groups must allow the Databricks compute SG on 9092 (PLAINTEXT) / 9094 (SASL/SSL) / 9098 (IAM); VPC peering or PrivateLink bridges the two VPCs.
3.2 Worked example E2: MSK → Delta bronze, rate-limited and deduplicated
# E2 - Amazon MSK (IAM via UC service credential) -> Delta bronze
# DBR 15.4 LTS jobs cluster; checkpoint on a UC volume (S3-backed)
from pyspark.sql.functions import from_json, col, expr
from pyspark.sql.types import StructType, StructField, StringType, DoubleType, TimestampType
schema = StructType([
StructField("event_id", StringType()),
StructField("order_ts", TimestampType()),
StructField("customer_id", StringType()),
StructField("amount", DoubleType()),
])
raw = (spark.readStream.format("kafka")
.option("kafka.bootstrap.servers", "b-1.msk.acme.amazonaws.com:9098")
.option("subscribe", "orders")
.option("databricks.serviceCredential", "msk-orders-cred") # DBR 16.1+; no sasl.* below
.option("startingOffsets", "earliest") # FIRST run only; checkpoint wins after
.option("maxOffsetsPerTrigger", 500_000) # ~16,600 msg/s at a 30 s interval
.option("failOnDataLoss", "true")
.load())
parsed = (raw.select(col("value").cast("string").alias("json"), col("timestamp").alias("ingest_ts"))
.select(from_json(col("json"), schema).alias("e"), col("ingest_ts"))
.select("e.*", "ingest_ts"))
deduped = (parsed
.withWatermark("order_ts", "1 hour") # lateness bound
.dropDuplicatesWithinWatermark(["event_id"])) # DBR 13.3 LTS+
(deduped.writeStream.format("delta")
.option("checkpointLocation", "/Volumes/lake/ops/ckpt/orders_bronze")
.trigger(processingTime="30 seconds")
.toTable("lake.bronze.orders"))Two documented traps live in those twenty lines. startingOffsets=latest is the streaming default, on a brand-new checkpoint the query silently skips everything already in the topic; set earliest for the first run, then trust the checkpoint forever. And on DBR 17.1+ the latest Kafka offsets are fetched after each micro-batch completes, so backlog metrics (avgOffsetsBehindLatest etc.) "may show small, persistent non-zero values. This is expected behavior", do not page someone at 03:00 for a 12-offset "lag".
3.3 Delta as a source, and CDF as the recommended change path
"Structured Streaming only accepts append inputs and throws an exception if any modifications occur on the source Delta Lake table" (docs.../structured-streaming/delta-lake, 2026-08-24), which is why "Databricks recommends streaming from the CDC feed of a Delta Lake table rather than directly from the table whenever possible." Automatic change data feed "uses the same table_changes() and readChangeFeed APIs as legacy change data feed and works with batch queries, Structured Streaming…" (CDF page). Enable it at table creation (delta.enableChangeDataFeed=true), nothing is captured before that moment. Section 5 builds the full fan-out.
- 📚 Did you know? The DStreams-era "Spark Streaming" project "is now a legacy project and new streaming applications should use Structured Streaming instead" (Databricks blog, 2026-04-20). If your resume or runbook still says "Spark Streaming" or
Dstream, you are advertising a technology its own creator retired. The Structured Streaming blog of the same date notes the API "became production-ready in Spark 2.2": the model you are learning here has been the default for the better part of a decade.
4. foreachBatch and the exactly-once story
4.1 The contract
"You must use foreachBatch for Delta Lake merge operations in Structured Streaming": MERGE is a batch operation, so an upsert from a stream can only happen inside the micro-batch callback (docs.../structured-streaming/foreach, 2026-08-24). The same page fixes the guarantee: "foreachBatch() provides only at-least-once write guarantees. However, you can use the batchId provided to the function as way to deduplicate the output and get an exactly-once guarantee." On DBR 14.0+ the Delta path requires the explicit idempotency API: "You must use the txnAppId and txnVersion write options, binding txnVersion to batchId", and "Do not catch and retry exceptions locally"; let errors propagate so the orchestrator retries the whole micro-batch cleanly. For custom/external destinations (OLTP DB, queue, HTTP API): "Implement your own idempotency": if the batchId is the same, your side effect must be the same.
Three corollaries the docs state and junior engineers violate constantly:
- New checkpoint ⇒ new
txnAppId. "If you delete the streaming checkpoint and restart the query with a new checkpoint, you must provide a differenttxnAppId." New checkpoints start at batch ID 0; with the old appId, Delta treats the replay as a duplicate and drops it. - One streaming write per sink. "Databricks recommends configuring a separate streaming write for each sink you want to update instead of using
foreachBatch. Writes to multiple sinks in foreachBatch reduces parallelization and increases overall latency because writes to multiple tables are serialized." - Idempotent MERGE. "Verify that your
mergestatement insideforeachBatchis idempotent. Otherwise, restarts of the streaming query can apply the operation on the same batch of data multiple times." Narrow theONclause to the batch's keys.
4.2 Worked example E3, the headline job: MSK → silver MERGE with a dead-letter path
# E3 - foreachBatch MERGE into Delta silver, idempotent, with DLQ
# At-least-once callback + txnAppId/txnVersion = effectively exactly-once
from delta.tables import DeltaTable
APP_ID = "orders-silver-msk" # change IFF you intentionally start a NEW checkpoint
def upsert_to_silver(batch_df, batch_id):
if batch_df.isEmpty():
return
# batch_df is consumed twice (valid + rejects): persist, or you re-read the source
batch_df.persist()
valid = batch_df.filter("order_id IS NOT NULL AND amount >= 0")
rejects = batch_df.filter("order_id IS NULL OR amount < 0")
(DeltaTable.forName(spark, "lake.silver_orders")
.merge(valid.alias("s"), "s.order_id = t.order_id") # pruned ON clause
.whenMatchedUpdateAll(condition="s.order_ts > t.order_ts")
.whenNotMatchedInsertAll()
.execute()) # must be idempotent
(rejects.write.format("delta").mode("append")
.option("txnAppId", f"{APP_ID}-dlq").option("txnVersion", batch_id)
.saveAsTable("lake.quarantine.orders_rejects"))
# if you also plain-append rows from foreachBatch, stamp them:
# valid.write.format("delta").mode("append") \
# .option("txnAppId", APP_ID).option("txnVersion", batch_id)...
batch_df.unpersist() # never catch/retry inside this function - let the job retry the batch
(deduped.writeStream.format("delta")
.option("checkpointLocation", "/Volumes/lake/ops/ckpt/orders_silver")
.outputMode("update") # accepted by foreachBatch; the table itself gets append/complete
.foreachBatch(upsert_to_silver)
.trigger(processingTime="30 seconds")
.start())4.3 The exactly-once decision tree
5. Change data feed out: the gold fan-out
5.1 The pattern
Create the silver table with CDF on; every downstream consumer that needs changes streams the CDC feed instead of re-reading the table. Each CDF record carries _change_type ∈ insert / update_preimage / update_postimage / delete. Batch replay uses table_changes('tbl', v1, v2) for backfill and audit: the same mechanics Databricks' own CDF blog (2021-06-09) used to build the medallion CDC pattern.
5.2 Worked example E4: CDF stream to gold plus a batch replay
# E4a - silver created with CDF enabled (forward-looking only)
spark.sql("""
CREATE TABLE lake.silver_orders (
order_id STRING, order_ts TIMESTAMP, customer_id STRING,
amount DOUBLE, updated_at TIMESTAMP
) USING DELTA
TBLPROPERTIES (delta.enableChangeDataFeed = true)
""")
# E4b - gold consumer streams the change feed (serverless-friendly: availableNow)
changes = (spark.readStream.format("delta")
.option("readChangeFeed", "true")
.option("startingVersion", 70) # skip history you already backfilled
.table("lake.silver_orders"))
(gold_df.writeStream.format("delta")
.option("checkpointLocation", "/Volumes/lake/ops/ckpt/orders_gold")
.trigger(availableNow=True) # deterministic nightly replay
.toTable("lake.gold.orders_daily"))# E4c - batch replay / audit via table_changes (mirrors the docs' recovery example)
replay = spark.read.format("delta")
.option("readChangeFeed", "true")
.option("startingVersion", 70)
.option("endingVersion", 75)
.table("lake.silver_orders")
# or the SQL form: SELECT * FROM table_changes('silver_orders', 70, 75)"You must use Structured Streaming for Databricks to automatically track versions for your table's change data feed", version tracking is a streaming-consumer behaviour, which is why pairing CDF with availableNow on a schedule is the documented deterministic-replay pattern. Lakeflow pipelines add AUTO CDC (the Applier) for DB→Delta and Delta→Delta without hand-written MERGE, covered in lesson 07; this section is the raw-notebook version you now understand from the inside.
6. Watermarks, state and the bloat failure mode
6.1 What a watermark is for
"Watermarks automatically remove old state data to prevent memory errors and increased processing latency" (watermarks, 2026-09-11). You assert a maximum lateness, withWatermark("event_time", "10 minutes"), and the engine advances a global watermark past max(event_time) − delay. "Queries always process records that arrive within the threshold. Queries might still process records that arrive outside the threshold, but this isn't guaranteed." Output mode changes what you see:
Output mode | Emissions | State | Delta sink? |
|---|---|---|---|
append (default) | Only finalized rows, after the watermark passes | Bounded by watermark | Supported |
update | Rows changed in each trigger | Trimmed by watermark when it advances | Not supported by Delta: use MERGE in foreachBatch |
complete | Every row the operator ever produced | "Aggregation state isn't dropped": unbounded by design | Supported, but "can perform poorly as data scales" |
Stream-stream joins "only support append mode"; "For outer joins, watermarking is mandatory"; for inner joins it is strongly recommended. With multiple watermarks "Structured Streaming uses the minimum as the global watermark" by default; spark.sql.streaming.multipleWatermarkPolicy=max lowers latency but "drops data from the slowest streams… apply with caution." Window durations run "from microseconds up to days. Month durations and longer are not supported."
6.2 Worked example E5: windowed aggregation with the state math visible
# E5 - 5-minute tumbling windows per ad, append mode, watermark 10 minutes
clicks = spark.readStream.format("kafka") \
.option("kafka.bootstrap.servers", "...").option("subscribe", "clicks") \
.option("maxOffsetsPerTrigger", 200_000).load()
parsed = clicks.select(from_json(col("value").cast("string"), click_schema).alias("e")).select("e.*")
windowed = (parsed
.withWatermark("event_time", "10 minutes")
.groupBy(window(col("event_time"), "5 minutes"), col("ad_id"))
.count())
query = (windowed.writeStream.format("delta")
.outputMode("append") # finalized windows only
.option("checkpointLocation", "/Volumes/lake/ops/ckpt/clicks_5m")
.trigger(processingTime="1 minute")
.toTable("lake.silver.clicks_5m"))
# Inspect the state after a few batches:
# query.lastProgress["stateOperators"][0]["numRowsTotal"]
# query.lastProgress["stateOperators"][0]["numRowsDroppedByWatermark"]
# query.lastProgress["eventTime"]["watermark"]Sizing logic (ours on documented semantics): live keys ≈ ads × windows-within-watermark = 5,000 ads × 3 windows (10 min watermark ÷ 5 min window) ≈ 15,000 state rows, trivial. Remove the watermark and state retains every (window, ad_id) forever; add complete mode and every historical row is re-emitted each trigger. The dedup variant matters as much: plain dropDuplicates(["event_id"]) holds state for every key forever; dropDuplicatesWithinWatermark(["event_id"]) (DBR 13.3 LTS+, requires a watermark) only holds keys within the lateness window, "the correct default for idempotent event ingestion when duplicates arrive close in event time."
6.3 Worked example E6: production state defaults and custom state
For all stateful queries on current DBRs, Databricks' documented posture is: RocksDB state store + changelog checkpointing (DBR 13.3 LTS+), "Databricks recommends enabling changelog checkpointing for all Structured Streaming stateful queries"; async state checkpointing and async progress tracking on the production page; no autoscaling for raw Structured Streaming job clusters ("Compute auto-scaling has limitations scaling down"); and state rebalancing on resize (default-on in Lakeflow pipelines; config spark.sql.streaming.statefulOperator.stateRebalancing.enabled since DBR 11.3+). Custom keyed state beyond aggregation/dedup/joins: transformWithState (DBR 16.2+) supersedes the legacy mapGroupsWithState/flatMapGroupsWithState for new code. Shuffle partition count is frozen at checkpoint creation for stateful operators (on-demand repartitioning arrives in DBR 18 LTS+), a silent footgun when someone "just bumps parallelism" on an existing query.
- 📚 Did you know?
VACUUM"skips any directories that begin with_", which is why the Delta streaming docs suggest storing checkpoints "using a directory structure such as/_checkpoints". A one-character naming convention is the difference between a routine maintenance command and a wiped streaming state: pair it with a UC volume path and an IAM policy that forbidss3:DeleteObjecton the checkpoint prefix, and checkpoint deletion stops being a 3 a.m. possibility.
7. Schema evolution on streams: locked plans, additive sinks
"A streaming query's schema is locked in during the planning phase… If the source schema changes mid-execution, the query fails, and the user must restart the streaming query" (schema-evolution, 2026-08-05), which is why production streams must run under a job with auto-restart. On the sink side, Delta accepts additive change only: .option("mergeSchema", "true") on the streaming write "only supports adding new columns". Prefer the per-write option (or SQL WITH SCHEMA EVOLUTION) over the session-wide spark.databricks.delta.schema.autoMerge.enabled, which is legacy. On the source side: new columns flow with mergeSchema; renames and drops need spark.databricks.delta.streaming.allowSourceColumnRename / …allowSourceColumnDrop; type widening is DBR 16.4 LTS+; "Other type changes: Not supported." And the nuclear fact, "Updating a table schema terminates any streams reading from that table" (update-schema, 2026-08-19). Auto Loader's own evolution loop (fail-with-UnknownFieldException-then-restart, schemaEvolutionMode=addNewColumns / rescue) was lesson 05's E-series; the Delta-source story here is the same restart-driven discipline at a different layer.
8. Observability and the near-real-time dashboard
8.1 Three layers of stream monitoring (stream-monitoring, 2026-09-23)
- Query progress: the notebook's returned
StreamingQuery, the Spark UI Streaming tab (.queryName()to disambiguate), and the progress JSON. Key fields:numInputRows,inputRowsPerSecond,processedRowsPerSecond,batchId,batchDuration, thedurationMsbreakdown (getBatch,addBatch,commitOffsets,walCommit),eventTime.watermark, andstateOperators(numRowsTotal,memoryUsedBytes,numRowsDroppedByWatermark, RocksDBcustomMetrics). Source metrics include Delta/Auto LoadernumBytesOutstanding/numFilesOutstanding;reservoirIdmaps toDESCRIBE DETAIL <table>id. StreamingQueryListener(Python and Scala, DBR 11.3 LTS+; UC access-mode floors 15.1+ dedicated, 16.1+ standard Scala): push to Prometheus/Grafana/Kafka. Keep callbacks tiny: "Processing latency with listeners can significantly affect query processing speeds."df.observe(name, agg…): arbitrary per-epoch aggregates emitted intoobservedMetrics(e.g. a malformed-row ratio that alerts before downstream freshness does).
8.2 Dashboards: pull-based, cached, honest
DBSQL/Lakeview dashboards are pull-based: datasets run on a SQL warehouse and results land in a cache that "can return results that are up to 24 hours old even when the underlying data has changed", only an explicit schedule (or a cache miss) refreshes it, and "Serving results from the dashboard cache does not start the SQL warehouse" (dashboards caching, 2026-07-16). The supported near-real-time shape on AWS is therefore: stream writes Delta in seconds; a standalone streaming table in Databricks SQL (CREATE OR REFRESH STREAMING TABLE … SCHEDULE EVERY … / TRIGGER ON UPDATE AT MOST EVERY INTERVAL 5 MINUTES, minimum TRIGGER ON UPDATE interval 1 minute, running on an automatically created serverless pipeline) keeps the table fresh; the dashboard queries that table on its own cron. Sub-minute dashboard push is not a documented DBSQL feature, do not promise it in a design review.
-- Worked example E7 - SQL-native streaming table feeding a scheduled dashboard
CREATE OR REFRESH STREAMING TABLE orders_5m
TRIGGER ON UPDATE AT MOST EVERY INTERVAL 5 MINUTES AS
SELECT window(event_time, '5 minutes') AS w, count(*) AS orders, sum(amount) AS revenue
FROM STREAM(main.bronze.orders)
GROUP BY window(event_time, '5 minutes');9. Production posture: Lakeflow Jobs, and the three "continuous"s
Databricks' production page is unusually direct: "Run production Structured Streaming workloads as scheduled Lakeflow Jobs on Databricks"; "Don't run Structured Streaming workloads using all-purpose compute"; "Schedule Lakeflow Jobs using Continuous mode. This refers to the Databricks Jobs scheduling feature, not the Structured Streaming trigger interval"; "Don't enable autoscaling for compute for Structured Streaming jobs" (compensated by RocksDB state, async state checkpointing and async progress tracking). And the platform recommendation: "Databricks recommends using Lakeflow pipelines for new Structured Streaming pipelines."
The naming trap that breaks architecture reviews: three different things called "continuous":
Name | What it actually is | Status (Oct 2026) |
|---|---|---|
OSS Trigger.Continuous | Spark continuous processing trigger | "Not supported… experimental… Use real-time mode instead" |
Lakeflow pipelines continuous mode | Databricks' managed continuous refresh of declarative pipelines | Supported; the recommended continuous streaming path (lesson 07) |
Jobs continuous scheduling | A Workflows scheduling mode preventing concurrent runs and auto-restarting the task | Unrelated to any trigger; the production page's recommended execution vehicle for streams |
Two more incompatibilities from the same pages: async progress tracking "isn't compatible with the AvailableNow or Once triggers", you cannot combine the cost-optimized trigger with that latency optimization; and "If you try to recover from a query failure with an incremental batch, a trigger interval change doesn't solve the problem", a stuck failed micro-batch must still complete; scale up or start a new checkpoint instead.
10. The traps: fourteen confusions this lesson will not let you keep
- Default trigger as a production default. 3 to 5 s latency with per-few-millisecond storage checks: the documented "unexpected charges" scenario. Set an explicit trigger; prefer
availableNowfor scheduled ingestion. - Deleting a checkpoint to "fix lag". Fresh start, full replay, watermark reset, and Delta silently drops the replay unless
txnAppIdrotates. - Sharing one checkpoint across queries. Explicitly forbidden: "Each query must have a different checkpoint location."
- Same
txnAppIdafter a new checkpoint. Batch IDs restart at 0; Delta ignores the writes. Document the appId per stream; rotate it deliberately. - Catching exceptions inside
foreachBatch. Breaks at-least-once accounting; let the orchestrator retry the whole batch. - Fan-out inside
foreachBatch. Multiple sinks serialize; prefer one streaming write per sink. - Missing
persist()inforeachBatch. Read the micro-batch DataFrame twice without it and you re-read the source. dropDuplicateswithout a bound. State for every key forever; usedropDuplicatesWithinWatermark(DBR 13.3 LTS+) with a watermark.completemode on a growing aggregation. State is never dropped by design; "can perform poorly as data scales."- Kafka first run on default
startingOffsets.latestsilently skips the backlog; setearliestonce, then trust the checkpoint. - Treating small Kafka backlog metrics (DBR 17.1+) as an incident. Persistent non-zero
*OffsetsBehindLatestis expected by design. - The three "continuous"s. Unsupported OSS trigger ≠ Lakeflow continuous mode ≠ Jobs continuous scheduling.
- Serverless + non-
availableNowtrigger. OnlyAvailableNow/Oncestart on serverless: plan compute per trigger. - "The dashboard is real-time because the stream is." The cache is up to 24 hours stale; the schedule, or a DBSQL streaming table underneath, is what makes it fresh.
Comparative Verdict: raw Structured Streaming notebooks vs Lakeflow Declarative Pipelines (as of Oct 2026)
- Raw
writeStreamnotebooks (this lesson's code), the transparent option. Full control over triggers, checkpoints,foreachBatchMERGE,txnAppIddiscipline and rate limits; the right teaching tool and the right choice for one-off jobs, bespoke sinks (OLTP upserts, queues, APIs) and maximum customization. Databricks still fully supports them, when they run as "scheduled Lakeflow Jobs on Databricks" with auto-restart, on jobs compute, without autoscaling. The cost of that control is yours: you own checkpoint placement, state-store configuration (RocksDB + changelog), restart-on-schema-change wiring, and every line of idempotency code.- Lakeflow Declarative Pipelines (formerly Delta Live Tables; lesson 07): the recommended production path. "Databricks recommends using Lakeflow pipelines for new Structured Streaming pipelines" (production page). You declare sources, sinks and expectations; Databricks manages checkpoints and state, runs continuous mode for true continuous streaming (including serverless), applies AUTO CDC instead of hand-written MERGE, rebalances state by default on resize, and offers enhanced autoscaling that raw streaming jobs should not use. The trade-off: less fine-grained control of the micro-batch loop, a pipeline (not notebook) development model, and another concept to govern.
- Auto Loader ingestion (lesson 05) splits the same way: "Lakeflow Spark Declarative Pipelines is the recommended way to run Auto Loader for most production ingestion… If your workload doesn't have low-latency requirements and your priority is minimizing compute cost, you can instead schedule Auto Loader as a triggered batch job that uses
Trigger.AvailableNow."- Verdict in one line: new streaming pipelines → Lakeflow; bespoke sinks, teaching labs and tightly cost-tuned
availableNowbatch jobs → raw Structured Streaming on Lakeflow Jobs with the hygiene of this lesson, and never an all-purpose cluster either way.
2026 Updates (as of October 2026)
What changed in the streaming platform this lesson depends on: each line checked against a primary source:
- 2026-10-08: the triggers page, current wording. Default ≈ 3 to 5 s latency;
realTimedocumented with "under 1 second at the tail, and in common cases around 300 ms";continuous"Not supported… Use real-time mode instead"; on serverless onlyAvailableNow/Onceare supported;Trigger.Oncedeprecated since 11.3 LTS; availableNow floors: files 9.1 LTS, Delta/Auto Loader/Kafka 10.4 LTS, Kinesis 13.1 (docs.../structured-streaming/triggers, footer 2026-10-08). - 2026-10-09: stateful streaming page. RocksDB + changelog checkpointing "recommended for all Structured Streaming stateful queries" (DBR 13.3 LTS+);
transformWithStateDBR 16.2+; state rebalancing default in Lakeflow pipelines; on-demand state repartitioning DBR 18 LTS+; shuffle partitions otherwise frozen at checkpoint creation (docs.../structured-streaming/stateful-streaming, footer 2026-10-09). - 2026-10-05: rate-limit options page.
maxFilesPerTriggerdefault 1000 for Delta/Auto Loader;maxBytesPerTriggersoft, files never split; KafkamaxTriggerDelaydefault 15m;failOnDataLossdefault true; Auto Loader rate limits dynamically configured on DBR 18.0+ (docs.../spark/api-options, page date 2026-10-05). - 2026-09-23: monitoring page. Spark UI Streaming tab,
StreamingQueryListenerfloors (Python/Scala 11.3 LTS+; UC dedicated 15.1+, standard Scala 16.1+), progress fields includingstateOperatorsanddurationMsbreakdown,df.observeas the third layer (docs.../structured-streaming/stream-monitoring, footer 2026-09-23). - 2026-09-11: checkpoints and watermarks pages. "Each query must have a different checkpoint location"; source-evolution (named sources) DBR 18.2+ and irreversible per checkpoint;
dropDuplicatesWithinWatermarkDBR 13.3 LTS+ requiring a watermark; window durations microseconds→days, no months (docs, footers 2026-09-11). - 2026-08-24: foreach and Delta streaming pages.
foreachBatchat-least-once; DBR 14.0+ requirestxnAppId/txnVersionfor Delta targets; "Do not catch and retry exceptions locally"; separate streaming write per sink preferred; new-checkpoint restarts demand a newtxnAppId(docs, dated 2026-08-24). - 2026-10-12, the certification weighting that pays for this lesson. The Data Engineer Associate blueprint (exam guide live May 4, 2026) lists Data Ingestion and Loading at 21%, the second-heaviest domain, and the one this lesson's triggers, checkpoints and exactly-once worked examples are built to cover (Databricks certification pages, verified 2026-10-12).
Real-World Case Studies
📋 Case A: Burberry (luxury retail, Cloud: AWS) Challenge. Daily clickstream batches arriving "anywhere between 2:00 and 7:00 PM GMT", and a cloud warehouse that pre-aggregated away the detail needed for Customer 360. Stack on AWS. Databricks Data Intelligence Platform with Snowplow Behavioral Data Platform for real-time Customer 360: the story page carries Databricks' own
Cloud: AWStag (https://www.databricks.com/customers/burberry, undated page, accessed 2026-10). Reported outcome. "99% Reduction in latency for customer clickstream data · 52x Increase in cookie duration"; "we've broken free from daily data batches and formed an accurate real-time view of our customers' digital activity" (same URL, accessed 2026-10; story origin 2023). Maps to this lesson. Sections 1 and 9: a clickstream latency SLA is a business requirement; Burberry's fix was an event-driven lakehouse path, not a bigger nightly window. The trigger choice (availableNowon file arrivals, or a seconds-interval stream from a bus) is exactly the E1 decision, and the dashboard freshness promise belongs to section 8's cache honesty. ⏳ Temporal note: a 2023-origin figure still displayed in 2026: quote it as what Burberry reported at the time, not a platform guarantee.
📋 Case B, Shell (energy, Cloud: AWS, Azure, dual-cloud story) Challenge. Millions of industrial time-series tags, legacy infrastructure, exponential data growth and a shortage of data-engineering skills: ETL at IoT scale. Stack. Shell.ai's platform on Databricks: "Delta Live Tables" (now Lakeflow / Spark Declarative Pipelines), Delta Lake, and structured streaming ingestion via the open-source RTDIP framework; the story page lists AWS and Azure, so we teach the AWS half as S3-backed Delta plus streaming ingestion (https://www.databricks.com/customers/shell, undated, accessed 2026-10). Reported outcome. "1.3 trillion rows of sensor data processed with Delta Live Tables · Millions of dollars saved with data-driven operations" (Shell DLT PDF, August 2021); "Real-time data consumption has increased 15-fold over the last 5 years at Shell"; RTDIP "downloaded 25,000+ times/month" (https://www.databricks.com/blog/developing-time-series-lakehouse-shell, 2023-10-09). Maps to this lesson. Sections 6 and 9: a trillion-row sensor estate survives only with watermark-bounded state and declarative continuous pipelines; the raw-notebook skills in this lesson are what let you debug the managed pipeline when it misbehaves. ⏳ Temporal note: DLT PDF figures are 2021-vintage under the historic product name; the RTDIP numbers are 2023. The story spans AWS and Azure: only the streaming pattern is cloud-portable.
📋 Case C: Asurion (insurance & device protection, Cloud: AWS) Challenge. 8,000 tables, 10,000 views and ~600 data marts with per-table ETL mappings and a duplicated batch/speed layer that could not absorb new sources cheaply. Stack on AWS. Spark Structured Streaming, Auto Loader and
foreachBatchinto Delta, ingesting "database CDC feeds, APIs and flat files to streams from Kinesis, Kafka, SNS and SQS": a production data lake explicitly "on AWS" (https://www.databricks.com/blog/2021/09/16/large-scale-etl-and-lakehouse-implementation-at-asurion.html, 2021-09-16). Reported outcome. "The Asurion data lake executes 4,000+ streaming jobs and hosts over 4,000 tables in a production data lake on AWS" (https://www.databricks.com/blog/2021/05/10/aws-guide-to-data-ai-summit-featuring-disney-comcast-capital-one-and-mcdonalds.html, 2021-05-10). Maps to this lesson. Sections 4 and 10: at 4,000+ streaming jobs, the architecture is a parameterisedforeachBatch/checkpoint framework (one idempotent pattern, many feeds), and the AWS-native source list (Kinesis, SNS, SQS, MSK) is why this course teaches MSK IAM auth and S3 checkpoint hygiene as first-class skills. ⏳ Temporal note: 2021 figures reported at the time; re-check any workspace concurrency statement from that blog against current docs before reusing it.
Key Takeaways:
- Five triggers, four real choices. Default ≈ 3 to 5 s (processingTime 0, an API-call storm on S3);
processingTime='10 seconds'the cost-balancing default;availableNow=Truedrain-then-exit (DBR 10.4 LTS+ for Kafka/Delta/Auto Loader; the serverless-recommended trigger; replaces deprecatedOnce);realTime='5 minutes'for sub-second ops (<1 s tail, ~300 ms common, dedicated compute);continuousis not supported, "use real-time mode instead." - The checkpoint is the query. Offsets + commits + state + metadata on a UC volume path (S3); one per query; no lifecycle policies; safe to change on restart = filters/rate limits/triggers; unsafe = source set, state schema, backend, sink type, and deletion means fresh start, full replay, and a required
txnAppIdrotation or Delta drops the writes. - Exactly-once is a chain: checkpointed offsets → deterministic replay → idempotent sink. Delta appends give it via the transaction log;
foreachBatchgives only at-least-once until you bindtxnAppId/txnVersion=batchId, keep the MERGE idempotent, never retry locally, and prefer one streaming write per sink. - CDF is the change path.
delta.enableChangeDataFeed=trueat creation;readChangeFeed=truestreaming withstartingVersion;_change_typerows;table_changes()for batch replay; "Databricks recommends streaming from the CDC feed… rather than directly from the table." - Watermarks bound state; their absence is state bloat. Append waits out the watermark; update emits deltas; complete never drops state. Outer joins require watermarks; multiple watermarks combine via min by default;
dropDuplicatesWithinWatermark(DBR 13.3 LTS+) is the bounded dedup default; RocksDB + changelog checkpointing is recommended for all stateful queries (DBR 13.3 LTS+). - ⚠️ Watch
stateOperators.numRowsTotalandmemoryUsedBytesclimbing with a flatnumRowsDroppedByWatermarkmeans the watermark is wrong or missing: fix the watermark, do not delete the checkpoint. - Streams lock their schema. Plan-time lock; source changes fail the query (auto-restart job required); sinks accept new columns only via
mergeSchema=true; ALTER TABLE on a source terminates its readers; type widening is DBR 16.4 LTS+. - Operate from the progress JSON.
numInputRows,batchDuration,durationMsbreakdown,eventTime.watermark,stateOperators; Spark UI Streaming tab;StreamingQueryListenerto Prometheus (keep callbacks tiny);df.observefor per-epoch custom metrics. - Dashboards are pull-based. Cache up to 24 hours, refreshed only by schedule or miss; DBSQL streaming tables on serverless pipelines (
TRIGGER ON UPDATEminimum 1 minute) are the documented near-real-time substrate: sub-minute push is not a DBSQL feature. - Production posture: Lakeflow Jobs with continuous scheduling and auto-restart, jobs compute (never all-purpose), no autoscaling on raw streaming, RocksDB state, async progress tracking (not compatible with
availableNow), and, Databricks' own words for new pipelines, "Databricks recommends using Lakeflow pipelines for new Structured Streaming pipelines." - Case anchors on AWS: Burberry: 99% clickstream latency reduction, 52x cookie duration (undated, accessed 2026-10); Shell: 1.3 trillion sensor rows via DLT/RTDIP (2021/2023, dual-cloud); Asurion: 4,000+ streaming jobs on a lake "on AWS" with
foreachBatch(2021-05-10 / 2021-09-16).
Practice Questions
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A notebook stream on classic compute omits .trigger(...). According to the October 2026 triggers page, what is the documented behaviour?
How to answer
Multiple-choice question with 2 options. Select one option, then choose Check Answer.
Setting .trigger(continuous='1 second') on a Databricks streaming query enables OSS continuous processing for lower latency than micro-batches.
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
Which statement about the four things inside a Structured Streaming checkpoint directory is correct as of October 2026?
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A team deletes the checkpoint of a foreachBatch query that writes Delta with txnAppId/txnVersion and restarts it without changing anything. What happens?
How to answer
Fill-in-the-blank exercise with 5 blanks. Type the missing words, then check your answer.
Fill in the trigger and state facts exactly as the October 2026 docs define them:
Trigger.Once is deprecated since Databricks Runtime blank LTS in favour of availableNow; on serverless compute only Trigger.AvailableNow and the deprecated Trigger.blank are supported; realTime mode's parameter (for example realTime='5 minutes') is the length of a micro-batch, with documented end-to-end latency under 1 second at the tail, around blank ms in common cases and as low as 5 ms; dropDuplicatesWithinWatermark requires DBR blank LTS or above and an existing watermark; and OSS continuous processing is blank on Databricks.
Word Bank, drag words into the blanks above:
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A stream-stream join between an orders stream and a payments stream must emit a row for every order within 30 minutes even if its payment never arrives. What do the October 2026 watermark docs require?
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
An ingestion pipeline deduplicates on event_id because upstream Kafka producers retry. Which configuration is the documented, state-bounded default as of Oct 2026?
How to answer
Matching exercise with 6 pairs. Match each item on the left with its answer on the right.
Match each trigger or execution concept to its verified October 2026 property:
Drag answers from here:
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
Which statement about streaming change data feed is correct per the October 2026 docs?
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A brand-new Kafka streaming query is started with all defaults to backfill a topic. What is the documented risk?
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
On Amazon MSK with IAM authentication and no Unity Catalog service credential, which client configuration is correct per the October 2026 Kafka authentication page?
How to answer
Ordering exercise with 7 items. Move them into the correct order, then check your answer.
Order the incident response after a streaming silver MERGE query is found to be silently dropping replayed batches (as of Oct 2026):
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
Which production posture do the Databricks production considerations page recommend for Structured Streaming as of October 2026?
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
What is the documented freshness model for a Databricks SQL / Lakeview dashboard sitting on top of a streaming-written Delta table?
Hover a bar: availableNow runs ~20 minutes per hourly schedule (1 on the scale, it turns itself off); a processingTime='10 seconds' stream is up whenever data flows (36, roughly a full month of uptime against a 20-minute job); the unspecified default adds a per-few-millisecond storage poll on top of continuous compute (60, the documented API-call multiplier, which lesson 05 priced at 25,920/month in LIST alone on a 2M-file prefix versus $28.80 at a 15-minute availableNow); real-time mode requires dedicated compute and less frequent checkpoints (80, the price of sub-second latency, and the reason it is reserved for fraud and personalization); the OSS continuous trigger is free because you cannot run it (0, "Not supported"). The scale is a teaching model of documented semantics, not a platform metric, do the DBU arithmetic with your own cluster shape and system.billing.list_prices.