critical
#29823
HistoryCompaction: Löschen von REVISION/REVISION_XREF und Umschreiben der Revisionsbereiche in je einer Transaktion lässt die Datenbank explodieren
Problem
Kompaktierung (#27490) auf dem Release-Notes-Produktivsystem (H2, Tabelle REVISION mit ca. 35 Mio. Zeilen): Der Lauf endet erfolgreich, danach ist die Datei *.mv.db doppelt so groß wie vorher und wächst anschließend weiter um ca. 2 GB pro Minute, bis der Plattenplatz erschöpft ist.
Ursache
Nur das Löschen der Zeilen der Item-Tabellen läuft in Fenstern über REV_MAX mit einem Commit je Fenster (DEFAULT_DELETE_WINDOW_SIZE). Die übrigen Schritte von HistoryCompaction sind je ein einzelnes Statement über den gesamten Revisionsbereich in einer Transaktion:
- compactRevisionTable(): DELETE FROM REVISION WHERE rev > lower AND rev < upper – alle Revisionen auf einmal.
- rebuildXref(): DELETE der XRef-Einträge unterhalb der Kompaktierungsrevision, danach INSERT ... SELECT DISTINCT je Item-Tabelle, alles mit einem Commit.
- Umschreiben von REV_MIN, REV_MAX und REV_CREATE je Item-Tabelle und in FLEX_DATA (_raiseRevMin, _lowerRevMax, _raiseRevCreate): je ein UPDATE über alle betroffenen Zeilen.
H2s MVStore schreibt jede geänderte Seite als neuen Chunk an das Dateiende und gibt alte Chunks erst frei, wenn sie nicht mehr referenziert sind. Transaktionen dieser Größe schreiben praktisch jede Seite der betroffenen Tabellen neu und halten die alten Stände bis zum Commit (Verdopplung). Danach sind fast alle Chunks dünn belegt, die Hintergrund-Kompaktierung kopiert die lebenden Daten in neue Chunks, schneller als alte freigegeben werden (fortgesetztes Wachstum). Auf anderen Datenbanken führen Transaktionen dieser Größe entsprechend zu Undo-/WAL-Wachstum und langen Commits (vgl. #28053).
Verbesserung
- Alle großen Schritte der Kompaktierung laufen in Fenstern mit einem Commit je Fenster, wie bereits das Löschen der Item-Zeilen: Löschen in REVISION und REVISION_XREF über Revisionsfenster, Umschreiben von REV_MIN/`REV_MAX`/`REV_CREATE` je Item-Tabelle und FLEX_DATA über Fenster der umzuschreibenden Revisionen.
- Jeder Schritt bleibt idempotent, so dass ein abgebrochener Lauf wiederholt werden kann.
Lösung
HistoryCompaction führt alle großen Schritte über einen gemeinsamen Mechanismus für gefensterte Statements aus (WindowedStatement): Ein Statement, dessen letzte beiden Parameter die Fenstergrenzen sind, wird je Fenster der konfigurierten Größe (DEFAULT_WINDOW_SIZE, 5000 Revisionen, setWindowSize) ausgeführt und nach jedem Fenster committiert. Jeder Schritt fenstert über die Spalte, die er umschreibt bzw. nach der er löscht; eine umgeschriebene Zeile (Wert upper bzw. upper - 1) liegt außerhalb jedes weiteren Fensters, so dass jeder Schritt idempotent bleibt und ein abgebrochener Lauf wiederholt werden kann.
Gefenstert laufen:
- das Löschen der Item-Zeilen (wie bisher),
- je Item-Tabelle und FLEX_DATA das Anheben von REV_MIN, das Absenken von REV_MAX und das Anheben von REV_CREATE,
- das Anheben der Referenz-Pins (_REV-Spalten historischer und gemischter Referenzen),
- das Löschen in REVISION und REVISION_XREF.
Einzelne Statements bleiben für die Tabelle BRANCH (eine Zeile je Branch), das Umschreiben von Autor und Log der Kompaktierungsrevision und den Neuaufbau der XRef-Einträge der Kompaktierungsrevision (DELETE ... WHERE rev = upper und ein INSERT ... SELECT DISTINCT je Item-Tabelle, das nur wenige Zeilen schreibt).
Nebenbei korrigiert: Das Absenken von REV_MAX erfasste auch Zeilen mit REV_MAX = upper - 1, die bereits ihren Endzustand haben; ein wiederholter Lauf der Bereichsform und der Trockenlauf zählten diese Zeilen als umgeschrieben. Das Fenster endet jetzt bei upper - 1, der Trockenlauf zählt entsprechend.
Abgedeckt durch TestHistoryCompaction: Mit Fenstergröße 2 entsprechen Revisionen, XRef-Einträge und umgeschriebene Zeilen dem Ergebnis eines einzelnen Fensters, der Trockenlauf zählt dasselbe wie der echte Lauf; nach einer Kompaktierung der Bereichsform sind Trockenlauf und zweiter Lauf leer.
Grenzen
- Das Wachstum der 35-Mio.-Zeilen-Datenbank wurde lokal nicht nachgestellt; die Korrektur ist strukturell abgesichert (jeder große Schritt committiert je Fenster), nicht durch Messen der Dateigröße.
- Lokal wurde nur H2 getestet.
- Ob und wie eine Datenbankdatei nach der Kompaktierung kleiner wird, hängt von der Datenbank ab und wird nicht dokumentiert.