Skip to content

CSHARP-6029: Make ICoreServerSessionPool disposable and send endSessions on dispose#1995

Open
sanych-sun wants to merge 6 commits into
mongodb:mainfrom
sanych-sun:CSHARP-6029
Open

CSHARP-6029: Make ICoreServerSessionPool disposable and send endSessions on dispose#1995
sanych-sun wants to merge 6 commits into
mongodb:mainfrom
sanych-sun:CSHARP-6029

Conversation

@sanych-sun
Copy link
Copy Markdown
Member

No description provided.

Copilot AI review requested due to automatic review settings May 9, 2026 00:26
@sanych-sun sanych-sun requested a review from a team as a code owner May 9, 2026 00:26
@sanych-sun sanych-sun added the bug Fixes issues or unintended behavior. label May 9, 2026
@sanych-sun sanych-sun requested review from BorisDog and ajcvickers and removed request for ajcvickers May 9, 2026 00:26
@sanych-sun sanych-sun marked this pull request as draft May 9, 2026 00:26
Copy link
Copy Markdown
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 aims to ensure server-side sessions are explicitly ended when a client/cluster is disposed (via an endSessions command), and adjusts test event capture behavior to avoid capturing disposal-time commands.

Changes:

  • Added session-pool draining logic that issues endSessions for pooled server sessions during cluster disposal.
  • Introduced EventCapturer.Stop() and invoked it from the JSON-driven spec test runner.
  • Added a shared helper on Cluster to select an available server and trigger session-pool cleanup during disposal.

Reviewed changes

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

Show a summary per file
File Description
tests/MongoDB.Driver.Tests/Specifications/Runner/MongoClientJsonDrivenTestRunnerBase.cs Stops event capture after test operations complete.
tests/MongoDB.Driver.TestHelpers/Core/EventCapturer.cs Adds a stop flag to disable capturing after disposal begins.
src/MongoDB.Driver/Core/Clusters/SingleServerCluster.cs Triggers session-pool cleanup before server removal on dispose.
src/MongoDB.Driver/Core/Clusters/MultiServerCluster.cs Triggers session-pool cleanup during cluster disposal.
src/MongoDB.Driver/Core/Clusters/Cluster.cs Adds ReleaseServerSessionPool() helper to drive cleanup.
src/MongoDB.Driver/Core/Bindings/ICoreServerSessionPool.cs Extends pool contract with ReleaseAll(IServer).
src/MongoDB.Driver/Core/Bindings/CoreServerSessionPool.cs Implements ReleaseAll to drain pooled sessions and send endSessions.

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

Comment thread tests/MongoDB.Driver.TestHelpers/Core/EventCapturer.cs
Comment thread src/MongoDB.Driver/Core/Bindings/CoreServerSessionPool.cs
Comment thread src/MongoDB.Driver/Core/Bindings/CoreServerSessionPool.cs Outdated
Comment on lines +116 to +120
}

