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.
Versuch mit pg_upgradecluster –link und –method=upgrade
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