Skip to content

Introduce the RetryPolicy abstraction#2004

Open
stIncMale wants to merge 4 commits into
mongodb:backpressurefrom
stIncMale:introduceRetryPolicy
Open

Introduce the RetryPolicy abstraction#2004
stIncMale wants to merge 4 commits into
mongodb:backpressurefrom
stIncMale:introduceRetryPolicy

Conversation

@stIncMale

@stIncMale stIncMale commented Jun 19, 2026

Copy link
Copy Markdown
Member

Notable changes

  • LoopState -> LoopControl.
  • RetryState -> RetryControl.
  • onAttemptFailureOperator and retryPredicate in RetryState->RetryControl were replaced by RetryPolicy.onAttemptFailure.
    • An accidental consequence is that the logic that was in retryPredicate is now called on every failed attempt, unlike previously, where only onAttemptFailureOperator was called on every failed attempt. As a result, we no longer need to call CommandOperationHelper.addRetryableLabelOrGetWriteAttemptFailureNotToBeRetried->SpecRetryPolicy.decideRetryableAndAddRetryableWriteErrorLabelIfNeeded explicitly in MixedBulkWriteOperation/ClientBulkWriteOperation, or use AsyncOperationHelper.addingRetryableLabelCallback/CommandOperationHelper.addRetryableWriteErrorLabel in AsyncOperationHelper.executeRetryableWriteAsync/SyncOperationHelper.executeRetryableWrite.
  • RetryingAsyncCallbackSupplier, RetryingSyncSupplier no longer pass Errors to advanceOrThrow.
  • RetryingSyncSupplier passes Exceptions to advanceOrThrow as is, without wrapping them in RuntimeException. Wrapping is done by RetryState->RetryControl, and only if that checked Exception has to be thrown.
  • AttachmentKey and the related methods and implementations were deleted. Instead, SpecRetryPolicy now exposes methods with more specific semantics and more specific assertions guarding their usage.
  • AttachmentKeys
    • commandDescriptionSupplier -> SpecRetryPolicy.onCommand instead of
    • retryableWriteCommandFlag&maxWireVersion -> SpecRetryPolicy.onWriteRetryRequirements
    • command -> was not needed at all, and was replaced with a local MutableValue<BsonDocument> variable.
  • Since SpecRetryPolicy implements both read and write retry logic, the methods AsyncOperationHelper.decorateReadWithRetriesAsync/decorateWriteWithRetriesAsync and SyncOperationHelper.decorateReadWithRetries/decorateWriteWithRetries were replaced with a single method AsyncOperationHelper.decorateWithRetriesAsync and SyncOperationHelper.decorateWithRetries respectively.
  • RetryState->RetryControl.doWhileDisabled/doWhileDisabledAsync was introduced to replace the broken ClientBulkWriteOperation.doWithRetriesDisabled/doWithRetriesDisabledAsync.
  • AsyncOperationhelper.executeRetryableWriteAsync was rewritten with the beginAsync API. Now it is an even more straightforward translation of SyncOperationHelper.executeRetryableWrite.
    • More importantly, this allowed us to delete RetryState->RetryControl.breakAndCompleteIfRetryAnd, and use only the breakAndThrowIfRetryAnd method in both sync and async code.
  • RetryState->RetryControl.breakAndThrowIfRetryAnd is 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 the OperationHelper.canRetryWrite->isServerWriteRetryRequirementsMet method.
    • Read commands have not been checking that information, so RetryState->RetryControl.breakAndThrowIfRetryAnd is now used only by writes.
  • BulkWriteBatch
    • getRetryWrites -> isWriteRetryRequirementsMet
    • isRetryable -> isCommandWriteRetryRequirementsMet
  • OperationHelper
    • isRetryableWrite -> isNonCommandWriteRetryRequirementsMet
    • canRetryWrite -> isServerWriteRetryRequirementsMet
    • canRetryRead -> isReadRetryRequirementsMet
  • CommandOperationHelper
    • onRetryableReadAttemptFailure -> became part of SpecRetryPolicy.onAttemptFailure
    • chooseRetryableReadException -> SpecRetryPolicy.decideReadProspectiveFailedResult
    • onRetryableWriteAttemptFailure -> became part of SpecRetryPolicy.onAttemptFailure
    • chooseRetryableWriteException -> SpecRetryPolicy.decideWriteProspectiveFailedResult
    • initialRetryState -> createSpecRetryControl
    • loggingShouldAttemptToRetryRead -> became part of SpecRetryPolicy.onAttemptFailure, isRetryableReadError.
    • loggingShouldAttemptToRetryWriteAndAddRetryableLabel -> became part of SpecRetryPolicy.onAttemptFailure
    • addRetryableLabelOrGetWriteAttemptFailureNotToBeRetried -> SpecRetryPolicy.decideRetryableAndAddRetryableWriteErrorLabelIfNeeded
    • isRetryableWriteCommand -> isWriteRetryRequirementsMet
    • decideRetryableAndAddRetryableWriteErrorLabel -> became part of SpecRetryPolicy.decideRetryableAndAddRetryableWriteErrorLabelIfNeeded
    • addRetryableWriteErrorLabel -> addRetryableWriteErrorLabelIfNeeded
    • logRetryCommand -> SpecRetryPolicy.onAttemptStart
    • logUnableToRetryCommand -> SpecRetryPolicy.logUnableToRetryError (also, SpecRetryPolicy.logUnableToRetryMaxAttemptsReached was added)

AI usage

All changes were done manually. AI was used only to review the code after I self-reviewed it.

JAVA-6229

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/LoopState with RetryControl/LoopControl, and introduce RetryPolicy + RetryContext.
  • Add SpecRetryPolicy and 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 bulkWrite is not retried when a follow-up getMore fails.

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.

Comment thread driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java Outdated
Comment thread driver-core/src/main/com/mongodb/internal/TimeoutContext.java Outdated
@stIncMale stIncMale force-pushed the introduceRetryPolicy branch 2 times, most recently from 14ad7b4 to cf47ad8 Compare June 19, 2026 18:35
@stIncMale stIncMale requested a review from Copilot June 19, 2026 18:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 52 out of 52 changed files in this pull request and generated 1 comment.

Comment thread driver-core/src/main/com/mongodb/internal/TimeoutContext.java
@stIncMale stIncMale force-pushed the introduceRetryPolicy branch from cf47ad8 to dd340a1 Compare June 19, 2026 19:58
@stIncMale

Copy link
Copy Markdown
Member Author

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 52 out of 52 changed files in this pull request and generated 4 comments.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 52 out of 52 changed files in this pull request and generated 1 comment.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 52 out of 52 changed files in this pull request and generated 3 comments.

Comment thread driver-core/src/main/com/mongodb/internal/TimeoutContext.java Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 53 out of 53 changed files in this pull request and generated no new comments.

@stIncMale stIncMale marked this pull request as ready for review June 20, 2026 01:46
@stIncMale stIncMale requested a review from a team as a code owner June 20, 2026 01:46
@stIncMale stIncMale force-pushed the introduceRetryPolicy branch from 48ee5ca to 69ad9b4 Compare June 22, 2026 19:28

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants