Conversation
…e pipelines Behind --useStructuredStreaming, SparkPipelineRunner translates the fused Runner API pipeline into Spark Datasets. Impulse, Flatten, Reshuffle and GroupByKey are Dataset operations, and executable stages run through the existing Fn API bridge inside mapPartitions. Bounded pipelines run as batch Datasets. Unbounded input, user state and timers are rejected at translation. The branch clears the streaming option so the metrics accumulator and anything else reading it agree with how the job runs. The validatesPortableRunnerStructuredStreaming task runs the streaming PortableValidatesRunner suite on this backend and is the exit gate for making it the default for portable streaming pipelines.
Eliaaazzz
force-pushed
the
spark4-portable-dataset-poc
branch
from
September 15, 2026 12:50
d1b25b9 to
58719bd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proof of concept for the Dataset-based portable Spark 4 backend discussed in the "[DISCUSS] Spark 4 portable runner direction" thread on dev@. Opening this as a draft so the discussion has concrete code to work from. Addresses #36841.
What this adds
An opt-in
--useStructuredStreamingflag on the portable Spark runner. With the flag set,SparkPipelineRunnertranslates the fused Runner API pipeline into Spark Datasets through a newSparkDatasetPortablePipelineTranslator:Impulse,Flatten,ReshuffleandGroupByKeybecome Dataset operations.GroupByKeygroups on the encoded key and runsSparkGroupAlsoByWindowViaOutputBufferFnper key. That is the generalGroupAlsoByWindowthe RDD batch translator falls back to when a windowing strategy is not eligible for theGroupNonMergingWindowsFunctionsfast paths. The Dataset path always takes the general branch, so it gives up the non-merging memory optimization and keeps theReduceFnRunnersemantics.Dataset.mapPartitionsthrough the existing Fn API bridge,SparkExecutableStageFunction, which is unchanged. Outputs are demultiplexed withEncoderHelpers.oneOfEncoder, followingParDoTranslatorBatch. Side inputs are collected during translation and broadcast.The translation context attaches to the
SparkContextthatSparkContextFactoryalready created, so the job server code path, the metrics accumulator and result handling are unchanged. The translator lives in the sharedrunners/sparkbase and compiles for both the Spark 3 and Spark 4 modules.The flag name matches the terminology in the dev@ thread and is provisional. Today it only selects the Dataset translator. No Structured Streaming query is built yet.
The exit gate
Following Yi's suggestion on the thread,
validatesPortableRunnerStructuredStreamingruns the streamingPortableValidatesRunnersuite with the flag on, keeping exactly the same categories and test filters asvalidatesPortableRunnerStreaming, and aPostCommit Java PVR Spark4 StructuredStreamingworkflow runs it. That suite is the exit gate for flipping the default for portable streaming pipelines.What that gate currently measures is narrower than its name suggests.
--streamingdoes not by itself mark any PCollection unbounded, and the streaming exclusion list already removes the categories that would produce one.ValidatesRunnerextendsNeedsRunner, so tests annotated withNeedsRunnerandUsesUnboundedPCollectionsare outside the suite, and the remainingValidatesRunnertests carryingUsesUnboundedPCollectionsare each excluded byUsesTimersInParDo,UsesTestStream,UsesStrictTimerOrderingorUsesOnWindowExpiration. The first run therefore exercises bounded Dataset execution under the streaming exclusion list. The risk it actually probes is windowing, triggers and GroupByKey behaviour on this backend. This matches the point on the thread that the suite is mostly batch pipelines after the exclusions.The new task is deliberately not part of the aggregate
validatesPortableRunnertask while the backend is a preview.Out of scope, tracked separately
This does not modify the in-process Structured Streaming runner, including the work merged in #39906 and #39939 and the open #39576. It does reuse two of its classes,
EncoderHelpersandEvaluationContext, so the portable path also picks up the Spark 4EncoderHelpersoverride that came with the Spark 4 runner in #38255.Validation
SparkDatasetPortableExecutionTestruns Impulse, ParDo, Flatten, GroupByKey and PAssert end to end on the Dataset backend with the embedded SDK harness, and asserts that unbounded input and a stateful ParDo are rejected at translation with the expected message. Three tests, green on:runners:spark:4:testunder JDK 17 and on:runners:spark:3:testunder JDK 11.oneOfEncoderdemux is only ever built with one alternative;Reshuffle; theFlattenre-encode branch for mismatched coders; side inputs, which the streaming suite also excludes viaUsesSideInputs; and the timer half of the state and timer rejection.nullnessat class level, matching the surrounding translator code.Notes for review
Three things I would particularly like a second opinion on:
pipelineOptions.setStreaming(false)in the new branch.MetricsAccumulator.initreads the option directly, not the mode the runner resolved, so without this it takes its checkpoint branch and seeds the accumulator from a checkpoint file this path never writes back.SparkSessionis never cleared. That is fine whenPortableBatchMode.stop()stops the context, and it accumulates when the context is provided or reused.Thank you for your contribution! Follow this checklist to help us incorporate your contribution quickly and easily:
addresses #123), if applicable. This will automatically add a link to the pull request in the issue. If you would like the issue to automatically close on merging the pull request, commentfixes #<ISSUE NUMBER>instead.CHANGES.mdwith noteworthy changes. Holding this until the flag is more than a preview.See the Contributor Guide for more tips on how to make review process smoother.
To check the build health, please visit https://github.com/apache/beam/blob/master/.test-infra/BUILD_STATUS.md
GitHub Actions Tests Status (on master branch)
See CI.md for more information about GitHub Actions CI or the workflows README to see a list of phrases to trigger workflows.