sessionsBatch = _pool.GetRange(0, Math.Min(_pool.Count, 9999));
_pool.RemoveRange(0, sessionsBatch.Count);
}
Comment on lines +99 to +149
public void ReleaseAll(IServer server)
{
if (_pool.Count == 0)
{
return;
}

try
{
while (true)
{
List<ICoreServerSession> sessionsBatch;
lock (_lock)
{
if (_pool.Count == 0)
{
return;
}

sessionsBatch = _pool.GetRange(0, Math.Min(_pool.Count, 9999));
_pool.RemoveRange(0, sessionsBatch.Count);
}

var endSessionCommand = new BsonDocument("endSessions", new BsonArray(sessionsBatch.Select(s => s.Id)));
foreach (var session in sessionsBatch)
{
session.Dispose();
}

var operationContext = OperationContext.NoTimeout;
using var channel = server.GetChannel(operationContext);
channel.Command(
operationContext,
NoCoreSession.Instance,
ReadPreference.PrimaryPreferred,
DatabaseNamespace.Admin,
endSessionCommand,
null,
NoOpElementNameValidator.Instance,
null,
null,
CommandResponseHandling.Return,
BsonDocumentSerializer.Instance,
null);
}
}
catch
{
// Ignore any errors.
}
}
Comment on lines +20 to 25
internal interface ICoreServerSessionPool
{
internal interface ICoreServerSessionPool
{
ICoreServerSession AcquireSession();
void ReleaseSession(ICoreServerSession serverSession);
}
ICoreServerSession AcquireSession();
void ReleaseSession(ICoreServerSession serverSession);
void ReleaseAll(IServer server);
}
Comment thread src/MongoDB.Driver/Core/Bindings/CoreServerSessionPool.cs
@sanych-sun sanych-sun marked this pull request as ready for review May 22, 2026 03:10
private readonly object _lock = new object();
private readonly List<ICoreServerSession> _pool = new List<ICoreServerSession>();
private readonly ILogger<LogCategories.Client> _logger;
private readonly ConcurrentStack<ICoreServerSession> _pool = new();
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.

Accordingly to spec pool should be LIFO.

public void ReleaseSession(ICoreServerSession session)
{
lock (_lock)
ThrowIfDisposed();
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.

Is this be a behavioral breaking change? In the case server is disposed but the session/operation in flight is not, the exception might happen even for a successful operation?

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.

It is, but if MongoClient/Cluster is being disposed - all resources associated with it should be cleaned up. Otherwise we might let cluster "resurrect" and have leaked resources.

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.

Yes the resources should be clean up.
But should the pool throw on the "after-cleanup release attempt"?

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.

If somebody disposed client/cluster and then suddenly started returning sessions to the pool: it means a) they disposed cluster too early b) the returned sessions might not be closed properly (if CloseAndDispose is already done).

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.

As it was discussed offline we will log an error for now to prevent any possible breaking change in the minor release.

}
else
{
Interlocked.Increment(ref _sessionsAcquired);
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.

nit: consider having a single place for incrementing for readability (single return)

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.

Done.

Comment thread src/MongoDB.Driver/Core/Bindings/CoreServerSessionPool.cs
Comment thread src/MongoDB.Driver/Core/Bindings/CoreServerSessionPool.cs
{
_logger?.LogDebug(
"Closed server session pool for {clusterId} in {milliseconds}ms.",
_cluster.ClusterId, (Stopwatch.GetTimestamp() - timestamp) / (double)Stopwatch.Frequency * 1000);
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.

Add how many session where disposed (out of initial)?

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.

Done

Comment thread src/MongoDB.Driver/Core/Bindings/CoreServerSessionPool.cs
Comment thread src/MongoDB.Driver/Core/Bindings/CoreServerSessionPool.cs
}
catch(Exception ex)
{
_logger?.LogError(ex, "Error closing server session pool for {clusterId}: {exception}", _cluster.ClusterId, ex);
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.

Do we need to pass ex as an message formatting argument as well?

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.

It's there, the last parameter.

Copy link
Copy Markdown
Contributor

@BorisDog BorisDog May 22, 2026

Choose a reason for hiding this comment

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

Yes, but do we need it twice?

@sanych-sun sanych-sun requested a review from BorisDog May 22, 2026 18:06
public void ReleaseSession(ICoreServerSession session)
{
lock (_lock)
ThrowIfDisposed();
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.

Yes the resources should be clean up.
But should the pool throw on the "after-cleanup release attempt"?

Comment thread src/MongoDB.Driver/Core/Bindings/CoreServerSessionPool.cs
subject.ReleaseSession(mockSession.Object);

if (releasedSessionWasRecentlyUsed == 1)
if(shouldReturnToPool)
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.

space after if

@sanych-sun sanych-sun requested a review from BorisDog May 22, 2026 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Fixes issues or unintended behavior.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants