4.3 Streaming Inference with Delta Live Tables

Key Takeaways

  • Delta Live Tables (DLT) is declarative: you define the tables and their queries with `@dlt.table`, and DLT manages the streams, dependencies, compute, and retries.
  • A model is applied inside a DLT pipeline by registering it as a Spark UDF with `mlflow.pyfunc.spark_udf` and calling that UDF in the table's query.
  • Streaming tables read with `dlt.read_stream` (or `spark.readStream`), so each pipeline update processes only new rows rather than rescanning history.
  • DLT manages checkpoints for its own streaming tables, which is what makes exactly-once recovery automatic rather than a `checkpointLocation` you configure by hand.
  • Data-quality expectations — `@dlt.expect`, `@dlt.expect_or_drop`, `@dlt.expect_or_fail` — enforce contracts on both the input features and the predictions.
Last updated: August 2026

4.3 Streaming Inference with Delta Live Tables

Streaming Inference with Spark Structured Streaming

Structured Streaming applies MLflow UDFs incrementally against continuous data streams with micro-batch processing.

Implementation with Checkpointing

import mlflow.pyfunc
from pyspark.sql.functions import struct, col

# 1. Register distributed scoring UDF
model_uri = "models:/enterprise_ml.iot.turbine_anomaly@champion"
anomaly_udf = mlflow.pyfunc.spark_udf(spark, model_uri=model_uri, result_type="double")

# 2. Read streaming DataFrame from Bronze ingestion stream
streaming_raw_df = spark.readStream \
    .format("delta") \
    .table("enterprise_ml.iot.bronze_sensor_telemetry")

# 3. Apply inference transformation
streaming_scored_df = streaming_raw_df.withColumn(
    "anomaly_score",
    anomaly_udf(struct("vibration_level", "rpm", "temperature", "pressure"))
).filter("anomaly_score > 0.85")  # Alert threshold

# 4. Write stream to Gold Delta sink with mandatory checkpointing
query = streaming_scored_df.writeStream \
    .format("delta") \
    .outputMode("append") \
    .option("checkpointLocation", "dbfs:/checkpoints/turbine_anomaly_scoring") \
    .trigger(processingTime="10 seconds") \
    .toTable("enterprise_ml.iot.gold_critical_anomalies")

The Critical Role of checkpointLocation

In Spark Structured Streaming, the checkpointLocation directory stores the write-ahead transaction log, offset metadata, and state information in durable cloud storage. If an executor or cluster restarts, the stream resumes from the exact recorded offset without re-scoring duplicate records or dropping events, guaranteeing exactly-once processing when combined with Delta Lake sinks.


Declarative ML Pipelines with Delta Live Tables (DLT)

Delta Live Tables (DLT) simplifies production ETL and inference by replacing manual stream and cluster management with declarative pipeline definitions.

import dlt
import mlflow.pyfunc
from pyspark.sql.functions import struct

# Broadcast model UDF inside DLT pipeline context
model_uri = "models:/supply_chain.logistics.delivery_delay_model@champion"
predict_delay = mlflow.pyfunc.spark_udf(spark, model_uri, result_type="double")

@dlt.table(
    name="silver_cleaned_shipments",
    comment="Ingests and validates raw logistics telemetry"
)
@dlt.expect_or_drop("valid_shipment_id", "shipment_id IS NOT NULL")
@dlt.expect_or_drop("valid_distance", "distance_miles > 0")
def silver_cleaned_shipments():
    return dlt.read_stream("bronze_shipment_events")

@dlt.table(
    name="gold_predicted_delays",
    comment="Applies MLflow model to forecast delivery delays"
)
@dlt.expect_or_fail("valid_prediction", "predicted_delay_minutes >= 0")
def gold_predicted_delays():
    features = ["distance_miles", "weather_severity", "traffic_index", "carrier_historical_delay"]
    return dlt.read("silver_cleaned_shipments").withColumn(
        "predicted_delay_minutes",
        predict_delay(struct(*features))
    )

Why DLT Rather Than a Hand-Written Stream

Both approaches apply the same MLflow UDF to the same events. The difference is how much operational machinery you own.

