Skip to content

Conversation

@sanych-sun
Copy link
Member

@sanych-sun sanych-sun commented Dec 19, 2025

No description provided.

Copilot AI review requested due to automatic review settings December 19, 2025 01:01
@sanych-sun sanych-sun requested a review from a team as a code owner December 19, 2025 01:01
@sanych-sun sanych-sun requested review from adelinowona and papafe and removed request for adelinowona December 19, 2025 01:01
@sanych-sun sanych-sun added the chore Non–user-facing code changes (tests, build scripts, etc.). label Dec 19, 2025
Copy link
Contributor

Copilot AI left a comment

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 implements the RetryabilityHelper.GetRetryDelayMs method to control retry delays with exponential backoff, addressing the issue of transactions retrying too frequently. The implementation adds jitter to prevent thundering herd problems and includes comprehensive parameter validation.

Key Changes

  • Adds GetRetryDelayMs method with exponential backoff calculation and random jitter
  • Removes unused MongoDB.Driver.Core.Connections import from test file
  • Adds comprehensive test coverage for valid inputs, edge cases, and parameter validation

Reviewed changes

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

File Description
src/MongoDB.Driver/Core/Operations/RetryabilityHelper.cs Implements the core retry delay calculation with exponential backoff and jitter
tests/MongoDB.Driver.Tests/Core/Operations/RetryabilityHelperTests.cs Adds unit tests for the new method and removes unused import

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Ensure.IsGreaterThan(backoffMax, backoffInitial, nameof(backoffMax));

var j = __backoffRandom.NextDouble();
return (int)(j * Math.Min(backoffMax, backoffInitial * Math.Pow(backoffBase, attempt - 1)));
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

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

The calculation backoffInitial * Math.Pow(backoffBase, attempt - 1) may overflow for large attempt values, which would then be capped by Math.Min. Consider adding overflow protection or documenting the maximum safe attempt value to prevent unexpected behavior.

Suggested change
return (int)(j * Math.Min(backoffMax, backoffInitial * Math.Pow(backoffBase, attempt - 1)));
// compute the largest exponent such that backoffInitial * backoffBase^exponent <= backoffMax
var maxExponent = Math.Log(backoffMax / (double)backoffInitial, backoffBase);
var effectiveExponent = attempt - 1;
double delayWithoutJitter;
if (effectiveExponent >= maxExponent)
{
delayWithoutJitter = backoffMax;
}
else
{
delayWithoutJitter = backoffInitial * Math.Pow(backoffBase, effectiveExponent);
}
return (int)(j * delayWithoutJitter);

Copilot uses AI. Check for mistakes.
@sanych-sun sanych-sun requested a review from Copilot December 19, 2025 01:11
Copy link
Contributor

Copilot AI left a comment

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 2 out of 2 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@sanych-sun sanych-sun added feature Adds new user-facing functionality. and removed chore Non–user-facing code changes (tests, build scripts, etc.). labels Dec 19, 2025
Copy link
Contributor

Copilot AI left a comment

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 4 out of 4 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@papafe papafe left a comment

Choose a reason for hiding this comment

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

Looks good!

}

var subject = CreateSubject(coreSession: mockCoreSession.Object, clock: mockClock.Object);
SetupTransactionState(subject, true);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this related to the ticket?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, changes were rolled back. Thank you for noticing this.

Copy link
Contributor

@BorisDog BorisDog left a comment

Choose a reason for hiding this comment

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

Partial review

{
return callbackOutcome.Result;
var delay = GetRetryDelay(randomNumberGenerator, attempt);
if (HasTimedOut(operationContext, delay))
Copy link
Contributor

Choose a reason for hiding this comment

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

IsTimedOut?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

@sanych-sun sanych-sun requested a review from BorisDog January 5, 2026 23:32
@sanych-sun sanych-sun requested a review from BorisDog January 6, 2026 04:18
var backoffTime = await ExecuteWithTransactionAsync(1);

var difference = backoffTime - noBackoffTime - TimeSpan.FromSeconds(2.2);
difference.Should().BeLessThan(TimeSpan.FromMilliseconds(1));
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we using a 1 millisecond window here instead of a 1 second because we're "faking" the prose test?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep. We do not interact with the server, so timing should be more strict. 1ms could be small though, we might will need to adjust it in future.


return sb.ToString();
#else
return string.Create(length, legalCharacters, (buffer, charset) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice!

return callback(clientSession, cancellationToken);
}
catch (Exception ex)
catch (Exception) when (IsTransactionInStartingOrInProgressState(clientSession))
Copy link
Contributor

Choose a reason for hiding this comment

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

Will
catch when (IsTransactionInStartingOrInProgressState(clientSession))
work?

Copy link
Member Author

Choose a reason for hiding this comment

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

The entire method was removed.

var noBackoffTime = await ExecuteWithTransactionAsync(0);
var backoffTime = await ExecuteWithTransactionAsync(1);

var difference = backoffTime - noBackoffTime - TimeSpan.FromSeconds(2.2);
Copy link
Contributor

Choose a reason for hiding this comment

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

Will
backoffTimeMS.Should().BeApproximately(noBackoffTimeMS + 2200, 1);
be more readable?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

[InlineData(1, 2, 100, -1, "backoffMax")]
[InlineData(1, 2, 100, 0, "backoffMax")]
[InlineData(1, 2, 100, 50, "backoffMax")]

Copy link
Contributor

Choose a reason for hiding this comment

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

Empty line

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed.

[InlineData(2, 1.5, 100, 10000, 0, 150)]
[InlineData(3, 1.5, 100, 10000, 0, 225)]
[InlineData(9999, 1.5, 100, 10000, 0, 10000)]
public void GetRetryDelayMs_should_return_expected_results(int attempt, double backoffBase, int backoffInitial, int backoffMax, int expectedRangeMin, int expectedRangeMax)
Copy link
Contributor

Choose a reason for hiding this comment

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

minor: expected_result

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

@sanych-sun sanych-sun requested a review from BorisDog January 8, 2026 20:24
Copy link
Contributor

@BorisDog BorisDog left a comment

Choose a reason for hiding this comment

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

Minor suggestion

{
return callbackOutcome.Result; // assume callback intentionally ended the transaction
}
if (IsTransactionInStartingOrInProgressState(clientSession))
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar to ShouldRetryTransaction consider ShouldAbortTransaction(out AbortTransactionOptions)

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

Labels

feature Adds new user-facing functionality.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants