Databricks DE ยท Topic 4.4

CDC, REST/JSON & Streaming Ingestion

Beyond files: capture database changes (CDC), pull REST/JSON APIs, and stream events from Kafka / Azure Event Hubs into Delta. This deep dive walks each with the code.

Kafka / Event HubsCDC via Lakeflow ConnectREST + JSONexactly-once

The one-liner

Three non-file paths into the lakehouse: database CDC (Lakeflow Connect), REST/JSON pulls, and message-bus streaming (Kafka/Event Hubs via Structured Streaming format("kafka")) โ€” all landing in Delta.
๐Ÿ“ป Analogy: files are mail in a mailbox; a message bus (Kafka) is a live radio feed you tune into; CDC is a security camera on a database recording every change as it happens.

Which ingestion path? โ€” click a source

โ–ธ match the source to the right mechanism

Message bus โ†’ Structured Streaming. readStream.format("kafka") with kafka.bootstrap.servers + subscribe โ†’ deserialize value โ†’ write to Delta. Near real-time, exactly-once via checkpoint.
Database โ†’ Lakeflow Connect (CDC). Use a managed database connector (ingestion gateway + CDC) to mirror inserts/updates/deletes continuously โ€” no hand-coded change tracking. Apply downstream with AUTO CDC (5.4).
REST API โ†’ land JSON โ†’ parse. Handle auth/pagination/rate-limits, land raw JSON to a Volume, then Auto Loader / from_json into bronze.

1 ยท Read from Kafka / Event Hubs

subscribe (exact) / subscribePattern (regex) / assign (partitions) select topics. startingOffsets = latest or earliest; maxOffsetsPerTrigger caps batch size so a backlog doesn't overwhelm you.

PySpark

df = (spark.readStream.format("kafka")
        .option("kafka.bootstrap.servers", "host:9092")
        .option("subscribe", "events")            # or subscribePattern / assign
        .option("startingOffsets", "latest")      # "earliest" to backfill
        .option("maxOffsetsPerTrigger", 500000)   # cap per micro-batch
        .load())

SQL โ€” read_kafka streaming TVF

CREATE OR REFRESH STREAMING TABLE cat.sch.bronze_raw AS
SELECT * FROM STREAM read_kafka(bootstrapServers => 'host:9092', subscribe => 'events');

2 ยท Kafka rows are binary โ€” deserialize the value

โ–ธ toggle to see why you must parse value

Deserialize + keep metadata

from pyspark.sql.functions import col, from_json
schema = "user_id INT, event STRING, ts TIMESTAMP"
parsed = (df.select(
            from_json(col("value").cast("string"), schema).alias("d"),  # deserialize
            col("topic"), col("partition"), col("offset"), col("timestamp"))
          .select("d.*", "topic", "partition", "offset", "timestamp"))   # keep offsets for replay

3 ยท Write to Delta โ€” exactly-once via checkpoint

The checkpoint gives exactly-once + restart safety (one checkpoint per stream). Event Hubs is consumed through the same Kafka reader (Kafka protocol). Track lag with avgOffsetsBehindLatest.

(parsed.writeStream
   .option("checkpointLocation", "/Volumes/cat/sch/vol/_ck_kafka")  # unique per stream
   .trigger(availableNow=True)        # batch-style catch-up; omit for always-on
   .toTable("cat.sch.bronze_events"))

4 ยท Database CDC & REST/JSON

# After landing raw JSON to a Volume, parse incrementally with Auto Loader
raw = (spark.readStream.format("cloudFiles")
         .option("cloudFiles.format", "json")
         .option("cloudFiles.schemaLocation", "/Volumes/cat/sch/vol/_api_schema")
         .load("/Volumes/cat/sch/vol/api_landing"))

Pick the path per source

SourceMechanismNotes
Files in object storageAuto Loader / COPY INTO (4.1โ€“4.2)incremental file ingest
SaaS app / OLTP databaseLakeflow Connect (4.3)managed connector / CDC
Kafka / Event HubsStructured Streaming format("kafka")near real-time events
REST / JSON APIland raw โ†’ Auto Loader / from_jsonhandle auth/pagination

Why it matters

๐Ÿ“ก Real-world use case: IoT sensors publish to Kafka; a streaming job parses the JSON value into a bronze Delta table in near real-time, while a Lakeflow Connect CDC connector mirrors the orders DB into the same lakehouse.

Uses, edge cases & limitations

Common gotchas