Skip to content
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

CSHARP-3985: Support multiple SerializerRegistries #1592

Draft
wants to merge 43 commits into
base: main
Choose a base branch
from

Conversation

papafe
Copy link
Contributor

@papafe papafe commented Jan 13, 2025

/// <summary>
/// //TODO
/// </summary>
public interface IBsonSerializationDomain
Copy link
Member

@sanych-sun sanych-sun Jan 14, 2025

Choose a reason for hiding this comment

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

Why do we need such super-interface? It has everything in it: configuration, resolving and even reading/writing. Is there any use case when one need all of this together? I would prefer to use 3 separate interfaces: IBsonSerializationBuilder (or similar, to contain everything related to configuration, registration) - when one done with configuration - can call Build method and get IBsonSerializerRegistry (+ we might want to make IBsonSerializerRegistry as readonly for lookup only) and finally there is IBsonSerializer (to contain Serialize-Deserialize methods). So we will separate all use-cases: for application bootstrap/configuration - one have to use IBsonSerializationBuilder, in run-time to resolve the serializer - IBsonSerializerRegistry should be used, and finally to read/write - we have IBsonSerializer itself.

Copy link
Member

Choose a reason for hiding this comment

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

BTW, having IBsonSerializationBuilder is very well aligned with idea of having MongoClientBuilder to create MongoClients. Serialization builder could be part of the bigger MongoClientBuilder.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think the main problem here is that we're trying to move away from the static super-class that is BsonSerializer without creating a breaking change. At the moment IBsonSerialisationDomain is an interface with everything that BsonSerialiser does, so that we can create an instance class that we can inject where we need. At a later time (possibly we can break this ticket in multiple PRs) this would allow developers to define custom serialization configuration for a certain mongo client/database/collection.
And I agree that this interface does a lot, but so did BsonSerializer and unfortunately it's difficult to separate everything from each other without breaking what's already there, given that almost everything is in the public API and we can't change the behaviour before a new major. For example, one of the issue we have is that the code to work with discriminators is not part of IBsonSerializerRegistry, but it's all over the place.
I feel that moving away from a static class would already be a positive change for the future, and that changing the way we do serialisation is definitely something we should do, but that would require changing the public API and a new major version.
What do you think?
Also @rstam tagging you here so you can follow the conversation 😄

Copy link
Member

Choose a reason for hiding this comment

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

I do understand that we are limited by not allowing to have a breaking changes, but if we decide to introduce a new interface(s), let's make them useful for initiatives that will follow soon. Let's discuss pros and cons of each idea with whole team.

@rstam rstam self-requested a review January 14, 2025 17:11
src/MongoDB.Bson/Serialization/BsonSerializationDomain.cs Outdated Show resolved Hide resolved
/// <summary>
/// //TODO
/// </summary>
IBsonCoreSerializerConfiguration SerializationConfiguration { get; }
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this property doesn't belong in this interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My idea would be that the IBsonCoreSerializer interface would contain everything that can be used to do the serialization and this also includes the configuration necessary for that.

src/MongoDB.Bson/Serialization/IBsonSerializationDomain.cs Outdated Show resolved Hide resolved
src/MongoDB.Bson/Serialization/IBsonSerializationDomain.cs Outdated Show resolved Hide resolved
src/MongoDB.Bson/Serialization/IBsonSerializationDomain.cs Outdated Show resolved Hide resolved
src/MongoDB.Bson/Serialization/IBsonSerializationDomain.cs Outdated Show resolved Hide resolved
src/MongoDB.Bson/Serialization/IBsonSerializationDomain.cs Outdated Show resolved Hide resolved
src/MongoDB.Bson/Serialization/IBsonSerializationDomain.cs Outdated Show resolved Hide resolved
@papafe
Copy link
Contributor Author

papafe commented Jan 30, 2025

