MartijnVisser opened a new pull request, #318:
URL: https://github.com/apache/flink-connector-kafka/pull/318

   ## What is the purpose of the change
   
   A bounded `KafkaSource` never finishes once the job has been restored from a 
savepoint or a retained
   checkpoint, or after a JobManager failover. All three recreate the 
enumerator from state.
   
   The recreated enumerator runs its one-time discovery, finds every subscribed 
partition already in
   `assignedSplits`, and `checkPartitionChanges` returns on the empty 
`PartitionChange` before it
   reaches `handlePartitionSplitChanges`, which is the only place that sets 
`noMoreNewPartitionSplits`.
   The restored readers consume up to their stopping offset and then wait 
forever for a
   `NoMoreSplitsEvent`, which the runtime never re-sends on its own. The same 
early return hangs a
   bounded source that subscribes to no existing partition on a fresh start.
   
   The early return came in with `0b38d1a9` (FLINK-22147). Before that 
refactor, discovery and split
   initialisation shared one callable whose callback always ran, so a restored 
bounded source did
   signal correctly. The Pulsar enumerator does not have this bug, because its 
split assigner marks the
   discovery finished on every discovery rather than only on a change.
   
   ## Brief change log
   
   - `KafkaSourceEnumerator.checkPartitionChanges` lets an empty partition 
change through when a
     bounded source has run its only discovery, so that it reaches 
`handlePartitionSplitChanges` and
     signals the registered readers.
   - Two test-only `[hotfix]` commits first, each independently green: the 
`createEnumerator` helper in
     `KafkaSourceEnumeratorTest` dropped its `initialDiscoveryFinished` 
argument, and the context proxy
     in `DynamicKafkaSourceEnumeratorTest` passed `null` where production passes
     `DynamicKafkaSourceEnumerator::handleNoMoreSplits`, so no test could 
observe that wiring.
   - New `KafkaSourceBoundedRestoreITCase`, plus enumerator coverage for the 
static and the dynamic
     source.
   
   Periodic discovery and unbounded sources keep returning early. Neither can 
act on an empty change,
   and treating one as a finished discovery would make the partitions that a 
later discovery finds
   resolve against the earliest offset rather than the configured one.
   
   Signalling from `addReader` on the strength of the restored 
`initialDiscoveryFinished` flag would
   need no new state, but it finishes a reader before the post-restore 
discovery assigns it a partition
   created while the job was down. That is what apache/flink#21909 was rejected 
for, and
   `testRestoredBoundedEnumeratorAssignsBeforeItSignals` pins the ordering that 
avoids it. The
   reasoning is written up on the Jira, answering the question left open on 
FLINK-33466 in 2023.
   
   ## Verifying this change
   
   Flink 2.2.1, JDK 17. Each of the following fails without the production 
change and passes with it:
   
   - 
`KafkaSourceEnumeratorTest.testRestoredBoundedEnumeratorSignalsNoMoreSplitsWithoutPartitionChanges`
   - 
`KafkaSourceEnumeratorTest.testBoundedSourceWithoutPartitionsSignalsNoMoreSplits`
   - 
`DynamicKafkaSourceEnumeratorTest.testBoundedRestoreSignalsNoMoreSplitsWithoutPartitionChanges`
   - 
`KafkaSourceBoundedRestoreITCase.testBoundedSourceFinishesAfterRestoreFromSavepoint`,
 which times
     out after two minutes on the base and passes in about six seconds with the 
fix
   
   Two further unit tests pass on both sides by design, because they pin 
decisions rather than reproduce
   the bug: `testUnboundedSourceKeepsSkippingAnEmptyPartitionChange` fails if 
the condition is ever
   widened past bounded sources, and 
`testRestoredBoundedEnumeratorAssignsBeforeItSignals` fails under
   the `addReader` variant described above.
   
   `mvn verify` on the module with no skips: 443 unit tests and 326 integration 
tests, no failures,
   spotless, checkstyle, enforcer and japicmp clean, plus `apache-rat:check` at 
the root. The new
   ITCase was run 20 consecutive times, all passing. No existing test restored 
a bounded `KafkaSource`,
   which is the coverage asked for on the earlier attempt in #71 and never 
added.
   
   ## Does this pull request potentially affect one of the following parts
   
   - Dependencies (does it add or upgrade a dependency): **no**
   - The public API, i.e., is any changed class annotated with 
`@Public(Evolving)`: **no**
   - The serializers: **no**
   - The runtime per-record code paths (performance sensitive): **no**
   - Anything that affects deployment or recovery: **yes**. It changes when 
`NoMoreSplitsEvent` is sent
     to the readers of a bounded source after the enumerator is recreated from 
state. No state format
     change; `noMoreNewPartitionSplits` stays derived rather than checkpointed.
   - The S3 file system connector: **no**
   
   ## Documentation
   
   - Does this pull request introduce a new feature? **no**
   - If yes, how is the feature documented? **not applicable**. The documented 
behaviour, that a bounded
     source exits once all partitions reach their stopping offsets, is what 
this restores.
   
   ## AI disclosure
   
   - [x] This pull request was written with the help of an AI tool.
   
   Generated-by: Claude Code 2.1.267 (Claude Opus 5). The diagnosis, the fix 
and the tests were
   reviewed and verified locally by the submitter.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to