Ingest and Transform Data · Topic 6 of 6
Spark Structured Streaming, KQL Processing and Windowing Functions
DP-700 stream processing: Spark structured streaming with checkpoints and watermarks, KQL update policies and materialized views, and window types.
- 5 min
readStreamandwriteStream, with a checkpoint for restart and exactly-once writes.- A watermark limits lateness and lets append mode emit finished windows.
- Update policies transform each ingested batch; materialized views maintain aggregates.
What it is
Streaming data never "finishes", so processing works on small batches as they arrive and on time windows rather than whole tables. In Fabric you process streams with Spark structured streaming in a notebook or Spark job definition, or with KQL inside an Eventhouse.
Windowing functions group events into time intervals, such as "count of readings per device every five minutes". The same window types appear in Eventstream, Spark and KQL.
How it works
Spark structured streaming
A streaming DataFrame is read with readStream and written with writeStream. Spark processes new data as a series of micro-batches.
from pyspark.sql import functions as F
events = spark.readStream.table("bronze_telemetry")
per_device = (events
.withWatermark("event_time", "10 minutes")
.groupBy(F.window("event_time", "5 minutes"), "device_id")
.agg(F.avg("temperature").alias("avg_temp"), F.count("*").alias("readings")))
query = (per_device.writeStream
.format("delta")
.outputMode("append")
.option("checkpointLocation", "Files/checkpoints/telemetry_5min")
.trigger(processingTime="1 minute")
.toTable("silver_telemetry_5min"))
| Element | Purpose |
|---|---|
checkpointLocation | Stores progress so the query restarts where it stopped, with exactly-once writes to Delta |
withWatermark | How late events may arrive; older state is dropped, and windows can be finalized |
outputMode | append writes finished windows only; complete rewrites the whole result; update writes changed rows |
trigger | processingTime for regular micro-batches, availableNow=True to process what is waiting and stop |
For a stream that must run for days, use a Spark job definition with a retry policy rather than an interactive notebook.
Process data by using KQL
In an Eventhouse, transformation happens at ingestion or as a precomputed view.
An update policy runs a query on every new batch in a source table and writes the result into a target table.
.create-or-alter function ParseTelemetry() {
RawTelemetry
| extend DeviceId = tostring(Payload.deviceId),
Temperature = todouble(Payload.temperature),
EventTime = todatetime(Payload.ts)
| project DeviceId, Temperature, EventTime
}
.alter table Telemetry policy update
@'[{"IsEnabled": true, "Source": "RawTelemetry", "Query": "ParseTelemetry()", "IsTransactional": false}]'
A materialized view keeps an aggregation up to date in the background, so queries read precomputed results.
.create materialized-view TelemetryHourly on table Telemetry
{
Telemetry
| summarize AvgTemp = avg(Temperature), Readings = count() by DeviceId, bin(EventTime, 1h)
}
Use update policies for row-level parsing and enrichment, and materialized views for aggregates or "latest value per key" with arg_max.
Windowing functions
| Window | Behaviour | Example |
|---|---|---|
| Tumbling | Fixed size, no overlap, each event in exactly one window | Count every 5 minutes |
| Hopping | Fixed size, moves forward by a hop smaller than the size, so windows overlap | 10 minute window every 5 minutes |
| Sliding | Output whenever an event enters or leaves the window | Alert if more than 3 errors in any 10 minutes |
| Session | Groups events separated by less than a gap timeout; size varies | User activity until 30 minutes of silence |
| Snapshot | Groups events with the same timestamp | Readings sent together |
Syntax in each engine:
| Engine | Tumbling | Overlapping | Session |
|---|---|---|---|
| Eventstream | Group by operator, Tumbling | Group by, Hopping or Sliding | Group by, Session |
| Spark | F.window("t", "5 minutes") | F.window("t", "10 minutes", "5 minutes") | F.session_window("t", "30 minutes") |
| KQL | summarize ... by bin(t, 5m) | No dedicated operator | row_window_session() |
In KQL, bin() is the everyday tumbling window. Row functions such as prev(), next(), row_cumsum() and row_window_session() need serialized (sorted) rows.
Clicks
| sort by UserId asc, Timestamp asc
| extend SessionStart = row_window_session(Timestamp, 4h, 30m, UserId != prev(UserId))
| summarize Clicks = count(), Duration = max(Timestamp) - min(Timestamp) by UserId, SessionStart
Here a new session starts after 30 minutes of inactivity, after 4 hours in total, or when the user changes.
A worked example
Temperature readings arrive in a lakehouse bronze table, some up to five minutes late. The team needs five minute averages per device in a silver Delta table, with no duplicate windows after a restart.
A Spark structured streaming job reads the bronze table, sets withWatermark("event_time", "10 minutes"), groups by a five minute window, and writes in append mode with a checkpoint. Late readings within the watermark update their window before it is emitted, and the checkpoint prevents reprocessing after restarts.
How the exam asks about this
- "Count events in fixed, non-overlapping five minute intervals." A tumbling window,
bin(Timestamp, 5m)in KQL. - "Group a user's clicks until 30 minutes of inactivity." A session window.
- "Restart a streaming query without reprocessing data." Set a
checkpointLocation.
What to remember for the exam
readStreamandwriteStream, with a checkpoint for restart and exactly-once writes.- A watermark limits lateness and lets append mode emit finished windows.
- Update policies transform each ingested batch; materialized views maintain aggregates.
- Tumbling has no overlap; hopping overlaps by the hop; session windows end after a gap.
- KQL row window functions need serialized input, usually from
sortorserialize.
Two streaming queries must never share a checkpoint folder. Each query needs its own checkpointLocation, or progress tracking is corrupted.
Common mistakes
Aggregating in append mode without a watermark. Spark cannot know when a window is complete, so the query is rejected.
Calling a hopping window "tumbling" because both have a fixed size. Only tumbling windows do not overlap.
Using prev() on unsorted rows. Without serialization, "previous" has no meaning and the query fails.
Check yourself
What does the checkpoint location do in a structured streaming query?
It stores the query's progress and state so it can restart where it stopped without duplicating output.
Which Eventhouse feature parses raw rows into a clean table as data is ingested?
An update policy on the target table, running a function over the source table's new rows.
What window type groups events until a period of inactivity?
A session window, such as
session_windowin Spark orrow_window_session()in KQL.
Topic 6 of 6 in Ingest and Transform Data
Test yourself on Ingest and Transform Data
The full Microsoft DP-700 question bank has 645 questions, with timed practice exams and an explanation for every answer.
This study guide is independent and is not affiliated with, endorsed by, or sponsored by Microsoft. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.