[Spark][#36841] Add the DataSourceV2 unbounded source for the Spark 4 streaming runner - #39971
[Spark][#36841] Add the DataSourceV2 unbounded source for the Spark 4 streaming runner#39971tkaymak wants to merge 2 commits into
Conversation
Exposes any Beam UnboundedSource as a Spark 4 DataSourceV2 streaming table with a fixed two column schema, encoded payload plus event timestamp. Offsets are opaque, strictly increasing epoch counters, so Spark keeps scheduling micro-batches and termination stays with the lifecycle owner. Recovery is durable under the query's checkpoint location: the source id derives deterministically from the read transform's full name, the first run pins its split list (Beam sources do not guarantee deterministic splitting), and every split persists its CheckpointMark per epoch with a retention of two, written atomically via temp file and rename. Executors cache live readers between micro-batches and fall back to the newest durable mark at or before the replayed epoch after a restart. Semantics are at least once, a crash between finishing a read and Spark's commit replays the last micro-batch. The batch cutoff honors maxRecordsPerBatch, values below 1, including the default, mean no limit and the batch ends on the duration deadline.
|
Assigning reviewers: R: @tvalentyn 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). |
|
Thank you for the review @Abacn! |
Marks are no longer finalized when a partition reader closes. A reader finalizes the mark taken at its start offset when the next micro-batch for that split is scheduled, because Spark only starts a batch at the initial offset or at the end offset of a batch already in its commit log. A reader whose position does not match the start offset, or that moved without completing its batch (task retry, killed attempt, executor change, restart), is dropped without finalizing and recreated from the durable mark at that offset. Per source state lives under the checkpoint location Spark hands to toMicroBatchStream, written through CheckpointFileManager with the session Hadoop configuration broadcast to executors. Marks are coded with the source's checkpoint mark coder. commit(end) purges marks below end on a background thread, nothing is retained by a fixed count. The dataset is built like BoundedDatasetFactory, a Table holding the real objects wrapped in StreamingRelationV2, no string options, no Base64. Splits travel as objects in the InputPartition, options and Hadoop configuration as broadcasts. maxRecordsPerBatch is divided across splits like the legacy MicrobatchSource, defaultParallelism decides the split count, idle readers back off with FluentBackoff, offsets serialize as the bare epoch like LongOffset with the base class equality. A new option readerIdleTimeoutMillis bounds how long an executor keeps an idle reader. Tests drive the reader cache protocol directly and prove restart recovery, finalization only after commit, and mark purging against Spark's real offsets and commits logs. The JUnit per test timeout is removed from the streaming test, its throwaway thread group poisoned Spark's static pools for later batch tests in the same JVM.
|
@Abacn thanks for the thorough review, the failing tests seem to be unrelated (the setup environment step fails, because GitHub's org policy rejects the pinned SHA), will have a look at that later if I can.
|
Third slice of the Spark 4 Structured Streaming work split out of #39576, following the dispatch seam (#39906) and the Kryo registrations (#39939). Addresses #36841.
This adds the DataSourceV2 micro-batch source that exposes any Beam UnboundedSource as a Spark 4 streaming table. The only change outside the new package is one option on
SparkStructuredStreamingPipelineOptions.Design notes:
BoundedDatasetFactory, aTableholding the source, coder and broadcasts wrapped inStreamingRelationV2. No string options, splits travel as objects inside theInputPartition, pipeline options and the session Hadoop configuration as broadcasts.LongOffset.latestOffsetalways advances so Spark keeps scheduling micro-batches, termination belongs to the lifecycle owner.toMicroBatchStream, written throughCheckpointFileManager. The first run pins its split list because Beam sources do not guarantee deterministic splitting. Each split writes its CheckpointMark, coded withgetCheckpointMarkCoder(), at the end of every micro-batch under its end epoch.commit(end)purges marks belowend.PubsubCheckpointthrows on a restored checkpoint andKafkaCheckpointMarkis a no-op without its reader, and DSv2 has no executor side commit callback.spark.speculationis not supported for sources with non deterministic reads.maxRecordsPerBatchis a per batch total divided across splits like the legacyMicrobatchSource, values below 1 mean no limit and the batch ends on themaxBatchDurationMillisdeadline.defaultParallelismdecides the desired split count. Idle readers back off withFluentBackoff. The new optionreaderIdleTimeoutMillisbounds how long an executor keeps an idle reader, its last mark is not finalized when it is closed.Tests cover element delivery, watermark tracking through typed maps, the offset round trip, the quota division, the reader cache protocol driven directly (continuation, retry, executor change, missing mark, never started reader, failed mark write), the checkpoint layout, and, against Spark's real offsets and commits logs, restart recovery with at most one replayed batch, finalization only after commit, and mark purging.
Remaining slices: the state and timer bridge on transformWithState, then the translators with the end to end tests. End to end evidence remains in draft #39576.
R: @Abacn