Mastodon: PostgreSQL Datenbank von Version 11 auf 13 aktualisieren

Vor einer ganzen Weile habe ich meinen Mastodon-Container von Debian Buster auf Debian Bullseye umgestellt - ohne jedoch die PostgreSQL Datenbank von Version 11 auf Version 13 zu migrieren. Es gab wichtigere Themen auf meiner Liste, und eine Migration schien mir zunächst heikel zu sein - vor allem bei einer für meine Verhältnise relativ große Datenmenge von 80 GB. Die Stabilität meiner Mastodon-Instanz war wichtiger und ein Upgrade würde nur unnötig Zeit fressen und die Instanz einer gewissen Downtime aussetzen, die ich zunächst vermeiden wollte.

Durch die neu angekündigte Debian Version 12 “Bookworm” ist das Thema nun aber wieder präsent geworden, denn Debian Bookworm kommt mit PostgreSQL in version 15. Dafür will ich gewappnet sein und habe mich dazu entschlossen, dass das Upgrade nun endlich einmal vollzogen werden muss.

Pgtune Einstellungen und sonstige Konfigurationsanpassungen übernehmen

PostgreSQL 13 wurde durch das damalige Systemupgrade von Buster auf Bullseye bereits parallel zu PostgreSQL 11 installiert. Überraschenderweise wurden wohl auch meine Anpassungen in der postgresql.conf automatisch auf die neue Konfiguration von Version 13 in /etc/postgresql/13/main/postgresql.conf übertragen - oder habe ich es selbst erledigt und nur wieder vergessen? …

Upgrade durchführen - mit pg_upgradecluster

Um das Upgrade durchzuführen, gibt es drei Wege: Der einfachste (und üblicherweise während des OS-Upgrades vom System vorgeschlagene Weg) führt über das pg_upgradecluster Script. Nach einem einfachen

pg_dropcluster --stop 13 main
pg_upgradecluster 11 main

kümmert sich das Upgrade-Script um alles andere und man kann sich entspannt zurücklehnen.

… denn obwohl das Script für kleine Datenbanken hervorragend funktioniert (und ich es dafür auch jederzeit empfehlen würde!), benötigt es bei großen Datenbanken nicht nur viel Zeit, sondern auch viel Speicher: Im Hintergrund wird eine zweite, Postgres 13 kompatible Datenbank angelegt und sämtliche Daten dorthin kopiert. Das Speicherbedarf ist also am Ende der doppelte. Eigentlich geschieht nichts anderes, als sämtliche Daten aus der alten Version 11 zu dumpen und dann in die neue PostgreSQL 13 Datenbank zu importieren.

Nach 40 Minuten habe ich aufgegeben und den Vorgang auf meinem Mastodon-Testdeployment abgebrochen. Die Downtime war mir deutlich zu lang.

Zum Glück gibt es aber noch eine effizientere Methode: Das Upgrade mit --link und --method=upgrade. Hierbei wird die alte PostgreSQL 11 Datenbank nicht behalten, sondern “in-place” in eine PostgreSQL 13 Datenbank umgewandelt. Der Prozess kann nicht rückgängig gemacht oder die alte Datennbank wiederhergestellt werden - doch die Methode hat einen entscheidenden Vorteil: Dank der --link Option müssen Datensätze größtenteils nicht neu generiert oder importiert werden, sondern können einfach durch sog. Hardlinks im Dateisystem in die neue Version übernommen werden. Ein Kopiervorgang ist dann nicht nötig und wir sparen wertvolle Zeit!

pg_dropcluster --stop 13 main
pg_upgradecluster --method=upgrade --link 11 main

Für meine 80 GB Mastodon Datenbank hat der Vorgang 11 Sekunden (!) gedauert. Dann war das Upgrade so gut wie fertig - dazu gleich mehr. Nach dem Upgrade können die alten Daten aus Version 11 mittels pg_dropcluster 11 main gelöscht werden:

Success. Please check that the upgraded cluster works. If it does,
you can remove the old cluster with
	pg_dropcluster 11 main

