Introduce the RetryPolicy abstraction#2004
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new internal retry abstraction (RetryPolicy + RetryControl) and refactors retryable read/write execution paths (sync + async) and bulk write implementations to use a unified spec-driven policy (SpecRetryPolicy). It also cleans up prior attachment-based state passing and aligns timeout message usage via a shared constant.
Changes:
- Replace
RetryState/LoopStatewithRetryControl/LoopControl, and introduceRetryPolicy+RetryContext. - Add
SpecRetryPolicyand refactor sync/async operation helpers and operations to use it for retry behavior and labeling. - Update/add tests, including a new prose-style functional test ensuring
bulkWriteis not retried when a follow-upgetMorefails.
Reviewed changes
Copilot reviewed 52 out of 52 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| driver-sync/src/test/functional/com/mongodb/client/CrudProseTest.java | Adds a new functional test around bulkWrite + getMore failure and updates method visibilities/assertions. |
| driver-sync/src/main/com/mongodb/client/internal/TimeoutHelper.java | Switches default timeout message to TimeoutContext.DEFAULT_TIMEOUT_MESSAGE. |
| driver-sync/src/main/com/mongodb/client/internal/ClientSessionImpl.java | Reuses the shared default timeout message constant for transaction timeout wrapping. |
| driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/TimeoutHelper.java | Switches default timeout message to TimeoutContext.DEFAULT_TIMEOUT_MESSAGE. |
| driver-core/src/test/unit/com/mongodb/internal/operation/SyncOperationHelperSpecification.groovy | Updates call sites for the new executeRetryableWrite signature. |
| driver-core/src/test/unit/com/mongodb/internal/operation/OperationHelperSpecification.groovy | Updates tests to renamed write retry requirement helpers. |
| driver-core/src/test/unit/com/mongodb/internal/operation/BulkWriteBatchSpecification.groovy | Updates tests to renamed bulk-write retry requirement accessor. |
| driver-core/src/test/unit/com/mongodb/internal/operation/AsyncOperationHelperSpecification.groovy | Updates call sites for the new executeRetryableWriteAsync signature. |
| driver-core/src/test/unit/com/mongodb/internal/async/function/RetryStateTest.java | Removes tests for the deleted RetryState. |
| driver-core/src/test/unit/com/mongodb/internal/async/function/RetryingSyncSupplierTest.java | Adds unit tests validating RetryControl.doWhileDisabled behavior in sync retry loops. |
| driver-core/src/test/unit/com/mongodb/internal/async/function/RetryingAsyncCallbackSupplierTest.java | Adds unit tests validating RetryControl.doWhileDisabledAsync behavior in async retry loops. |
| driver-core/src/test/unit/com/mongodb/internal/async/function/RetryControlTest.java | Adds unit tests for RetryControl advancement/break behavior and policy invocation. |
| driver-core/src/test/unit/com/mongodb/internal/async/function/LoopStateTest.java | Removes tests for the deleted LoopState. |
| driver-core/src/test/unit/com/mongodb/internal/async/function/LoopControlTest.java | Adds unit tests for the new LoopControl. |
| driver-core/src/test/functional/com/mongodb/internal/operation/MixedBulkWriteOperationSpecification.groovy | Updates functional expectations around retry behavior and server selection patterns. |
| driver-core/src/test/functional/com/mongodb/internal/operation/FindAndDeleteOperationSpecification.groovy | Expands coverage to test both async and sync execution paths. |
| driver-core/src/main/com/mongodb/internal/TimeoutContext.java | Introduces DEFAULT_TIMEOUT_MESSAGE and reuses it for timeout exception creation/throwing. |
| driver-core/src/main/com/mongodb/internal/operation/WriteConcernHelper.java | Updates to renamed addRetryableWriteErrorLabelIfNeeded helper. |
| driver-core/src/main/com/mongodb/internal/operation/TransactionOperation.java | Updates retryable write execution to pass the new effective retry setting parameter. |
| driver-core/src/main/com/mongodb/internal/operation/SyncOperationHelper.java | Refactors retryable read/write execution to use RetryControl + SpecRetryPolicy. |
| driver-core/src/main/com/mongodb/internal/operation/SpecRetryPolicy.java | Adds a unified spec-driven retry policy implementation for read/write retries. |
| driver-core/src/main/com/mongodb/internal/operation/retry/package-info.java | Removes the now-obsolete retry package metadata. |
| driver-core/src/main/com/mongodb/internal/operation/retry/AttachmentKeys.java | Removes attachment-key-based retry state plumbing. |
| driver-core/src/main/com/mongodb/internal/operation/OperationHelper.java | Renames and splits write retry requirement checks (non-command vs server-level). |
| driver-core/src/main/com/mongodb/internal/operation/MixedBulkWriteOperation.java | Refactors bulk write retry handling to use RetryControl/SpecRetryPolicy and fixes async retain/release flow. |
| driver-core/src/main/com/mongodb/internal/operation/ListIndexesOperation.java | Refactors retryable read execution to use RetryControl/SpecRetryPolicy. |
| driver-core/src/main/com/mongodb/internal/operation/ListCollectionsOperation.java | Refactors retryable read execution to use RetryControl/SpecRetryPolicy. |
| driver-core/src/main/com/mongodb/internal/operation/FindOperation.java | Refactors retryable read execution to use RetryControl/SpecRetryPolicy. |
| driver-core/src/main/com/mongodb/internal/operation/CommitTransactionOperation.java | Adjusts imports after retry refactor (no functional change in the shown hunk). |
| driver-core/src/main/com/mongodb/internal/operation/CommandOperationHelper.java | Replaces initialRetryState and attachment-based helpers with createSpecRetryControl and updated write-requirements helpers. |
| driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java | Refactors retry handling and cursor exhaustion with RetryControl.doWhileDisabled*, and updates retry requirement modeling. |
| driver-core/src/main/com/mongodb/internal/operation/BulkWriteBatch.java | Renames and clarifies bulk-write retry requirement computation and accessors. |
| driver-core/src/main/com/mongodb/internal/operation/BaseFindAndModifyOperation.java | Updates retryable write execution to pass effective retry writes setting parameter and renamed checks. |
| driver-core/src/main/com/mongodb/internal/operation/AsyncOperationHelper.java | Refactors retryable read/write async execution to use RetryControl + SpecRetryPolicy and simplifies callbacks. |
| driver-core/src/main/com/mongodb/internal/operation/AbortTransactionOperation.java | Adjusts imports after retry refactor (no functional change in the shown hunk). |
| driver-core/src/main/com/mongodb/internal/connection/OperationContext.java | Small rename for clarity in deprioritization failure handling. |
| driver-core/src/main/com/mongodb/internal/client/model/AbstractConstructibleBsonElement.java | Javadoc wording cleanup for internal constructible BSON elements. |
| driver-core/src/main/com/mongodb/internal/client/model/AbstractConstructibleBson.java | Javadoc wording cleanup for internal constructible BSON objects. |
| driver-core/src/main/com/mongodb/internal/async/SimpleRetryPolicy.java | Adds a simple predicate-backed policy used by AsyncRunnable retry composition. |
| driver-core/src/main/com/mongodb/internal/async/function/RetryState.java | Removes the previous retry state implementation. |
| driver-core/src/main/com/mongodb/internal/async/function/RetryPolicy.java | Adds the new retry policy interface and decision model. |
| driver-core/src/main/com/mongodb/internal/async/function/RetryingSyncSupplier.java | Refactors sync retry decorator to delegate retry decisions to RetryControl. |
| driver-core/src/main/com/mongodb/internal/async/function/RetryingAsyncCallbackSupplier.java | Refactors async retry decorator to delegate retry decisions to RetryControl. |
| driver-core/src/main/com/mongodb/internal/async/function/RetryControl.java | Adds the new retry controller with breaking/disable semantics. |
| driver-core/src/main/com/mongodb/internal/async/function/RetryContext.java | Adds the restricted view of retry state exposed to policies. |
| driver-core/src/main/com/mongodb/internal/async/function/package-info.java | Minor formatting cleanup in package metadata. |
| driver-core/src/main/com/mongodb/internal/async/function/LoopState.java | Removes the previous loop state implementation. |
| driver-core/src/main/com/mongodb/internal/async/function/LoopControl.java | Adds the new loop controller used by callback loop utilities. |
| driver-core/src/main/com/mongodb/internal/async/function/AsyncCallbackRunnable.java | Javadoc formatting cleanup. |
| driver-core/src/main/com/mongodb/internal/async/function/AsyncCallbackLoop.java | Refactors callback loop to use LoopControl and updates docs. |
| driver-core/src/main/com/mongodb/internal/async/function/AsyncCallbackFunction.java | Javadoc formatting cleanup. |
| driver-core/src/main/com/mongodb/internal/async/AsyncRunnable.java | Refactors retrying/looping compositions to use RetryControl/LoopControl. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
14ad7b4 to
cf47ad8
Compare
cf47ad8 to
dd340a1
Compare
|
The test failures match those on |
dd340a1 to
f1cf803
Compare
f1cf803 to
8cacb8c
Compare
JAVA-6229
8cacb8c to
48ee5ca
Compare
JAVA-6229
JAVA-6229
JAVA-6229
48ee5ca to
69ad9b4
Compare
There was a problem hiding this comment.
@vbabanin I extracted the "Rename LoopState -> LoopControl" and "Rename RetryState -> RetryControl" commits. This way Git preserves the history, which simplifies reviewing. I think, we should not lose them when squash-merging this PR. Let's discuss during the code walkthrough, and, if you agree, I propose to merge them alone separately into backpressure. Maybe could actually just "rebase and merge" this whole PR, but that's also to be discussed later.
| final OperationContext operationContext, | ||
| final SingleResultCallback<ClientBulkWriteResult> callback) { | ||
| beginAsync().<ClientBulkWriteResult>thenSupply(c -> { | ||
| binding.retain(); |
There was a problem hiding this comment.
Undo these changes, because they violate the ReferenceCounted usage proposed in https://jira.mongodb.org/browse/JAVA-3964?focusedCommentId=3584321&focusedId=3584321&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-3584321.
Notable changes
LoopState->LoopControl.RetryState->RetryControl.onAttemptFailureOperatorandretryPredicateinRetryState->RetryControlwere replaced byRetryPolicy.onAttemptFailure.retryPredicateis now called on every failed attempt, unlike previously, where onlyonAttemptFailureOperatorwas called on every failed attempt. As a result, we no longer need to callCommandOperationHelper.addRetryableLabelOrGetWriteAttemptFailureNotToBeRetried->SpecRetryPolicy.decideRetryableAndAddRetryableWriteErrorLabelIfNeededexplicitly inMixedBulkWriteOperation/ClientBulkWriteOperation, or useAsyncOperationHelper.addingRetryableLabelCallback/CommandOperationHelper.addRetryableWriteErrorLabelinAsyncOperationHelper.executeRetryableWriteAsync/SyncOperationHelper.executeRetryableWrite.RetryingAsyncCallbackSupplier,RetryingSyncSupplierno longer passErrors toadvanceOrThrow.RetryingSyncSupplierpassesExceptions toadvanceOrThrowas is, without wrapping them inRuntimeException. Wrapping is done byRetryState->RetryControl, and only if that checkedExceptionhas to be thrown.AttachmentKeyand the related methods and implementations were deleted. Instead,SpecRetryPolicynow exposes methods with more specific semantics and more specific assertions guarding their usage.AttachmentKeyscommandDescriptionSupplier->SpecRetryPolicy.onCommandinstead ofretryableWriteCommandFlag&maxWireVersion->SpecRetryPolicy.onWriteRetryRequirementscommand-> was not needed at all, and was replaced with a localMutableValue<BsonDocument>variable.SpecRetryPolicyimplements both read and write retry logic, the methodsAsyncOperationHelper.decorateReadWithRetriesAsync/decorateWriteWithRetriesAsyncandSyncOperationHelper.decorateReadWithRetries/decorateWriteWithRetrieswere replaced with a single methodAsyncOperationHelper.decorateWithRetriesAsyncandSyncOperationHelper.decorateWithRetriesrespectively.RetryState->RetryControl.doWhileDisabled/doWhileDisabledAsyncwas introduced to replace the brokenClientBulkWriteOperation.doWithRetriesDisabled/doWithRetriesDisabledAsync.AsyncOperationhelper.executeRetryableWriteAsyncwas rewritten with thebeginAsyncAPI. Now it is an even more straightforward translation ofSyncOperationHelper.executeRetryableWrite.RetryState->RetryControl.breakAndCompleteIfRetryAnd, and use only thebreakAndThrowIfRetryAndmethod in both sync and async code.RetryState->RetryControl.breakAndThrowIfRetryAndis now used only to break from a retry attempt based on the information that could not have been available before the attempt start. The only such information is the information about the server selected, which is checked by theOperationHelper.canRetryWrite->isServerWriteRetryRequirementsMetmethod.RetryState->RetryControl.breakAndThrowIfRetryAndis now used only by writes.BulkWriteBatchgetRetryWrites->isWriteRetryRequirementsMetisRetryable->isCommandWriteRetryRequirementsMetOperationHelperisRetryableWrite->isNonCommandWriteRetryRequirementsMetcanRetryWrite->isServerWriteRetryRequirementsMetcanRetryRead->isReadRetryRequirementsMetCommandOperationHelperonRetryableReadAttemptFailure-> became part ofSpecRetryPolicy.onAttemptFailurechooseRetryableReadException->SpecRetryPolicy.decideReadProspectiveFailedResultonRetryableWriteAttemptFailure-> became part ofSpecRetryPolicy.onAttemptFailurechooseRetryableWriteException->SpecRetryPolicy.decideWriteProspectiveFailedResultinitialRetryState->createSpecRetryControlloggingShouldAttemptToRetryRead-> became part ofSpecRetryPolicy.onAttemptFailure,isRetryableReadError.loggingShouldAttemptToRetryWriteAndAddRetryableLabel-> became part ofSpecRetryPolicy.onAttemptFailureaddRetryableLabelOrGetWriteAttemptFailureNotToBeRetried->SpecRetryPolicy.decideRetryableAndAddRetryableWriteErrorLabelIfNeededisRetryableWriteCommand->isWriteRetryRequirementsMetdecideRetryableAndAddRetryableWriteErrorLabel-> became part ofSpecRetryPolicy.decideRetryableAndAddRetryableWriteErrorLabelIfNeededaddRetryableWriteErrorLabel->addRetryableWriteErrorLabelIfNeededlogRetryCommand->SpecRetryPolicy.onAttemptStartlogUnableToRetryCommand->SpecRetryPolicy.logUnableToRetryError(also,SpecRetryPolicy.logUnableToRetryMaxAttemptsReachedwas added)AI usage
All changes were done manually. AI was used only to review the code after I self-reviewed it.
JAVA-6229