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

Enable replicaSet support for MongoDb #5712

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

Conversation

twsouthwick
Copy link
Member

@twsouthwick twsouthwick commented Sep 14, 2024

Description

This change adds support for replica sets in MongoDb. In order to support this a couple changes are required:

  • Connection strings have query parameters as options that define the replica set. To support this, the expression builder is now called manually to build it up, as well as changing the database connection string to no long reference the server connection string directly (as the server connection string may have the replicaSet option)
  • The target port and the exposed port must be the same for replica sets to work. This adds a check that updates the MongoDb server to not be proxied and have the same port. Ideally, if no port is set, we should use a random one but I couldn't figure out how to do that in an idiomatic way.

Fixes #5238

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No
  • Does the change require an update in our Aspire docs?
    • Yes
      • Link to aspire-docs issue:
    • No
Microsoft Reviewers: Open in CodeFlow

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication label Sep 14, 2024
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Sep 14, 2024
@twsouthwick twsouthwick marked this pull request as draft September 14, 2024 03:56
@davidfowl davidfowl added mongodb Issues related to mongodb integrations area-integrations Issues pertaining to Aspire Integrations packages and removed area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication labels Sep 14, 2024
{
if (builder.Resource.TryGetLastAnnotation<MongoDbReplicaSetAnnotation>(out _))
{
throw new InvalidOperationException("A replica set has already been added to the MongoDB server resource.");
Copy link
Member

@davidfowl davidfowl Sep 16, 2024

Choose a reason for hiding this comment

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

Why throw instead of noop? Because of the name?

Copy link
Member Author

@twsouthwick twsouthwick Sep 16, 2024

Choose a reason for hiding this comment

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

yeah. I can check if there's already one with the same name and return in those cases if you want

var dir = Path.Combine(Path.GetTempPath(), "aspire.mongo", Path.GetRandomFileName());
Directory.CreateDirectory(dir);

var rsInitContents = $$"""rs.initiate({ _id:'{{replicaSet}}', members:[{_id:0,host:'localhost:{{port}}'}]})""";
Copy link
Member Author

Choose a reason for hiding this comment

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

@davidfowl I'd prefer to be able to just docker exec into the container after it started, but couldn't figure out how to do that so I'm running a container and configuring things to ensure it completes before anything else needs to use the db. If there is a better way to do this with aspire things, let me know

Copy link
Member

Choose a reason for hiding this comment

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

We're adding support for this in Aspire 9 very soon. Might be better to wait until that support is available in the app model.

Copy link
Member Author

Choose a reason for hiding this comment

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

cool - is there a tracking issue for that?

Copy link
Member Author

Choose a reason for hiding this comment

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

@davidfowl any progress on being able to exec into the container?

// See the conversation about setting up replica sets in Docker here: https://github.com/docker-library/mongo/issues/246
static string GetReplicaSetInitDockerfileDir(string replicaSet, string host, int port)
{
var dir = Directory.CreateTempSubdirectory("aspire.mongo").FullName;
Copy link
Member

Choose a reason for hiding this comment

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

A couple notes here:

  1. Who cleans up this directory?
  2. On Unix, this is going to create a directory that only the current user can access. If the container is running on non-root, the container won't be able to access this folder.

Copy link
Member Author

Choose a reason for hiding this comment

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

is there a pattern that already exists to handle creating/cleaning up directories? Maybe another reason as @davidfowl mentioned above to maybe wait to exec into a container

Copy link
Member

Choose a reason for hiding this comment

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

So after some thought, I think a pattern we can use here is:

  • Put the docker file in the nuget package
  • Mark it as content so it gets copied to the output (we'lll want to name it so it doesn't conflict)
  • Use build arguments to parameterize the docker file and pass that state in

Copy link
Member

Choose a reason for hiding this comment

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

With #7136, we have now introduced a new "IAspireStore" API:

public interface IAspireStore
{
/// <summary>
/// Gets the base path of this store.
/// </summary>
string BasePath { get; }

This file could be written there and my above concerns would be resolved.

@twsouthwick
Copy link
Member Author

/azp run

Copy link

Commenter does not have sufficient privileges for PR 5712 in repo dotnet/aspire

@twsouthwick twsouthwick marked this pull request as ready for review September 18, 2024 19:42
@@ -94,7 +95,8 @@ public static IResourceBuilder<T> WithMongoExpress<T>(this IResourceBuilder<T> b
.WithImageRegistry(MongoDBContainerImageTags.MongoExpressRegistry)
.WithEnvironment(context => ConfigureMongoExpressContainer(context, builder.Resource))
.WithHttpEndpoint(targetPort: 8081, name: "http")
.ExcludeFromManifest();
.ExcludeFromManifest()
.WaitFor(builder);
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure about using WaitFor invisibly like this. I can see the benefit but if one of the mongo db instances files to start then you won't be able to inspect any of the other databases.

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 added that because once you have a replica set, the existing connection string may cause express to fail if it tries to connect before the replica set is initialized (a race condition) and it doesn't recover from it.

@creyke
Copy link

creyke commented Oct 28, 2024

+1 for this. Missing replica sets doesn't just block transactions, it also blocks change streams.

These are two very commonly utilised features.

This looks close to done, but is not active... is this likely to get merged @twsouthwick?

Thanks in advance.

@twsouthwick
Copy link
Member Author

@creyke I'd like to get this in, but in order to set up the replica set, we must run some commands in the container. sounds like docker exec support will be available soon, so this is waiting on that. Once that is available, I'll update it so this can be merged.

@feritzcan2
Copy link

@creyke I'd like to get this in, but in order to set up the replica set, we must run some commands in the container. sounds like docker exec support will be available soon, so this is waiting on that. Once that is available, I'll update it so this can be merged.

Still no luck? Thats highly blocker for integration testing /(

@alexjamesbrown
Copy link

@twsouthwick was there anything left to do on this? Currently using the workaround on this issue
#6811 - but definitely feels like this support should be in Aspire core.

private static void ConfigureMongoExpressContainer(EnvironmentCallbackContext context, MongoDBServerResource resource)
{
// Mongo Exporess assumes Mongo is being accessed over a default Aspire container network and hardcodes the resource address
var sb = new StringBuilder($"mongodb://{resource.Name}:{resource.PrimaryEndpoint.TargetPort}/?directConnection=true");
Copy link
Member

Choose a reason for hiding this comment

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

ReferenceExpressionBuilder

@danmoseley
Copy link
Member

@twsouthwick is this still in progress?

@danmoseley danmoseley added the needs-author-action An issue or pull request that requires more info or actions from the author. label Feb 2, 2025
@davidfowl
Copy link
Member

@twsouthwick @eerhardt and I have an offline thread about this one.

@davidfowl davidfowl removed the needs-author-action An issue or pull request that requires more info or actions from the author. label Feb 17, 2025
@alexjamesbrown
Copy link

Just commenting again as not to close this issue

@davidfowl / @eerhardt was there any update on this at all?

/// .WaitFor(messaging);
/// </code>
/// </example>
public static IResourceBuilder<T> WaitFor<T>(this IResourceBuilder<T> builder, IResourceBuilder<IResource> dependency, bool includeHealthChecks) where T : IResource
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if bool includeHealthChecks should instead be a WaitType. We currently have WaitUntilHealthy and WaitForCompletion. But could add WaitUntilStarted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-integrations Issues pertaining to Aspire Integrations packages community-contribution Indicates that the PR has been added by a community member mongodb Issues related to mongodb integrations
Projects
None yet
Development

Successfully merging this pull request may close these issues.

MongoDB Transactions requires replicas
8 participants