Ver Cluster Port Status Owner    Data directory              Log file
11  main    5433 down   postgres /var/lib/postgresql/11/main /var/log/postgresql/postgresql-11-main.log
Ver Cluster Port Status Owner    Data directory              Log file
13  main    5432 online postgres /var/lib/postgresql/13/main /var/log/postgresql/postgresql-13-main.log

Ich war zuerst vorsichtig und habe mich vergewissert, dass die Daten nun in meiner PostgreSQL 13 Datenbank liegen:

su - postgres
psql mastodon_production
select * from accounts limit 5;
\q
exit

Die Daten waren da und ich konnte Reste der alten PostgreSQL 11 Daten löschen:

pg_dropcluster 11 main

Übrigens: Da das Dateisystem sog. “Inodes” (in denen die eigentlichen Daten für eine Datei liegen) erst löscht, wenn kein einziger Hardlink mehr auf diese verweist, muss man nicht befürchten, dass noch benötigte Dateien in /var/lib/postgresql/11/ liegen oder das pg_dropcluster 11 main unvorhergesehenen Schaden anrichten könnte.

Dritter Upgradepfad: Replication

Der komplizierteste (aber auch eleganteste) Weg für ein Upgrade besteht darin, eine zweite, noch leere PostgreSQL 13 Datenbank im Replikations-Modus (Active/Passive) an die alte Datenbank anzubinden, die Daten dann langsam im Hintergrund zu migrieren, bis beide Datenbanken denselben Versionsstand haben und schlißelich während einer minimalen Downtime den endgültigen Umstieg auf Version 13 durchzuführen.

Es gibt hierfür sogar ein Ruby-Script, das den Vorgang ein Stück weit automatisieren kann: https://github.com/shayonj/pg_easy_replicate

Aus Zeitmangel habe ich diese Methode allerdings nicht ausprobiert. Für noch größere Datenbanken ist sie aber sicherlich sinnvoll. Vielleicht teste ich sie ja beim nächsten Upgrade …

Unerwartet: Reindizierung nach dem Upgrade

Wie bereits erwähnt, ging die Umstellung via pg_upgradecluster mit --link Option in Sekundenschnelle und völlig problemlos. Allerdings hatte ich nicht bedacht, dass die Umstellung auf PostgreSQL 13 eine vollständige Neuindizierung der Datenbank zur Folge haben würde. Wie sich in den Release Notes nachvollziehen lässt, wurde der Indizierungsalgorithmus angepasst und die Performance verbessert. Um von diesen Verbesserungen zu profitieren, muss eine Reindizierung durchgeführt werden - und das hat das automatische Upgradescript pg_upgradecluster für mich offenbar ohne weiteren Hinweis erledigt. Praktisch, aber überraschend. Denn der Reindizierungsvorgang führte dazu, dass die Performance meiner Mastodon-Datenbank in den ersten 5 Minuten so schlecht war, dass metalhead.club wegen Datenbank-Timeouts nicht zu erreichen war. Langsam besserte sich die Lage und nach etwa 10 Minuten hatte die Datenbank ihre ursprüngliche Performance wieder.

Es wäre nützlich gewesen, das vorher zu wissen…. ;-)

Pgbouncer anpassen

Übrigens - wer pgbouncer in seinem Mastodon-Setup nutzt, muss unter Umständen noch die Portnummer für sein neues PostgreSQL 13 Cluster anpassen. Die aktuelle Portnummer lässt sich via

sudo pg_lscluster

ermitteln. In meinem Fall hat sich die Portnummer nicht geändert - denn das Upgradescript hat die Portnummer aus Version 11 einfach für Version 13 gesetzt. Praktisch! Andernfalls hätte der Port in /etc/pgbouncer/pgbouncer.ini anangepasst und pgbouncer neu gestartet werden müssen:

[databases]
mastodon_production = host=127.0.0.1 port=5432 dbname=mastodon_production user=mastodon password=blablabla