Skip to content

CSHARP-5560: CSOT: Refactor IOperationExecutor to use operation context #1676

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

sanych-sun
Copy link
Member

No description provided.

@sanych-sun sanych-sun requested a review from a team as a code owner April 24, 2025 00:08
@sanych-sun sanych-sun requested review from BorisDog and adelinowona and removed request for a team April 24, 2025 00:08
{
return operation.Execute(binding, cancellationToken);
bool isOwnSession = session == null;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: use var?

here and other methods in this file

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.

{
return _client.StartImplicitSessionAsync(cancellationToken);
var options = new ClientSessionOptions { CausalConsistency = false, Snapshot = false };
ICoreSessionHandle coreSession = _client.GetClusterInternal().StartSession(options.ToCore(isImplicit: true));
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: use var?

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.


namespace MongoDB.Driver
{
internal record class ReadOperationOptions(ReadPreference ExplicitReadPreference = null, ReadPreference DefaultReadPreference = null);
Copy link
Contributor

Choose a reason for hiding this comment

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

no need for specifying class? I believe record is equivalent to record class

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm OK with removing the class here. I've left it only to stress on the fact that it should be class, not struct. As changing to struct might affect its functionality (because of the copy-by-value nature of structs).

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah let's remove the class, record is by default a class and if someone tries to change it to struct then we can address it then in the PR containing that change.

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

Comment on lines 225 to 202
return UsingImplicitSession(session => BulkWrite(session, requests, options, cancellationToken), cancellationToken);
using var session = OperationExecutor.StartImplicitSession(cancellationToken);
return BulkWrite(session, requests, options, cancellationToken);
Copy link
Contributor

Choose a reason for hiding this comment

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

I noticed that for some of the other methods above (like AggregateToCollection) you did not use OperationExecutor to create the implicit session and just used the existing UsingImplicitSession mechanism. Should we update all those use cases as well? or am I missing something?

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 idea was to hide the implicit session creation inside of the OperationExecutor. But for some corner cases (like Aggregate method) we should have access to the created session, so sometimes we have to create the implicit session from outside.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thank you for reporting this. I've converted rest of the methods and removed UsingImplicitSession.

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.

Looks good! left some minor comments

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: minor comments so far.

@@ -58,6 +58,8 @@ private MongoCollectionImpl(IMongoDatabase database, CollectionNamespace collect
_documentSerializer = Ensure.IsNotNull(documentSerializer, nameof(documentSerializer));

_messageEncoderSettings = GetMessageEncoderSettings();
_readOperationOptions = new ReadOperationOptions(DefaultReadPreference: _settings.ReadPreference);
_writeOperationOptions = new WriteOperationOptions();
Copy link
Contributor

Choose a reason for hiding this comment

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

minor:

_readOperationOptions = new()
_writeOperationOptions = new(...)

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

Ensure.IsNotNullOrEmpty(name, nameof(name));
if (name == "*")
{
throw new ArgumentException("Cannot specify '*' for the index name. Use DropAllAsync to drop all indexes.", "name");
Copy link
Contributor

Choose a reason for hiding this comment

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

minor: mistake in the original code, should be DropAll and DropAllAsync.

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

@@ -50,6 +51,9 @@ public MongoDatabase(IMongoClient client, DatabaseNamespace databaseNamespace, M
_settings = Ensure.IsNotNull(settings, nameof(settings)).Freeze();
_cluster = Ensure.IsNotNull(cluster, nameof(cluster));
_operationExecutor = Ensure.IsNotNull(operationExecutor, nameof(operationExecutor));

_readOperationOptions = new ReadOperationOptions(DefaultReadPreference: _settings.ReadPreference);
_writeOperationOptions = new WriteOperationOptions();
Copy link
Contributor

Choose a reason for hiding this comment

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

minor: new(...) ?

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.


private IReadWriteBindingHandle CreateReadWriteBinding(IClientSessionHandle session)
{
// TODO: CreateReadWriteBinding from MongoClient did not used ChannelPinningHelper, double-check if it's OK to start using it
Copy link
Contributor

Choose a reason for hiding this comment

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

Need to address this TODO.

Copy link
Member Author

Choose a reason for hiding this comment

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

Add parameter to disable channel pinning + ticket to investigate why do we need that: https://jira.mongodb.org/browse/CSHARP-5600

@@ -100,7 +104,7 @@ public MongoClient(string connectionString)
internal MongoClient(IOperationExecutor operationExecutor, MongoClientSettings settings)
: this(settings)
{
_operationExecutor = operationExecutor;
_operationExecutor = new OperationExecutorWrapper(operationExecutor);
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider using the Fork approach.

Copy link
Contributor

Choose a reason for hiding this comment

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

@BorisDog what is the Fork approach?

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 problem here is: MongoClient implements IDisposable, and disposed mongo client throws ObjectDisposedException. MongoDatabase and MongoCollection are not disposable on other hand. However most of work regarding commands execution is implemented in OperationExecutor, that is passed into Database and Collection. OperationExecutor holds reference to mongo client, but not used to implement IDisposable - which means that operation executor passed to database would be half-operational after the mongo client would be disposed. By half-operational I mean it will throw on any code path which require accessing mongo client: for example any implicit session creation will require that.

This is what I've tried to address here - I've made OperationExecutor disposable and move all disposed checks there. So now we will have consistent behaviour: once mongo client will be disposed - all databases and collections created from the client will throw. So this is a good part.

The bed part: mongo client has a number of methods to clone itself, by adjusting some settings (like WithReadConcern). Such method should create a new copy of mongo client with separate operation executor - so disposal of the original mongo client will not affect the created one. Plus we have a tests that wants to substitute implementation of the OperationExcecutor with the mock.

The only option to achieve all of that is to introduce the OperationExecutorFactory parameter - the factory method that accepts IMongoClient and returns OperationExecutor. So tests could substitute it, and clonning methods could ask for the fresh OperationExecutor.

{
[Theory]
[ParameterAttributeData]
public async Task StartImplicitSession_should_call_cluster([Values(true, false)]bool isAsync)
Copy link
Contributor

Choose a reason for hiding this comment

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

minor: _cluster_StartSession

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


namespace MongoDB.Driver
{
internal record class WriteOperationOptions();
Copy link
Contributor

Choose a reason for hiding this comment

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

remove class here too

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

Oleksandr Poliakov added 4 commits May 27, 2025 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants