Skip to content

Conversation

@sanych-sun
Copy link
Member

No description provided.

@sanych-sun sanych-sun requested a review from a team as a code owner June 6, 2025 01:11
@sanych-sun sanych-sun added the feature Adds new user-facing functionality. label Jun 6, 2025
@sanych-sun sanych-sun removed the request for review from a team June 6, 2025 01:16

message =
$"Timed out after {stopwatch.ElapsedMilliseconds}ms waiting for a connection from the connection pool. " +
$"Timed out after {operationContext.Elapsed.TotalMilliseconds}ms waiting for a connection from the connection pool. " +
Copy link
Contributor

Choose a reason for hiding this comment

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

minor: Looks like Elapsed.TotalMilliseconds is frequently used, does this award a shortcut property:
ElapsedMilleseconds?

Copy link
Member Author

Choose a reason for hiding this comment

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

Reverted to use stopwatch here.

// TODO: this static field is temporary here and will be removed in a future PRs in scope of CSOT.
public static readonly OperationContext NoTimeout = new(System.Threading.Timeout.InfiniteTimeSpan, CancellationToken.None);

private readonly Stopwatch _stopwatch;
Copy link
Contributor

Choose a reason for hiding this comment

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

So we are saving the multiple stopwatches creation on operation execution path, it's minor but still nice.

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, but as it was discussed had to revert the change =)

bool async)
{
var subject = CreateSubject();
var subject = CreateSubject(serverSelectionTimeout: TimeSpan.FromMilliseconds(10));
Copy link
Member Author

Choose a reason for hiding this comment

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

This adjusted serverSelectionTimeout is an optimization of the test: we probably do not have to wait whole 2 seconds here (the default server selection for this test class, see line 49).

.Subject.ParamName.Should().Be("session");
}

private OperationExecutor CreateSubject(out Mock<IClusterInternal> clusterMock, out Mock<ICoreSessionHandle> implicitSessionMock)
Copy link
Member Author

Choose a reason for hiding this comment

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

Leftovers of the implicit session creation, that was factored out in one of the latest commits related to OperationExecutor refactoing.

@sanych-sun sanych-sun requested a review from rstam June 7, 2025 00:55
@sanych-sun sanych-sun requested a review from BorisDog June 9, 2025 20:48
@BorisDog BorisDog requested a review from Copilot June 9, 2025 21:15
Copy link

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 refactors various server selection and connection pool APIs to use a new OperationContext abstraction instead of separate CancellationToken and timeout TimeSpan parameters. Key changes include:

  • Replaced all CancellationToken (and internal Stopwatch timeout logic) with OperationContext throughout connection pool helpers, cluster selection, and binding classes.
  • Updated CreateTimeoutException methods to accept a TimeSpan elapsed rather than a Stopwatch.
  • Removed legacy overloads and helper classes associated with CancellationToken/TimeSpan and simplified server selection wait logic using OperationContext.

Reviewed Changes

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

Show a summary per file
File Description
src/MongoDB.Driver/Core/ConnectionPools/ExclusiveConnectionPool.Helpers.cs Swapped Stopwatch usage and token/timeouts for OperationContext
src/MongoDB.Driver/Core/Clusters/LoadBalancedCluster.cs Updated server selection to use OperationContext and WithTimeout
src/MongoDB.Driver/Core/Clusters/IClusterExtensions.cs Changed extension methods to accept OperationContext
src/MongoDB.Driver/Core/Clusters/ICluster.cs Updated interface signatures to use OperationContext
src/MongoDB.Driver/Core/Clusters/Cluster.cs Refactored internal select/wait loops to data in OperationContext
src/MongoDB.Driver/Core/Bindings/**/*.cs Replaced all CancellationToken parameters in binding APIs with OperationContext
Comments suppressed due to low confidence (3)

src/MongoDB.Driver/Core/ConnectionPools/ExclusiveConnectionPool.Helpers.cs:50

  • [nitpick] Using TotalMilliseconds can include fractional values (e.g., 12.345ms). Consider formatting with a fixed precision or rounding (e.g., {elapsed.TotalMilliseconds:F0}) to produce a cleaner integer-based message.
message = $"Timed out after {elapsed.TotalMilliseconds}ms waiting for a connection from the connection pool. " +

src/MongoDB.Driver/Core/Clusters/LoadBalancedCluster.cs:179

  • The new WithTimeout logic and WaitTask behavior are significant changes to server selection. Please add unit tests that simulate both timeout and cancellation scenarios for SelectServer/SelectServerAsync using mocks/stubs for _serverReadyTaskCompletionSource.
var serverSelectionOperationContext = operationContext.WithTimeout(_settings.ServerSelectionTimeout);

src/MongoDB.Driver/Core/Clusters/ICluster.cs:64

  • Removing the old CancellationToken overloads is a breaking API change. Consider retaining the old overloads (marked obsolete) to preserve backward compatibility, or provide clear upgrade guidance in release notes.
IServer SelectServer(IServerSelector selector, OperationContext operationContext);

