[Spark][#36841] Translate stateless streaming pipelines on the Spark 4 runner - #40090
Conversation
Makes the DataSourceV2 unbounded source from apache#39971 reachable. The Spark 4 module overrides PipelineTranslatorFactory and dispatches streaming pipelines to PipelineTranslatorStreaming, which translates unbounded reads and reuses the batch translators for stateless single output ParDo, Window.Assign, Flatten and Reshuffle. GroupByKey, Combine.perKey, stateful ParDo, ParDo with side inputs or additional outputs, Impulse and bounded reads fail at translation, the batch translators for them persist or collect the Dataset, which Spark rejects on a streaming plan. StreamingEvaluationContext runs one noop sink query per leaf, checkpoints under checkpointDir/<leaf index>, stops siblings when a query fails and stops a query after streamingStopAfterIdleBatches triggers without input. The test source of BeamMicroBatchSourceTest moves to TestUnboundedSource so the translator tests share it.
|
Assigning reviewers: R: @chamikaramj added as fallback since no labels match configuration Note: If you would like to opt out of this review, comment Available commands:
The PR bot will only process comments in the main thread (not review comments). |
|
Waiting for #40093 to be merged, then the Spark Precommit can run here |
|
Thanks. Close and reopen PR to triggger tests |
Abacn
left a comment
There was a problem hiding this comment.
Thanks, had a few comments.
…e session SparkStructuredStreamingPipelineResult.cancel() interrupted the execution thread and ran the terminal state callback at once, which stops the SparkSession. The thread kept translating or evaluating on a stopped SparkContext. When that happened during the static initialization of PipelineTranslatorBatch the class was poisoned for the JVM and every later batch pipeline failed with NoClassDefFoundError, seen in the Spark Versions PreCommit on apache#40090. runAsync now hands the result its single thread executor and cancel() waits for it to terminate, bounded at 60 seconds, before the callback runs.
Split the translator registry into PipelineTranslatorCommon with a thin PipelineTranslatorBatch subclass, the streaming translator extends the common class. Exceptions are created at the call site, the unchecked cast is scoped to one statement, the checkpoint location is joined with a Path, a failing evaluate stops every started query, and stop failures log the exception. Tests share one polling helper, collect results as one Set snapshot, and only the restart test keeps a source counter teardown.
|
Thanks you! One more commit is folded in, 75dbe36: the red Spark Versions run on this PR was |
This could be a real bug. Is this a test timing or an issue could affect production? add 60s timeout is just an ad-hoc mitigation and is non-deterministic If multiple tests / pipeline share same Spark session, cancel the session should only happen after all tests completed. We can scope out the long term fix as separate PR though. But should be a known blocker for the Spark 4 runner |
…pping the session" This reverts commit 75dbe36.
|
You are right on both points, it is a production bug and the 60 s wait was a mitigation. What is established, from the Spark 4.0.2 sources:
Suggested Fix (in a separate PR today): job group cancellation in |
Part of #36841, follows #39971 (DataSourceV2 unbounded source). This makes that source reachable: the Spark 4 runner now translates and runs stateless streaming pipelines.
Scope
Supported in streaming mode:
Read.from(UnboundedSource), stateless single outputParDowithout side inputs,Window.Assign,Flatten,Reshuffle. The last four reuse the batch translators unchanged. Everything else fails at translation with anUnsupportedOperationExceptionpointing at #36841:GroupByKey,Combine.perKey, statefulParDo,ParDowith side inputs or additional outputs,Impulseand bounded reads (soCreateandPAssert). The rejections are explicit because the batch translators for those primitives persist or collect the Dataset, which Spark refuses on a streaming plan with a rawAnalysisException.GroupByKeyand statefulParDoarrive with thetransformWithStatebridge in the next PR.Main code, four files under
runners/spark/4translation/PipelineTranslatorFactory.javashadows the shared base file that throws for streaming today. The Spark 4 module compiles the override tree with later wins, so only Spark 4 gets the streaming dispatch.translation/PipelineTranslatorStreaming.javaroutesPrimitiveUnboundedReadto the new translator, rejects the unsupported primitives, and falls back to the common registry for the rest. Without the rejectionsGroupByKeywould silently run the batch translator against a streaming Dataset.translation/StreamingEvaluationContext.javastarts onenoopsink query per leaf with a processing time trigger ofmaxBatchDurationMillis, checkpoints undercheckpointDir/<leaf index>, blocks until every query terminates, stops siblings when one fails, and stops a query afterstreamingStopAfterIdleBatchestriggers without input when that option is set. Idle triggers arrive as zero row progress events while the source offset moves and asQueryIdleEventotherwise, the listener counts both.checkpointDirmust be set, the shared default is/tmp/<jobName>.translation/streaming/ReadUnboundedTranslator.javabuilds the Dataset throughUnboundedSourceDataset.ofand decodes the payload column with the full windowed value coder.Shared code,
runners/spark/srcPipelineTranslatorBatchtoPipelineTranslatorCommon,PipelineTranslatorBatchis a thin subclass and the streaming translator extends the common class, so batch only translators added later do not reach the streaming path by inheritance.SparkStructuredStreamingPipelineResult.cancel()waits for the execution thread before the terminal state callback stops the SparkSession. Before, the thread kept translating on a stopped SparkContext, and when that hit the cold static init ofPipelineTranslatorBatchthe class was poisoned for the JVM, which is what the first Spark Versions run on this PR showed. Covered bySparkStructuredStreamingPipelineResultTest.Tests, all live
StreamingQueryrunsTestUnboundedSourceis the source thatBeamMicroBatchSourceTestused as a nested class in [Spark][#36841] Add the DataSourceV2 unbounded source for the Spark 4 streaming runner #39971, extracted so the translator tests share it. No second synthetic source.StatelessParDoStreamingTest: pass through, andFlattenof two unbounded reads.StreamingPipelineLifecycleTest: RUNNING to DONE on idle, cancel, a failing leaf fails the pipeline and stops its healthy sibling.StreamingCheckpointRestartTest: two runs against one checkpoint location with the reader cache wiped in between, the second run recreates readers from the durable marks and re-emits nothing the first run committed. The file layout itself is covered byBeamMicroBatchSourceTest, here only the wiring of the checkpoint location is asserted.PipelineTranslatorStreamingTest: the rejections surface fromrun()with the Beam message, not a Spark one.Delivery is at least once, as documented on
BeamReaderCachein #39971. NoCHANGES.mdentry yet, that comes when the runner can execute a windowedGroupByKey.R: @Abacn