Conversation
…thMockDatanodes#testContainerReconciliationFailureContainerScan The container replicas and the OnDemandContainerScanner are created once in @BeforeAll and shared by every test method. MockDatanode.scanContainer asserted that OnDemandContainerScanner.scanContainerWithoutGap(...) returns a present Future, but that method returns an empty Optional when the container is already registered as in progress in containerRescheduleCheckSet. KeyValueHandler.reconcileContainer schedules a fire-and-forget scan via scanContainerWithoutGap in a finally block, so every reconciliation test leaves one scan in flight. Those tests wait via waitForExpectedScanCount(...), which polls the numContainersScanned metric that is incremented inside the scan, before the executor thread runs removeContainerFromScheduledContainers. When the next test's synchronous scanContainer ran before that removal, no scan was scheduled, the Optional was empty, and the assertion failed intermittently. Make scanContainer wait until scanContainerWithoutGap returns a scheduled Future before blocking on it. The single-thread scan executor is guaranteed to drain the prior scan, so this is deterministic by construction and keeps the per-call scan count at exactly one. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
No unresolved review comments; the test helper robustly handles in-flight scans while preserving existing assertions.
Pull request overview
Fixes an intermittent race in mock datanode reconciliation tests caused by in-flight asynchronous scans.
Changes:
- Retry scan scheduling until a
Futureis returned. - Preserve synchronous completion and scan-count assertions.
File summaries
| File | Summary |
|---|---|
hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/keyvalue/TestContainerReconciliationWithMockDatanodes.java |
Makes mock datanode scans resilient to in-progress scanner registration. |
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
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.
Generated-by: Claude Code (Opus 4.8)
What changes were proposed in this pull request?
TestContainerReconciliationWithMockDatanodesfails intermittently intestContainerReconciliationFailureContainerScan:The container replicas and the
OnDemandContainerScannerare created once in@BeforeAlland shared by every test method. TheMockDatanode.scanContainerhelper asserts thatOnDemandContainerScanner.scanContainerWithoutGap(...)returns a presentFuture. That method returns an emptyOptionalwhen the container is already registered as in progress incontainerRescheduleCheckSet; the entry is only removed by the scan executor task after the scan finishes.KeyValueHandler.reconcileContainerschedules a fire-and-forget scan viascanContainerWithoutGapin afinallyblock, so every reconciliation test leaves one scan in flight. Those tests wait viawaitForExpectedScanCount(...), which polls thenumContainersScannedmetric that is incremented inside the scan, before the executor thread runsremoveContainerFromScheduledContainers. When the next test's synchronousscanContainerruns before that removal, no scan is scheduled, theOptionalis empty, and the assertion fails. ThewithoutGaphelper usesminScanGap = 0, so the scan-gap path cannot cause the emptyOptional, and this test introduces no corruption, so the volume-failed path is not exercised either; the only cause is the still-registered prior scan.The fix makes
MockDatanode.scanContainerwait untilscanContainerWithoutGapreturns a scheduledFuturebefore blocking on it, instead of asserting the first call always schedules one. The single-thread scan executor is guaranteed to drain the prior scan, so this is deterministic by construction and keeps the per-call scan count at exactly one, preserving the existingwaitForExpectedScanCountassertions.What is the link to the Apache JIRA
https://issues.apache.org/jira/browse/HDDS-16423
How was this patch tested?
Ran the full test class (which runs the reconciliation tests that leave a scan in flight before the failing test) offline:
A single green run cannot prove the absence of a race, so the fix is deterministic by construction: the only transient reason
scanContainerWithoutGapreturns an emptyOptionalhere is a prior scan still registered incontainerRescheduleCheckSet, and the single-thread scan executor is guaranteed to drain it, after which the retry schedules exactly one scan.🤖 Generated with Claude Code