This is still a super quick and dirty proof of concept, just to verify we can actually create a custom domain and move it along down the call stack. Of course this broke down several things, and works only in certain cases, but it's a starting point.
The main idea here is:

  • The serialization domain (IBsonSerializationDomain) can be setup at the client/database/collection level
  • The serialization domain is also added to the serialization context (BsonSerializationContext/BsonDeserializationContext). This way it is passed down wherever it is needed
  • In order to pass the serialization domain between the collection level and the BsonSerializationContext, the serialization domain is passed down in the MessageEncoderSettings,
  • From MessageEncoderSettings, the domain is then added to the BsonBinaryWriterSettings in the MessageBinaryEncoderBase.
  • Finally, the writer settings are read in the serialization context constructor.

(This works similar for `BsonSerializationContext)

I'm not trying to say that this is the perfect way, but I just tried to come up with the "most direct" way to go from the collection to the serialization contexts, so it's mostly a proof of concept.

Also, I still did not touch the conventions, the discriminators and the class maps, that are also statics and need to be part of the serialization domain.

@papafe papafe requested review from rstam and sanych-sun January 31, 2025 11:14
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.

I didn't review every single file. I tried to pick the most relevant ones.

Overall this looks like the right direction.

But... it's going to be a pain to finish and to review.

/// <summary>
/// //TODO
/// </summary>
public IBsonSerializationDomain SerializationDomain
Copy link
Contributor

Choose a reason for hiding this comment

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

I can't find anywhere this property is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's used in the constructor of the BsonDeserializationContext.

/// <summary>
/// //TODO
/// </summary>
public IBsonSerializationDomain SerializationDomain
Copy link
Contributor

Choose a reason for hiding this comment

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

I can't find anywhere this property is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's used in the constructor of the BsonSerializationContext

@@ -62,7 +62,7 @@ public interface IBsonReader : IDisposable
/// <summary>
/// Pops the settings.
/// </summary>
void PopSettings();
void PopSettings(); //TODO Why do we have push and pop methods? They are not used
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm surprised they are not used.

The matching methods in IBsonWriter are used.

public void Apply(BsonMemberMap memberMap)
public void Apply(BsonMemberMap memberMap) => Apply(memberMap, BsonSerializer.DefaultDomain);

public void Apply(BsonMemberMap memberMap, IBsonSerializationDomain domain)
Copy link
Contributor

Choose a reason for hiding this comment

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

new domain parameter is not used?

public void PostProcess(BsonClassMap classMap)
public void PostProcess(BsonClassMap classMap) => PostProcess(classMap, BsonSerializer.DefaultDomain);

public void PostProcess(BsonClassMap classMap, IBsonSerializationDomain domain)
Copy link
Contributor

Choose a reason for hiding this comment

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

new domain parameter is not used?

{
BsonSerializer.ConfigLock.EnterReadLock();
var configLock = (context.SerializationDomain as IBsonSerializationDomainInternal)!.ConfigLock; //TODO This is ugly
Copy link
Contributor

Choose a reason for hiding this comment

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

You could create a FreezeContext method to do the ugly part:

var configLock = context.GetConfigLock();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We could! To be honest, I'm not sure how we want to deal with Freeze. It's a very special method, as it calls static methods, and uses static variables internally.
I tried to isolate them by creating the FreezeContext, but it's still not 100% correct and I'm not sure how we can completely separate it from the static part.

/// <summary>
/// //TODO
/// </summary>
public IBsonSerializationDomain Domain => _domain;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should probably standardize on Domain or SerializationDomain.

SerializationDomain is more accurate, but longer...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, at the moment it's a little bit here and there. I think serialization domain is a better name, even if longer.

/// <summary>
/// //TODO
/// </summary>
public interface IBsonSerializationDomain
Copy link
Contributor

Choose a reason for hiding this comment

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

IBsonSerializationDomain is a good name... but maybe you want to reserve it for 4.0?

If all we are doing is going from static to instance registries maybe a name like IBsonSerializationRegistries would work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To be honest I think we need to discuss how much of a change we are willing to do for this work. For example, we can't implement this PR without adding the serialization domain to some public interfaces, and this would constitute a binary breaking change.
Also I think that IBsonSerializationRegistries is a little confusing, since it's not only serialization registries.

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.

3 participants