ConcernHand-written Structured StreamingDelta Live Tables
CheckpointingYou set checkpointLocation on every writeStreamManaged by the pipeline for its streaming tables
Dependency orderingYou sequence the jobs yourselfInferred from the dlt.read / dlt.read_stream graph
Compute lifecycleYou size and manage the clusterProvisioned per pipeline update; autoscaling is a pipeline setting
Retries and recoveryCustom job-level retry logicBuilt into the pipeline update
Data qualityCustom filters and assertionsFirst-class @dlt.expect* decorators with recorded metrics
Lineage and observabilityAssembled manuallyPipeline graph and per-expectation metrics in the UI

This is why a scenario that mentions fluctuating event volume, dynamic resizing, and declarative pipeline management points to DLT, while one that mentions a single bespoke stream with custom state handling points to Structured Streaming.

Streaming Tables vs. Materialised Views

DefinitionSemanticsUse for
dlt.read_stream("upstream")Incremental: each update processes only new rowsAppend-only event ingestion and scoring
dlt.read("upstream")Recomputed from the full upstream contents on each updateAggregates, joins, dimension-style tables

Scoring an unbounded event feed should read as a stream, so the model is applied once per event rather than re-applied to the entire history on every update. That difference dominates both cost and latency at scale.

Expectations on Predictions, Not Just Inputs

Quality constraints are as valuable on the output of a model as on its input:

@dlt.table(name="gold_scored_events")
@dlt.expect("score_in_range", "anomaly_score BETWEEN 0 AND 1")
@dlt.expect_or_drop("has_entity", "device_id IS NOT NULL")
def gold_scored_events():
    return dlt.read_stream("silver_events").withColumn(
        "anomaly_score", predict_udf(struct(*feature_cols))
    )
  • @dlt.expect records violations as pipeline metrics but keeps the rows — the right choice for monitoring prediction drift.
  • @dlt.expect_or_drop filters violating rows out of the target table.
  • @dlt.expect_or_fail aborts the pipeline update, reserved for violations that must never reach downstream consumers.

Because the violation counts are recorded per expectation and per update, a rising "score out of range" rate becomes an early warning that the model or its inputs have shifted — without a separate monitoring job.

The Serving-Endpoint Alternative Inside a Pipeline

A pipeline can also call a Model Serving endpoint from a UDF rather than loading the model into the stream. That is the right design when the model is very large, when it runs on a GPU, or when several consumers must share one governed deployment. It is the wrong design for high-volume event scoring, where a per-event HTTPS round trip adds latency and cost that in-pipeline UDF scoring avoids entirely.

The Name Changed; the Objective Did Not

The exam guide dated March 1, 2025 words this objective as "Identify how streaming inference is performed with Delta Live Tables," and its own sample question describes tens of thousands of events per second with fluctuating volume and compute that must resize dynamically. The keyed answer there is a Delta Live Tables pipeline applying the algorithm as a Spark UDF — precisely the tradeoff above. The pipeline supplies the autoscaling and the managed update semantics, while applying the model in process avoids an HTTPS round trip per event. Calling a serving endpoint from the pipeline would add a network hop to every event and force two components to scale independently where one suffices.

Since the 2025 Data + AI Summit, Databricks has folded Delta Live Tables into Lakeflow; the documentation now calls these Lakeflow pipelines, and the declarative framework itself was open-sourced as Spark Declarative Pipelines. Databricks states that no migration is required — existing DLT code still runs — and the concepts carry over directly: streaming tables, materialized views, expectations, and pipeline updates all mean what they meant before. In current Python, import dlt is superseded by from pyspark import pipelines as dp, where @dp.table creates a streaming table, @dp.materialized_view creates a materialized view, and the old @dlt.view becomes @dp.temporary_view. Expect the exam itself to use the older Delta Live Tables vocabulary, because that is the language of the live exam guide.

Loading diagram...
Distributed Batch and Streaming Inference Execution Lifecycle
Test Your Knowledge

In a production Spark Structured Streaming pipeline applying an MLflow UDF to continuous telemetry events, what is the mandatory requirement for guaranteeing exactly-once fault recovery across stream restarts?

A
B
C
D
Test Your Knowledge

A podcast platform scores a 10-minute engagement window over an event feed that fluctuates between hundreds and tens of thousands of events per second. The team wants the pipeline compute to resize dynamically and wants declarative management of the tables and their dependencies. Which design fits best?

A
B
C
D
Test Your Knowledge

In a DLT pipeline that scores an append-only event feed, why should the scoring table be defined with dlt.read_stream(...) rather than dlt.read(...)?

A
B
C
D