This is an automated email from the ASF dual-hosted git repository.
cloud-fan pushed a commit to branch branch-4.3
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/branch-4.3 by this push:
new a1f2be744147 [SPARK-57235][DOC][SS] Add documentation for stateful
queries in RTM
a1f2be744147 is described below
commit a1f2be7441471e3d5c96567354914f4ce6a511b2
Author: Boyang Jerry Peng <[email protected]>
AuthorDate: Wed Aug 12 10:40:08 2026 +0800
[SPARK-57235][DOC][SS] Add documentation for stateful queries in RTM
### What changes were proposed in this pull request?
This is a documentation-only PR. It updates
`docs/streaming/real-time-mode.md` to document Real-Time Mode's (RTM) support
for **stateful queries** -- streaming **deduplication**
(`dropDuplicates`) and streaming **aggregation**
(`groupBy(...).agg(...)`, including windowed aggregation) -- which the page
previously stated were unsupported. Specifically:
- **Overview, comparison table, and "How does this compare"**: revise the
"stateless only" framing to state that RTM supports stateless queries and,
starting in Spark 4.3.0, a first set
of stateful queries (deduplication and aggregation).
- **New "How Stateful Queries Work" section**: explains why a stateful
operator needs a shuffle, why an ordinary materialize-then-read shuffle would
deadlock a long-running RTM task,
and how RTM instead runs a **pipelined shuffle** (scheduling the producer
and consumer stages as one concurrent pipelined group) over a **streaming
shuffle** (record-by-record network
transport, no intermediate files). Notes that every shuffle on the
streaming path is pipelined this way -- including a plain `repartition` -- with
range partitioning the one exception.
- **Supported Queries**: split into *Stateless operations* (since 4.1.0)
and *Stateful operations* (since 4.3.0); document that `withWatermark` now
takes effect for windowed aggregation
(late-data drop and state eviction).
- **Not supported**: enumerate what remains unsupported and the error
each raises -- other stateful operations (stream-stream joins,
`flatMapGroupsWithState`, `transformWithState`,
session windows, `dropDuplicatesWithinWatermark`), range partitioning,
and distinct aggregates (noting the last is a general Structured Streaming
restriction, not RTM-specific).
- **New examples**: end-to-end Python/Scala/Java examples for
deduplication and streaming aggregation over Kafka.
- **New "State store defaults" and "Incompatible configurations"
sections**: document the state-store configuration RTM applies at query start
(RocksDB provider, checkpoint format v2,
changelog checkpointing, `sortBeforeRepartition`) as soft defaults, the
v2-checkpoint requirement and the
`spark.sql.streaming.realTimeMode.dangerouslyAllowCheckpointV1.enabled` escape
hatch, and the explicit config values that are rejected up front with
`STREAMING_REAL_TIME_MODE.SQL_CONFIGURATION_NOT_SUPPORTED`.
- **Best Practices**: add cluster-sizing guidance for stateful queries
(the producer and consumer stages run concurrently and both hold task slots for
the whole batch, so size for the
sum).
The doc content was reviewed against the RTM code and tests; where the
code proved otherwise, the text was corrected (a plain `repartition` is
supported and pipelined, not rejected;
`sortBeforeRepartition` is a soft default whose incompatible explicit
value is rejected at startup, not a silent hard override).
### Does this PR introduce _any_ user-facing change?
No. Documentation only.
### How was this patch tested?
Documentation-only change; no tests. Verified by building the docs site
and checking the rendered page, in-page anchor links, and code-tab formatting.
Factual claims were cross-checked
against the RTM implementation and its test suites.
### Was this patch authored or co-authored using generative AI tooling?
Co-authored with Claude Code
Closes #57904 from jerrypeng/stack/pipelined-shuffle-pr10-rtm-docs.
Authored-by: Boyang Jerry Peng <[email protected]>
Signed-off-by: Wenchen Fan <[email protected]>
(cherry picked from commit 09da4a14b329be43ce19a88a9bd72ebbc8bc3d0b)
Signed-off-by: Wenchen Fan <[email protected]>
---
docs/streaming/real-time-mode.md | 326 ++++++++++++++++++++++++++++++++++++---
1 file changed, 305 insertions(+), 21 deletions(-)
diff --git a/docs/streaming/real-time-mode.md b/docs/streaming/real-time-mode.md
index 51a8ba04c073..6c4672924066 100644
--- a/docs/streaming/real-time-mode.md
+++ b/docs/streaming/real-time-mode.md
@@ -30,10 +30,14 @@ It is intended for operational workloads
that must react to data the moment it arrives, such as fraud detection,
real-time alerting, and live
personalization.
-In this release, Real-time Mode in Apache Spark supports **stateless queries**
only -- projections,
-filters and other map-like operations, unions, and stream-static joins.
Stateful operations such as
-streaming aggregations, deduplication, stream-stream joins, and
`transformWithState` are not yet
-supported, but support for them is planned starting in Spark 4.3. See
+Real-time Mode in Apache Spark supports **stateless queries** -- projections,
filters and other
+map-like operations, unions, and stream-static joins -- and, starting in Spark
4.3.0, a first set of
+**stateful queries**: streaming **deduplication** (`dropDuplicates`) and
streaming **aggregations**
+(`groupBy(...).agg(...)`). These
+stateful operations require a shuffle, which Real-time Mode runs as a
*pipelined shuffle* so that
+records still stream through without waiting for a batch boundary; see
+[How Stateful Queries Work](#how-stateful-queries-work). Other stateful
operations -- stream-stream
+joins, `flatMapGroupsWithState`, and `transformWithState` -- are not yet
supported. See
[Supported Queries](#supported-queries) for the full list.
The most important thing to know: **the duration you pass to the trigger
(default 5 minutes) is a
@@ -62,6 +66,47 @@ the time needed to process and ship one record (often a few
milliseconds).
Since records never wait for a batch boundary, the batch duration mainly
controls how often the
query checkpoints progress -- as the next section explains.
+## How Stateful Queries Work
+
+Stateless operations are per-record: each long-running task reads a partition,
transforms records,
+and ships them without ever needing data from another partition. Stateful
operations are different.
+A streaming aggregation or `dropDuplicates` groups records **by key**, so
every record for a given
+key must reach the same task, no matter which partition it arrived on. In the
micro-batch engine
+that regrouping is done by a **shuffle**: the batch's producer stage writes
shuffle files, and only
+once those files are fully materialized does the consumer stage read them
back, grouped by key.
+
+That "materialize, then read" boundary is exactly what Real-time Mode avoids
for latency, and a
+long-running Real-time task never finishes its batch, so an ordinary shuffle
would deadlock -- the
+consumer would wait forever for a producer that never completes. Real-time
Mode therefore runs the
+shuffle differently, using two cooperating pieces:
+
+- **Pipelined shuffle** changes *scheduling*. Normally the scheduler runs the
consumer stage only
+ after the producer stage has completed. For a Real-time stateful query the
producer (the source
+ scan) and the consumer (the stateful operator) are marked as a single
**pipelined group** and
+ scheduled to run **at the same time**, for the whole batch. Neither stage
waits for the other to
+ finish; they run concurrently as long-running tasks, just like the stateless
case.
+
+- **Streaming shuffle** changes *data transport*. Because the two stages run
concurrently, shuffle
+ data cannot be written to files and read back after the fact. Instead the
producer's output is
+ streamed directly to the consumer tasks over the network, record by record,
so a record is
+ regrouped by key and handed to the stateful operator as soon as it is
produced -- there is no
+ intermediate file and no wait for the batch to end.
+
+Together these let a stateful Real-time query keep the same continuous,
per-record flow as a
+stateless one: a record is read, routed to the task that owns its key through
the streaming shuffle,
+merged into state, and emitted -- all without a batch boundary. The regrouped
state itself is kept
+in Spark's usual state store and checkpointed each batch, so the exactly-once
and recovery
+guarantees are the same as the micro-batch engine (see [Fault
Tolerance](#fault-tolerance)).
+
+This mechanism is enabled automatically in Real-time Mode; there is nothing to
configure. It applies
+to every shuffle on the streaming path -- the shuffle a stateful operator
needs, and also a bare
+`repartition` (a hash or round-robin shuffle with no stateful operator), which
runs as a pipelined
+shuffle in the same way. The one exception is a shuffle that would require a
separate preparatory
+job: range partitioning (`repartitionByRange`, or an `ORDER BY` that plans to
a range shuffle) needs
+a sampling job to compute range bounds, and that job cannot complete while the
source keeps
+producing, so such a query fails to start with
+`STREAMING_REAL_TIME_MODE.OPERATOR_OR_SINK_NOT_IN_ALLOWLIST`.
+
## Batch Duration Is a Checkpoint Interval
In Real-time Mode, the batch duration is a **checkpoint interval, not a
latency interval.** With the
@@ -100,8 +145,8 @@ experimental [Continuous
Processing](./performance-tips.html#continuous-processi
| Mode | Latency | Processing Guarantees | Supported operations | When to use |
|---|---|---|---|---|
-| Micro-batch (default) | ~100 ms | Exactly-once | All streaming operations,
including stateful | Stateful or higher-throughput workloads, or queries
Real-time Mode does not yet support |
-| Real-time Mode | millisecond-scale | Exactly-once | Stateless today
(map-like operations, unions, and stream-static joins); designed to support all
query shapes, including stateful | Low-latency workloads |
+| Micro-batch (default) | ~100 ms | Exactly-once | All streaming operations,
including all stateful ones | Stateful or higher-throughput workloads, or
queries Real-time Mode does not yet support |
+| Real-time Mode | millisecond-scale | Exactly-once | Stateless operations
(map-like operations, unions, and stream-static joins) plus stateful
deduplication and aggregation; more stateful operations planned | Low-latency
workloads |
| Continuous Processing (experimental) | ~1 ms | At-least-once | Map-like only
(projections and selections); no stateful operations | Legacy; use Real-time
Mode instead |
The **Processing Guarantees** column refers to processing semantics, defined
under
@@ -118,8 +163,9 @@ substantially:
apply to it. These constraints have limited its adoption.
- **Real-time Mode** is designed to support all query shapes, including
stateful operations, while
reusing Spark's mature components such as state management, the Catalyst
optimizer, and the
- existing SQL operators. It provides exactly-once processing semantics. It
currently supports
- stateless queries, with stateful support planned starting in Spark 4.3.
+ existing SQL operators. It provides exactly-once processing semantics. It
supports stateless
+ queries and, starting in Spark 4.3.0, stateful deduplication and
aggregation; support for the
+ remaining stateful operations is ongoing.
For new low-latency workloads, prefer Real-time Mode over Continuous
Processing.
@@ -228,11 +274,12 @@ the query starts:
## Supported Queries
-Real-time Mode supports stateless, map-like queries only.
+Real-time Mode supports stateless, map-like queries and a first set of
stateful queries
+(deduplication and aggregation).
-The following operations, sources, and sinks are supported as of Spark 4.1.0:
+The following operations, sources, and sinks are supported:
-- *Operations*: stateless, map-like operations are supported:
+- *Stateless operations* (supported since Spark 4.1.0):
+ Projections: `select`, `selectExpr`, `withColumn`, `drop`, and the typed
`map` / `flatMap` Dataset operations.
+ Selections: `where` / `filter`.
+ Expressions that compile to a projection -- including functions such as
`from_json` / `to_json`
@@ -243,11 +290,22 @@ The following operations, sources, and sinks are
supported as of Spark 4.1.0:
`STREAMING_REAL_TIME_MODE.IDENTICAL_SOURCES_IN_UNION_NOT_SUPPORTED`;
create a separate DataFrame
for each source instead.
+ Stream-static joins, where a streaming DataFrame is joined with a static
DataFrame. The static
- side must be broadcast (use the `broadcast(...)` hint), because Real-time
Mode does not support
- shuffles.
- + `withWatermark` (event-time watermark declaration) is allowed, although it
has no effect because
- stateful operators are not supported. This lets queries that already
declare a watermark run in
- Real-time Mode without modification.
+ side must be broadcast (use the `broadcast(...)` hint), because a
stream-static join must not
+ introduce a shuffle.
+
+- *Stateful operations* (supported since Spark 4.3.0): these regroup records
by key through a
+ pipelined shuffle (see [How Stateful Queries
Work](#how-stateful-queries-work)) and keep their
+ state in the state store, checkpointed each batch.
+ + **Deduplication**: `dropDuplicates`. (`dropDuplicatesWithinWatermark` is
not yet supported in
+ Real-time Mode.)
+ + **Streaming aggregation**: `groupBy(...).agg(...)` (and the SQL `GROUP BY`
equivalent), including
+ windowed aggregations with `window(...)`. Distinct aggregates such as
`count(distinct ...)` are
+ not supported (see [Not supported](#not-supported)).
+ + `withWatermark` (event-time watermark declaration) is supported and now
takes effect: it lets a
+ windowed aggregation drop late input and evict the state for windows that
have closed, bounding
+ how much state a long-running query accumulates. (Real-time Mode always
runs in `update` output
+ mode -- see [Requirements](#requirements) -- so a windowed aggregation
emits each window's
+ running result as it changes, and the watermark governs when that window's
state is evicted.)
- *Sources*: the source must support Real-time Mode. In Apache Spark, the
**Kafka** source supports
Real-time Mode. An unsupported source fails with
@@ -270,11 +328,23 @@ starts; anything outside the allowlist fails with
### Not supported
-Stateful operations of any kind are not supported in this release. This
includes streaming
-aggregations, `dropDuplicates` / `dropDuplicatesWithinWatermark`,
stream-stream joins, `repartition`
-and other operations that introduce a shuffle, and stateful operators such as
-`flatMapGroupsWithState` and `transformWithState`. Support for stateful
operations is planned
-starting in Spark 4.3.
+The following are not yet supported in Real-time Mode. Unless noted otherwise,
a query that uses one
+fails to start with
`STREAMING_REAL_TIME_MODE.OPERATOR_OR_SINK_NOT_IN_ALLOWLIST`:
+
+- Stateful operations other than deduplication and aggregation:
**stream-stream joins**,
+ `flatMapGroupsWithState`, `transformWithState`, session-window aggregation,
and
+ `dropDuplicatesWithinWatermark`.
+- **Range partitioning**: `repartitionByRange`, or an `ORDER BY` / sort that
plans to a range
+ shuffle, because computing range bounds needs a separate sampling job that
cannot complete while
+ the source keeps producing. (A plain `repartition` -- hash or round-robin --
is supported; it runs
+ as a pipelined shuffle. See [How Stateful Queries
Work](#how-stateful-queries-work).)
+
+**Distinct aggregates** such as `count(distinct ...)` are not supported
either, but this is a
+general Structured Streaming restriction rather than a Real-time Mode one: any
streaming distinct
+aggregate is rejected during analysis (with a message suggesting
`approx_count_distinct()`),
+regardless of the trigger.
+
+Support for more stateful operations is ongoing.
## Fault Tolerance
@@ -391,6 +461,175 @@ spark
</div>
+### Deduplication
+
+Drop duplicate records by key. This is a stateful operation: Real-time Mode
regroups records by the
+deduplication key through a pipelined shuffle and keeps the set of seen keys
in the state store (see
+[How Stateful Queries Work](#how-stateful-queries-work)). No code changes are
needed beyond running
+under a Real-time trigger.
+
+<div class="codetabs">
+
+<div data-lang="python" markdown="1">
+{% highlight python %}
+spark \
+ .readStream \
+ .format("kafka") \
+ .option("kafka.bootstrap.servers", "host1:port1,host2:port2") \
+ .option("subscribe", "input-topic") \
+ .load() \
+ .selectExpr("CAST(key AS STRING) AS id", "CAST(value AS STRING) AS value") \
+ .dropDuplicates("id") \
+ .writeStream \
+ .format("kafka") \
+ .option("kafka.bootstrap.servers", "host1:port1,host2:port2") \
+ .option("topic", "output-topic") \
+ .option("checkpointLocation", "/path/to/checkpoint") \
+ .outputMode("update") \
+ .trigger(realTime="5 minutes") \
+ .start()
+{% endhighlight %}
+</div>
+
+<div data-lang="scala" markdown="1">
+{% highlight scala %}
+import org.apache.spark.sql.streaming.Trigger
+
+spark
+ .readStream
+ .format("kafka")
+ .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
+ .option("subscribe", "input-topic")
+ .load()
+ .selectExpr("CAST(key AS STRING) AS id", "CAST(value AS STRING) AS value")
+ .dropDuplicates("id")
+ .writeStream
+ .format("kafka")
+ .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
+ .option("topic", "output-topic")
+ .option("checkpointLocation", "/path/to/checkpoint")
+ .outputMode("update")
+ .trigger(Trigger.RealTime("5 minutes"))
+ .start()
+{% endhighlight %}
+</div>
+
+<div data-lang="java" markdown="1">
+{% highlight java %}
+import org.apache.spark.sql.streaming.Trigger;
+
+spark
+ .readStream()
+ .format("kafka")
+ .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
+ .option("subscribe", "input-topic")
+ .load()
+ .selectExpr("CAST(key AS STRING) AS id", "CAST(value AS STRING) AS value")
+ .dropDuplicates("id")
+ .writeStream()
+ .format("kafka")
+ .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
+ .option("topic", "output-topic")
+ .option("checkpointLocation", "/path/to/checkpoint")
+ .outputMode("update")
+ .trigger(Trigger.RealTime("5 minutes"))
+ .start();
+{% endhighlight %}
+</div>
+
+</div>
+
+This keeps every distinct key it has seen in the state store.
`dropDuplicatesWithinWatermark`, which
+bounds how long keys are retained, is not yet supported in Real-time Mode.
+
+### Streaming aggregation
+
+Maintain a running aggregate per key. Real-time Mode regroups input by the
grouping key through a
+pipelined shuffle, merges each record into the running aggregate in the state
store, and emits the
+updated result. Because the output mode is `update`, each key is emitted as it
changes rather than
+only at the end of the batch.
+
+<div class="codetabs">
+
+<div data-lang="python" markdown="1">
+{% highlight python %}
+from pyspark.sql.functions import count
+
+spark \
+ .readStream \
+ .format("kafka") \
+ .option("kafka.bootstrap.servers", "host1:port1,host2:port2") \
+ .option("subscribe", "input-topic") \
+ .load() \
+ .selectExpr("CAST(key AS STRING) AS id") \
+ .groupBy("id") \
+ .agg(count("*").alias("cnt")) \
+ .selectExpr("CAST(id AS STRING) AS key", "CAST(cnt AS STRING) AS value") \
+ .writeStream \
+ .format("kafka") \
+ .option("kafka.bootstrap.servers", "host1:port1,host2:port2") \
+ .option("topic", "output-topic") \
+ .option("checkpointLocation", "/path/to/checkpoint") \
+ .outputMode("update") \
+ .trigger(realTime="5 minutes") \
+ .start()
+{% endhighlight %}
+</div>
+
+<div data-lang="scala" markdown="1">
+{% highlight scala %}
+import org.apache.spark.sql.functions.count
+import org.apache.spark.sql.streaming.Trigger
+
+spark
+ .readStream
+ .format("kafka")
+ .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
+ .option("subscribe", "input-topic")
+ .load()
+ .selectExpr("CAST(key AS STRING) AS id")
+ .groupBy("id")
+ .agg(count("*").as("cnt"))
+ .selectExpr("CAST(id AS STRING) AS key", "CAST(cnt AS STRING) AS value")
+ .writeStream
+ .format("kafka")
+ .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
+ .option("topic", "output-topic")
+ .option("checkpointLocation", "/path/to/checkpoint")
+ .outputMode("update")
+ .trigger(Trigger.RealTime("5 minutes"))
+ .start()
+{% endhighlight %}
+</div>
+
+<div data-lang="java" markdown="1">
+{% highlight java %}
+import static org.apache.spark.sql.functions.count;
+import org.apache.spark.sql.streaming.Trigger;
+
+spark
+ .readStream()
+ .format("kafka")
+ .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
+ .option("subscribe", "input-topic")
+ .load()
+ .selectExpr("CAST(key AS STRING) AS id")
+ .groupBy("id")
+ .agg(count("*").as("cnt"))
+ .selectExpr("CAST(id AS STRING) AS key", "CAST(cnt AS STRING) AS value")
+ .writeStream()
+ .format("kafka")
+ .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
+ .option("topic", "output-topic")
+ .option("checkpointLocation", "/path/to/checkpoint")
+ .outputMode("update")
+ .trigger(Trigger.RealTime("5 minutes"))
+ .start();
+{% endhighlight %}
+</div>
+
+</div>
+
### Writing to the console for development
The console sink prints output to the driver's standard output and is handy
while developing a
@@ -471,6 +710,44 @@ spark
|---|---|---|
| `spark.sql.streaming.realTimeMode.minBatchDuration` | `5000` (ms, 5 seconds)
| The minimum batch duration, in milliseconds, allowed for a Real-time trigger.
See the batch-duration requirement under [Requirements](#requirements). |
| `spark.sql.streaming.realTimeMode.allowlistCheck` | `true` | Whether to
verify that all operators and sinks used by a Real-time query are in the
supported allowlist. Disabling this check (not recommended) lets unsupported
operators and sinks run at your own risk. |
+| `spark.sql.streaming.realTimeMode.dangerouslyAllowCheckpointV1.enabled` |
`false` | Whether to allow a stateful Real-time query to start on a checkpoint
written with the version 1 commit log format (for example, one created by a
micro-batch query, or with
`spark.sql.streaming.stateStore.checkpointFormatVersion=1`). This is unsafe --
format v1 does not persist the per-batch state store checkpoint ids Real-time
Mode relies on for correct recovery, so a failure could lose state. Prefer a
[...]
+
+### State store defaults
+
+A stateful Real-time query needs a low-latency, recovery-correct state store
configuration. Because
+that configuration is not the right default for the engine as a whole,
Real-time Mode applies it
+automatically at query start, only for Real-time queries. These are **soft
defaults**: each is set
+only when you have not set the config yourself, so an explicit value is
preserved -- with the
+exception of a few explicit values that are incompatible with Real-time Mode
and are rejected at
+query start rather than kept (see [Incompatible
configurations](#incompatible-configurations)).
+
+| Configuration | Real-time default | Meaning |
+|---|---|---|
+| `spark.sql.streaming.stateStore.providerClass` | `RocksDBStateStoreProvider`
| Real-time Mode defaults to the RocksDB state store, which checkpoint format
v2 (below) requires. |
+| `spark.sql.streaming.stateStore.checkpointFormatVersion` | `2` | Format v2
gives each batch its own state store checkpoint ids, which is what lets a
failed batch be rerun correctly from committed offsets. Real-time Mode requires
v2 for stateful queries (see below). |
+| `spark.sql.streaming.stateStore.rocksdb.changelogCheckpointing.enabled` |
`true` | Writes a changelog instead of a full snapshot at each commit,
shortening the state-commit step that sits on the critical path between
Real-time batches. Applied only when the state store is RocksDB. |
+| `spark.sql.execution.sortBeforeRepartition` | `false` | The local sort
inserted before a round-robin repartition never drains an unbounded stream and
would hang a Real-time query, so Real-time Mode defaults it off. Determinism
from the sort is not needed because Real-time Mode does not retry tasks. Like
the others this is a soft default -- but an explicit `true` is incompatible, so
rather than being kept it is rejected at query start (see [Incompatible
configurations](#incompatible-con [...]
+
+A stateful Real-time query requires a checkpoint written with commit log
format v2 (the state store
+format above). Starting a stateful Real-time query on a version 1 checkpoint
-- for example, when
+switching an existing micro-batch query to Real-time Mode, or when
+`spark.sql.streaming.stateStore.checkpointFormatVersion` is pinned to `1` --
fails to start with
+`STREAMING_REAL_TIME_MODE.CHECKPOINT_FORMAT_V1_NOT_SUPPORTED`. Use a fresh
checkpoint location, or,
+accepting the risk of state loss on failure, set
+`spark.sql.streaming.realTimeMode.dangerouslyAllowCheckpointV1.enabled=true`.
+
+### Incompatible configurations
+
+The defaults above are applied only when you have not set the config. If you
instead set one of the
+following to a value that Real-time Mode cannot run with, the query fails to
start with
+`STREAMING_REAL_TIME_MODE.SQL_CONFIGURATION_NOT_SUPPORTED` rather than having
your value silently
+overridden:
+
+- `spark.sql.streaming.stateStore.checkpointFormatVersion` set below `2`
(unless
+
`spark.sql.streaming.realTimeMode.dangerouslyAllowCheckpointV1.enabled=true`).
+- `spark.sql.streaming.stateStore.providerClass` set to a provider other than
+ `RocksDBStateStoreProvider`.
+- `spark.sql.execution.sortBeforeRepartition` set to `true`.
## Best Practices
@@ -481,6 +758,13 @@ spark
topic with 10 partitions requires at least 10 cores for the query to make
progress. Real-time Mode
uses a fixed 1:1 mapping between Kafka topic partitions and reader tasks;
the `minPartitions`
option is not supported in Real-time Mode.
+- Stateful queries need cores for **both** stages at once. A stateful
Real-time query runs its
+ producer stage (the source scan) and its consumer stage (the stateful
operator) concurrently as a
+ pipelined group (see [How Stateful Queries
Work](#how-stateful-queries-work)), and both hold their
+ tasks for the whole batch. Size the cluster for the sum: the source's reader
tasks plus the
+ stateful operator's tasks (`spark.sql.shuffle.partitions` post-shuffle tasks
by default). For
+ example, a 10-partition Kafka source feeding an aggregation with 5 shuffle
partitions needs at
+ least 15 cores to make progress.
- Run a single Real-time query per cluster. Because Real-time Mode holds its
task slots for the
entire batch duration, any other queries sharing the cluster compete for the
same slots, which can
starve the Real-time query of resources and increase its latency.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]