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
In a nutshell
  • readStream and writeStream, 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.

StreamWindowtumbling, hopping, sliding, sessionAggregate per windowOutput table

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"))
ElementPurpose
checkpointLocationStores progress so the query restarts where it stopped, with exactly-once writes to Delta
withWatermarkHow late events may arrive; older state is dropped, and windows can be finalized
outputModeappend writes finished windows only; complete rewrites the whole result; update writes changed rows
triggerprocessingTime 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

WindowBehaviourExample
TumblingFixed size, no overlap, each event in exactly one windowCount every 5 minutes
HoppingFixed size, moves forward by a hop smaller than the size, so windows overlap10 minute window every 5 minutes
SlidingOutput whenever an event enters or leaves the windowAlert if more than 3 errors in any 10 minutes
SessionGroups events separated by less than a gap timeout; size variesUser activity until 30 minutes of silence
SnapshotGroups events with the same timestampReadings sent together

Syntax in each engine:

EngineTumblingOverlappingSession
EventstreamGroup by operator, TumblingGroup by, Hopping or SlidingGroup by, Session
SparkF.window("t", "5 minutes")F.window("t", "10 minutes", "5 minutes")F.session_window("t", "30 minutes")
KQLsummarize ... by bin(t, 5m)No dedicated operatorrow_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

  • readStream and writeStream, 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 sort or serialize.
Watch out

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_window in Spark or row_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.