Morphium 6.2.1 — Bugfix-Release mit echtem Biss
Morphium 6.2.1 ist raus. Offiziell ein Bugfix-Release, aber wer in den Changelog schaut, merkt schnell: hier steckt richtig Arbeit drin. Von ByteBuddy-Migration über PoppyDB-Stabilität bis hin zu einem neuen Stream-API — das ist kein reines Patch-Release.
ByteBuddy statt spring-cglib
Das war leider schon länger überfällig und hat mich eine ganze Weile genervt: Die Abhängigkeit auf spring-cglib für Lazy-Loading-Proxies ist raus. Ersatz ist ByteBuddy 1.15.11 — damit fliegt spring-core komplett aus dem Dependency-Tree.
Was das konkret bedeutet:
- Kein
--add-opensmehr — der lästige JVM-Flag für Reflektionszugriff entfällt - Nativer Java 21 Support — ByteBuddy kommt damit ohne Workarounds aus
- Proxy Class Caching — verhindert Metaspace-Leaks bei wiederholter Proxy-Erzeugung
Für bestehende Projekte, die spring-core anderweitig nicht brauchen, schrumpft die Dependency-Liste spürbar.
PoppyDB: Stabilitäts-Overhaul
PoppyDB - unser neuer 100% pure Java In-Memory-MongoDB-kompatible Server, hat in dieser Version ordentlich Liebe bekommen. Die behobenen Probleme waren teilweise richtig fiese Edge Cases:
- BSON Size Limits — Dokumente über dem 16MB-Limit wurden falsch behandelt, was zu korrupten Responses führte
- Aggressive Idle Timeouts — Change Streams wurden getötet, weil PoppyDB Verbindungen zu früh als inaktiv eingestuft hat
- Stale Primary State nach Elections — nach einer Replica-Set-Election konnte PoppyDB im falschen Primary-State hängen bleiben
- Connection-killing Parse Errors — bestimmte BSON-Parse-Fehler haben die gesamte Verbindung abgeschossen statt nur den Request zu verwerfen
Wer PoppyDB für Tests nutzt, wird von diesen Fixes direkt profitieren — besonders bei Change Streams und länger laufenden Test-Suites.
Wire Protocol Hardening
Auch das Wire-Protokoll hat ein paar defensive Verbesserungen bekommen:
- EOF Handling — unerwartete Verbindungsabbrüche konnten bisher in Infinite Loops münden; das ist jetzt sauber abgefangen
- Geschlossene Connections — werden nicht mehr an den Connection Pool zurückgegeben und sorgen damit nicht mehr für Folgefehler
Das klingt banal, aber in Produktionsumgebungen mit flakiger Netzwerkinfrastruktur (und die gibt's öfter als man denkt) ist das der Unterschied zwischen "kurzer Hickup" und "Service hängt bis zum Restart".
Query.stream() — Lazy Cursor-backed Streams
Neu in diesem Release: Query.stream() gibt einen Stream<T> zurück, der lazy über einen MongoDB-Cursor arbeitet.
.field("status").eq("active")
.stream()
.filter(e -> e.getValue() > 100)
.forEach(e -> process(e));
Der wesentliche Unterschied zu asList(): Die Ergebnisse werden nicht alle auf einmal in den Heap geladen. Bei großen Collections ist das ein echter Gamechanger für den Speicherverbrauch. Der Cursor wird am Ende des Streams automatisch geschlossen.
Read-your-writes Consistency
Nach einem Transaction Commit werden Reads für ein konfigurierbares Zeitfenster auf PRIMARY geroutet. Das klingt nach einem Detail, vermeidet aber eine ganze Klasse von schwer zu debuggenden Race Conditions, bei denen man Daten schreibt und direkt danach "nicht findet", weil der Read noch auf einem Sekundär mit Replikations-Lag gelandet ist.
MongoField.not() — Breaking Change
MongoField.not() gibt jetzt MongoField<T> statt Query<T> zurück. Das war nötig, um die korrekte BSON-Struktur für alle Operatoren zu erzeugen — vorher war das Verhalten für einige Operator-Kombinationen falsch.
Was ändert sich: Wer .not() in einer Fluent-Chain verwendet und danach Query-spezifische Methoden aufruft, muss die Chain anpassen. In den meisten Fällen sind das nur minimale Refactoring-Aufwände.
Contributor Credits
Ein großes Dankeschön an Heiko Kopp (@Bardioc1977) für seine Beiträge zu diesem Release.
Links
<groupId>de.caluga</groupId>
<artifactId>morphium</artifactId>
<version>6.2.1</version>
</dependency>