if (index != 0)
try
{
serverSelectionOperationContext.WaitTask(_serverReadyTaskCompletionSource.Task);
Copy link

Copilot AI Jun 9, 2025

Choose a reason for hiding this comment

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

The original code explicitly threw on cancellation (cancellationToken.ThrowIfCancellationRequested()), but this method only catches TimeoutException. Consider catching OperationCanceledException from WaitTask or explicitly checking operationContext.CancellationToken to ensure cancellations are surfaced correctly.

Copilot uses AI. Check for mistakes.
// Server selection also updates the cluster type, allowing us to determine if the server
// should be pinned.
var server = cluster.SelectServer(selector, cancellationToken);
var server = cluster.SelectServer(selector, operationContext);
Copy link

Copilot AI Jun 9, 2025

Choose a reason for hiding this comment

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

[nitpick] We added non-null checks for selector and operationContext in core cluster methods; consider adding Ensure.IsNotNull(operationContext, nameof(operationContext)); here as well to maintain consistent validation.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

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

For some reasons we do not validate other parameters in the IClusterExtensions methods. Will keep it as is for now.

Comment on lines 51 to 56
{
return GetReadChannelSource(null, cancellationToken);
return GetReadChannelSource(null, operationContext);
}

public Task<IChannelSourceHandle> GetReadChannelSourceAsync(CancellationToken cancellationToken)
public Task<IChannelSourceHandle> GetReadChannelSourceAsync(OperationContext operationContext)
{
Copy link

Copilot AI Jun 9, 2025

Choose a reason for hiding this comment

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

[nitpick] Most public API methods now guard against null operationContext. Add an Ensure.IsNotNull(operationContext, nameof(operationContext)); at the top of this method to match the pattern in cluster selection.

Copilot uses AI. Check for mistakes.
}
catch (TimeoutException)
{
var message = BuildTimeoutExceptionMessage(_settings.ServerSelectionTimeout, selector, helper.Description);
Copy link
Contributor

Choose a reason for hiding this comment

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

BuildTimeoutExceptionMessage doesn't inform the user whether the operation timed out or the server selection timed out.

@sanych-sun sanych-sun requested a review from rstam June 10, 2025 19:09
using (var connectionCreator = new ConnectionCreator(_connectionPool))
{
var connection = connectionCreator.CreateOpened(cancellationToken);
var connection = connectionCreator.CreateOpened(operationContext);
Copy link
Contributor

Choose a reason for hiding this comment

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

The reason for tiny 20ms timeout is just try to take turn in the connecting queue without blocking checkouts.
This timeout is applied to every step individually: MaxConnections and MaxConnecting.
Seems that this code changes this and allocates 20ms to

  • MaxConnections
  • MaxConnecting
  • Whole Connection creation operation (excluding open)

If this thought is correct, it's really surprising that this was not caught by tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

@sanych-sun was this addressed?

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Contributor

Choose a reason for hiding this comment

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

What about _maxConnectingQueue inside ConnectionCreator ?
We don't want to block on that for whole timeout, just a quick try instead.

Copy link
Contributor

@rstam rstam left a comment

Choose a reason for hiding this comment

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

LGTM

Ensure.IsNotNull(session, nameof(session));
ThrowIfDisposed();

var cancellationContext = options.ToOperationContext(cancellationToken);
Copy link
Contributor

Choose a reason for hiding this comment

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

rename this variable (and other instances) to operationContext?

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

{
exception = Record.Exception(() => subject.SelectServer(Mock.Of<IServerSelector>(), cancellationTokenSource.Token));
}
var cancellationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationTokenSource.Token);
Copy link
Contributor

Choose a reason for hiding this comment

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

rename variable to operationContext?

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.

using (var connectionCreator = new ConnectionCreator(_connectionPool))
{
var connection = connectionCreator.CreateOpened(cancellationToken);
var connection = connectionCreator.CreateOpened(operationContext);
Copy link
Contributor

Choose a reason for hiding this comment

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

@sanych-sun was this addressed?

_enteredWaitQueue = true;
}

private void EnsureTimeout(OperationContext operationContext, Stopwatch stopwatch)
Copy link
Contributor

Choose a reason for hiding this comment

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

Would ThrowIfTimedOut be a better name?

Normally we use EnsureXyz for argument validation.

Copy link
Member Author

Choose a reason for hiding this comment

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

Renamed

Copy link
Contributor

@adelinowona adelinowona left a comment

Choose a reason for hiding this comment

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

LGTM

@sanych-sun sanych-sun requested a review from BorisDog June 11, 2025 21:28
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.

LGTM


return CreateOpenedInternal(cancellationToken);
var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken);
return CreateOpenedInternal(operationContext);
Copy link
Contributor

Choose a reason for hiding this comment

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

minor: return CreateOpenedInternal(new(Timeout.InfiniteTimeSpan, cancellationToken));

@sanych-sun sanych-sun merged commit cc69339 into mongodb:main Jun 12, 2025
31 of 35 checks passed
@sanych-sun sanych-sun deleted the csharp3550 branch June 12, 2025 15:39
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.

4 participants