Skip to content

Reuse an embedded MongoDB only when it is running the backend being asked for - #16216

Open
codeconsole wants to merge 1 commit into
apache:8.0.xfrom
codeconsole:fix/embedded-mongo-backend-reuse-8.0.x
Open

Reuse an embedded MongoDB only when it is running the backend being asked for#16216
codeconsole wants to merge 1 commit into
apache:8.0.xfrom
codeconsole:fix/embedded-mongo-backend-reuse-8.0.x

Conversation

@codeconsole

Copy link
Copy Markdown
Contributor

Follow-up to #16192.

Problem

EmbeddedMongoInitializer hands a restart the server this JVM already started when the settings match the ones it was started with:

StartedServer started = discard(STARTED.get(port), settings);

EmbeddedMongoSettings.equals compares port, version, databaseDir and replicaSet. The backend is not among them, and StartedServer does not carry it either, so nothing in the reuse path knows which backend is running.

Switching embedded.mongodb.backend between in-memory and flapdoodle across a devtools restart, changing nothing else, therefore keeps the old server. An application that asked for a real mongod carries on against the in-memory reimplementation, which implements neither transactions, change streams nor $text — so the failure surfaces later and somewhere else, as a missing feature rather than a backend that was never started.

Worth noting the shape of the omission: the check does compare version, which InMemoryMongoBackend ignores entirely. The one setting that decides what the server actually is was the one left out.

Fix

Keep the backend the server was started with alongside its settings, and compare it:

private record StartedServer(RunningEmbeddedMongo running, EmbeddedMongoSettings settings, String backend) { }

if (started == null || (started.settings().equals(settings) && started.backend().equals(backend))) {
    return started;
}

The backend is resolved in initialize before the reuse question rather than inside start, since it is part of that question, and passed to start so it is selected once.

EmbeddedMongoSettings is public API and its constructor is unchanged; the backend lives on the private StartedServer record instead.

One behaviour change worth calling out: asking for a backend whose library is absent now fails even when a server is already running on that port, because selectBackend runs before the reuse check. Previously such a restart was silently handed the running server. The new behaviour matches what the same configuration does when nothing is running.

Tests

A restart that switches backend while holding version, database and replica set unchanged now gets a new server. The assertion is behavioural — a document written to the first server, then counted after the switch — because object identity does not distinguish the two cases here: initialize constructs a fresh EmbeddedMongoLifecycle on every call, reused server or not.

That last point applies to the existing test beside it. a restart that asks for a different server is not handed the one already running was vacuous: I removed version from EmbeddedMongoSettings.equals and it still passed, so it was not verifying replacement at all. It is rewritten here with the same behavioural assertion, and now fails when version is removed from the comparison.

Both tests fail on unmodified 8.0.x and pass with the fix; :grails-data-mongodb-embedded:test is green.

A restart is handed the server this JVM already started when the settings it
asks for match the ones it was started with. The backend was not among them, so
switching from the in-memory reimplementation to flapdoodle -- or back -- while
changing nothing else quietly kept the old server. The application asked for a
real mongod and carried on against the in-memory one, which does not implement
transactions, change streams or $text, so the failure arrives later and
somewhere else.

The check already compares the version, which the in-memory backend ignores
entirely, so the omission was of the one setting that decides what the server
actually is.

The backend is resolved before the reuse question rather than inside start(),
since it is part of that question, and the name it was started with is kept
alongside the settings.

A restart that asks for a backend whose library is absent now says so even when
a server is already running, instead of being handed that server. That is the
same answer it would have given had nothing been running.
@codecov

codecov Bot commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 54.1139%. Comparing base (e40cb27) to head (1e74b1a).

Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##                8.0.x     #16216        +/-   ##
==================================================
- Coverage     54.1238%   54.1139%   -0.0099%     
+ Complexity      20307      20301         -6     
==================================================
  Files            2107       2107                
  Lines          101144     101144                
  Branches        17921      17921                
==================================================
- Hits            54743      54733        -10     
- Misses          38595      38603         +8     
- Partials         7806       7808         +2     
Files with missing lines Coverage Δ
...orm/mongodb/embedded/EmbeddedMongoInitializer.java 95.9016% <100.0000%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@testlens-app

testlens-app Bot commented Aug 25, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: 1e74b1a
▶️ Tests: 69325 executed
⚪️ Checks: 83/83 completed


Learn more about TestLens at testlens.app/docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant