Skip to content

[SQL] State-store instance metrics are shipped in every task result #58394

Description

@Kimahriman

Summary

Stateful operators currently create one state-store instance metric accumulator for every combination of:

state partition
x supported provider metric
x state-store name

Every task then receives and registers all of those accumulators, even though it processes only one state partition. On task completion, all external accumulators are included in the task result.

For a job with 20,000 shuffle partitions, RocksDB state, and one default store, this can result in approximately 20,000 state-instance metric updates per task.

Reproduction

A representative workload is:

spark.sql.shuffle.partitions = 20000
stateful operator using RocksDB state
checkpoint or write the stateful result

Observed behavior:

per-task result size: approximately 3 MiB
number of tasks: approximately 20,000
terminal stage: ResultStage

The cumulative task-result data can therefore be approximately 60 GiB. The job may fail with spark.driver.maxResultSize even though the actual rows are written or checkpointed by executors.

The same issue can occur with direct file writes because the final write stage is submitted through SparkContext.runJob and uses ResultTasks. Each task returns a commit result plus accumulator updates.

Root cause

Relevant code paths on master include:

  • StateStoreWriter.stateStoreInstanceMetricsWithIds creates metrics for all state partitions, supported metrics, and store names.
  • StateStoreWriter.setStoreInstanceMetrics updates the corresponding per-instance SQLMetric.
  • AccumulatorV2.readObject registers all deserialized accumulators with the task.
  • Task.collectAccumulatorUpdates sends all external accumulators on successful task completion.
  • Executor serializes those accumulator updates into DirectTaskResult.
  • TaskSetManager.canFetchMoreResults applies spark.driver.maxResultSize to ResultStages.

ignoreIfUnchanged and numStateStoreInstanceMetricsToReport are applied while generating state-operator progress after the driver has received and merged the updates. They do not reduce the task-result payload.

Why checkpointing and direct writes are affected

RDD checkpoint materialization calls SparkContext.runJob, making the checkpointing stage a ResultStage.

Direct file writes also use runJob and return per-task write results or commit messages to the driver. Those task results contain the write result plus accumulator updates, so state-store metrics can contribute to the cumulative result size.

The spark.driver.maxResultSize check is not enforced for upstream ShuffleMapStages, but it is enforced for these terminal ResultStages.

Impact

This behavior causes:

  • inflated task-result serialization and network traffic;
  • increased executor and driver deserialization work;
  • large CompletionEvent payloads on the driver;
  • driver heap pressure if result processing falls behind task completion;
  • possible spark.driver.maxResultSize failures;
  • increased per-task metric metadata retained by scheduler or UI structures.

Scope

This is not specific to TransformWithState. It can affect any stateful operator using StateStoreWriter and a provider that exposes state-store instance metrics.

Multiple state stores, such as those used by streaming joins, multiply the number of metrics included in each task result.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions