MongoDB Replicaset support#16698
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 16698Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 16698" |
Minor cleanup
66c95f0 to
67db131
Compare
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds single-node replica set support for hosted MongoDB resources so transaction-dependent scenarios can run against the Aspire MongoDB container, and introduces tests for the new API and connection string behavior.
Changes:
- Add
WithReplicaSet(...)to configure MongoDB as a single-node replica set and exposeReplicaSetName. - Replace the default MongoDB health check with a custom health check that initializes the replica set and waits for primary election.
- Add functional and unit tests covering the new API, container args, and connection string updates.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Aspire.Hosting.MongoDB.Tests/MongoDbFunctionalTests.cs | Adds a functional test that verifies transactions succeed against a replica-set-enabled MongoDB resource. |
| tests/Aspire.Hosting.MongoDB.Tests/MongoDBPublicApiTests.cs | Adds public API validation tests for WithReplicaSet and ReplicaSetName. |
| tests/Aspire.Hosting.MongoDB.Tests/AddMongoDBTests.cs | Adds unit tests for replica set defaults, args, annotations, and connection string behavior. |
| src/Aspire.Hosting.MongoDB/api/Aspire.Hosting.MongoDB.cs | Updates the shipped public API surface with WithReplicaSet and ReplicaSetName. |
| src/Aspire.Hosting.MongoDB/MongoDBServerResource.cs | Extends the resource model and connection string generation for replica set mode. |
| src/Aspire.Hosting.MongoDB/MongoDBServerHealthCheck.cs | Introduces a custom health check that initializes the replica set and checks primary readiness. |
| src/Aspire.Hosting.MongoDB/MongoDBBuilderExtensions.cs | Registers the new health check and implements WithReplicaSet. |
…ead safe way Disposing of the mongo cursor in the health check
…tensions.CreateGeneratedParameter
| new BsonDocument | ||
| { | ||
| ["_id"] = 0, | ||
| ["host"] = $"{resource.Name}:{targetPort}" |
There was a problem hiding this comment.
While it works for the simplest scenarios, the issue is that depending on which service is accessing the resource, it requires a different host name:
- If the replica set is accessed from the Aspire container network,
$"{resource.Name}:{targetPort}"should be fine, though more correct name would be$"{resource.Name}.dev.internal:{targetPort}". Ideally, the actual URL should be retrieved from the server'sConnectionStringExpressionwith theDefaultAspireContainerNetworkcontext. - If the replica set is accessed from the services running locally, not in a container, they should be resolved as
$"localhost:{targetPort}". Or, more precisely, from the server'sConnectionStringExpressionwith theLocalhostNetworkcontext. It's possible to add manualhostsmapping, of cause, but it does not seem to be a good solution.
I guess, if Dev Tunnels are being used, the things might get even more complicated, but I have not tried it myself.
As far as I know, the only reliable solution for that is using MongoDB Horizons feature, but it makes things more complicated as well.
There was a problem hiding this comment.
Nah, none of this should be a problem. The host name here is only for the internal replication engine initialisation.
For connections to the mongo database from the application, the regular connection string is used, with the addition of the directConnection=true param.
| /// <param name="replicaSetName">The name of the replica set. Defaults to <c>rs0</c>.</param> | ||
| /// <returns>The <see cref="IResourceBuilder{T}"/>.</returns> | ||
| [AspireExport(Description = "Configures the MongoDB container to start as a single-node replica set")] | ||
| public static IResourceBuilder<MongoDBServerResource> WithReplicaSet(this IResourceBuilder<MongoDBServerResource> builder, string replicaSetName = "rs0") |
There was a problem hiding this comment.
My concern is that this design does not allow to extend this feature beyond single-node replica sets.
In my opinion, having a replica set as a separate resource is a better option.
There was a problem hiding this comment.
Is this needed though, for a local development database? This shouldn't be used to deploy a resilient production level database, that would require an entirely different ballgame
| var targetPort = resource.PrimaryEndpoint.TargetPort ?? 27017; | ||
| var initCmd = new BsonDocument | ||
| { | ||
| ["replSetInitiate"] = new BsonDocument |
There was a problem hiding this comment.
It feels a bit like an abuse of the healthcheck. This logic can live in a separate event handler.
There was a problem hiding this comment.
You might say that, but it's the cleaner way to do this, considering the alternatives, while keeping the design simple and unobtrusive. Happy to discuss further, but I've explained the design decisions for this in the PR body above
Description
This PR modifies the MongoDB Builder to add support for building a single node replica set. This will allow applications using transactions or change streams to use MongoDB in Aspire.
I'm using the same approach as I used when building the Testcontainers.MongoDb library. Instead of building a separate container in a dockerfile, the standard mongo container is used. To start as a replicaset, a keyfile is generated and added to to the container. The healthcheck is used to ensure the replicaset is initialised on server startup. This keeps the implementation clean and doesn't require an additional dockerfile.
WithReplicaSet()Extension Method on the builderreplSetInitiatecommand runsdirectConnection=trueparameter when replica set is enabledFixes #5238
Decision points
New health check
The new health check replaces the simple original health check and will only return healthy once the replica set is initialised. It is also used to call the
rs.initiate()call in Mongo to initialise the replicaset.Alternatives considered:
OnResourceReadycallback fires after the health check passes - this would mark the resource as healthy before the replica set is readydocker-entrypoint-initdb.d- tried in the past, this won't work as that script runs against a standalonemongodinstance, and won't affect the main instanceDirect connection in connection string
Specifying the replicaset name in the connection string is explicitly discouraged by the MongoDB driver configuration, and can cause behavioural conflicts. Specifying
directConnection=truein the connection string tells the MongoDB driver to connect directly to the specified host bypassing topology discovery. Since we only have a single node, this is more than enough. Additionally, the topology discovery would return the internal host address within the container, which wouldn't be accessible by the application.Note
There may be an issue in Mongo 8.0 - 8.2 that causes Mongo to crash every ~30 seconds if replica set is initiated. Seems to be a compatibility issue with certain linux kernels. Switching to older Mongo versions (7.0) seems to completely bypass the issue. Not related to this PR or Aspire, purely a MongoDB regression.
ref:
bluewave-labs/Checkmate#3444
https://www.mongodb.com/community/forums/t/mongodb-8-x-and-linux-kernel-6-19/337547
stoatchat/self-hosted#268
Checklist
<remarks />and<code />elements on your triple slash comments?aspire.devissue: