v3.0.0
The audit release: a whole-codebase adversarial review, a second review of the fixes themselves, and roughly 80 new differential scenarios pinning every behaviour against real MongoDB 8.0.
Highlights
- 01
Silent divergences, found and closed
A dotted
Read the guide →$setthrough an array destroyed data.{$set:{"a.0.b":1}}on{a:[5]}silently replaced the element where the server refuses with PathNotViable, and the aggregation twin collapsed an array of subdocuments into one document.$allwith$elemMatchmembers matched nothing.$projectdropped computed dotted fields when the parent was absent.distinctreturned three values where the server returns one. Every one of these answered differently from MongoDB rather than failing loudly. That is the worst failure mode a test double has, and the one this release set out to eliminate. Every claim was checked against a live mongo:8.0 before any code changed. Four review findings turned out to be wrong: regexireally does fold Unicode case, a matchedreplaceOnealways reports modifiedCount 1,$pushmodifiers without$eachare stored literally, and the server kills a transaction at the failing write. Each is now pinned by a scenario, so the true behaviour cannot be "fixed" into a regression later. - 02
One ordering rule instead of four
Cross-type numeric comparison had drifted into four private copies, two of them lossy: the value engine rounded doubles to their shortest decimal rendering, so
Read the guide →Decimal128("0.1")compared equal to double0.1where the server orders them apart. The update engine kept its own copy plus UTF-16 string ordering, which meantfind({v: NumberDecimal("0.1")})and$pull:{v: NumberDecimal("0.1")}gave opposite answers for the same stored value. Sort,_idequality,$min/$max,$pull,$addToSetand array filters now share one exact rule, and query-style comparisons are type-bracketed as the server's are. - 03
Your catch block sees the server's exception
Read the guide →insertManyraisedMongoWriteExceptionwhere the driver raisesMongoBulkWriteException: the code and category were the same, but catchability differed, and the mismatch was invisible to a differential lane that only compared error codes. The lane now compares the exception class too, which immediately surfaced ten more scenarios: query errors that should beMongoQueryException, an index build that leaked the storage engine's own exception, malformed update operands arriving asIllegalArgumentExceptioninstead of a translated write error. - 04
Two configurations now refused instead of silently corrupting
A class-level
Read the guide →@Execution(CONCURRENT)ran a class's methods concurrently against one shared context while each method's cleanup dropped the others' databases. A Spring context defining reactive Mongo beans connected to the real server, because the emulator rewires only the synchronous ones. Both now fail fast with an actionable message. Both are breaking for suites that are green today. See the migration notes in the changelog.
Full notes
Breaking changes
insertManynow raises the driver'sMongoBulkWriteExceptionon a duplicate key, where the shim previously raisedMongoWriteException. The two are siblings, not subtypes, so an existingcatch (MongoWriteException e)around a batch insert stops catching. Change it toMongoBulkWriteException, or catchMongoExceptionfor both. Single-document writes are unchanged. Several other operations likewise moved to the exception class a real server produces (MongoQueryExceptionfor query-shaped errors,DuplicateKeyExceptionfor a failed unique-index build, translated write errors for malformed update operands).- A class-level
@Execution(CONCURRENT)on an emulator test class is refused when parallel execution is enabled. It ran the class's own methods concurrently against one context while each method's cleanup dropped the others' data. Ask for parallel classes withjunit.jupiter.execution.parallel.mode.classes.default=concurrentinstead. - An embedded Spring test context that defines a reactive MongoDB bean is refused, because those beans are outside the emulator's reach and connect to the configured server for real. Exclude the reactive auto-configuration from the slice, or set
bonsai.reactive.allow-unmanaged=trueto boot anyway. - Nothing a consumer imports was renamed. Coordinates, packages, annotations and properties are unchanged from 2.x, so this is not a 2.0-style migration. The major reflects corrections that are visible to code which already compiles and passes; every one of them moves the library towards real MongoDB, so a suite whose assertions were already true against a real server should upgrade as a no-op.
Changed
- The differential lane's error signatures now include the exception class, so a future divergence in exception type, not just code or category, fails the build. Three documented expected divergences gained compatibility-matrix rows:
codec.uuid-unspecified,transaction.uniqueIndexTimingandcrud.replaceNaturalOrder.
Fixed
- Silent divergences: dotted
$set/$incthrough arrays (data loss),$projectcomputed dotted parents,$allmember semantics, exact cross-type numeric ordering unified across the whole codebase, UTF-8/code-point string ordering, type-bracketed query comparisons,distinctcross-type deduplication,replaceOnemodifiedCount,$typealias validation, non-finite arithmetic, mixed-type_idordering with a consistent hashCode, a phantom index left behind by a failed unique-index build, andinsertManygenerated-id write-back. - Exception families:
MongoBulkWriteExceptionforinsertMany,MongoQueryExceptionfor query-shaped errors,DuplicateKeyExceptionfor failed index builds, translated write errors for malformed update operands, a real E11000 for in-transaction unique-index violations, and the driver's own rejection of empty batches. - Harness correctness: the
@Nesteddeadlock refusal runs before any shared state is touched, the parallel-methods refusal honours an explicit@Execution(SAME_THREAD)and names the declaring class, the SEC-1 non-local-client warning works again after a Spring Data accessor rename had silenced it, plus session close/cleanup races, drop-versus-commit lock discipline, per-fork durability files and shutdown-hook lifecycle. - Includes the 2.0.2 fix: an unterminated
${placeholder in a seed fixture is rejected rather than seeded as literal text.