Mastodon: PostgreSQL collation version mismatch Warning beheben
Kürzlich habe ich meinen Mastodon-Container von Debian Bookworm auf Debian Trixie aktualisiert. Dabei wurde auch PostgreSQL von Version 15 auf Version 17 aktualisiert. Seitdem hatte ich bei der Ausführung von tootctl Kommandos immer einige dieser Warnungen auf der Konsole:
WARNING: database "mastodon_production" has a collation version mismatch
DETAIL: The database was created using collation version 2.36, but the operating system provides version 2.41.
HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE mastodon_production REFRESH COLLATION VERSION, or build PostgreSQL with the right library version.
In diesem Post geht es darum, wie ich das Problem behoben habe.
Der Fix besteht - wie es im “hint” schon steht, als zwei Teilen:
- Zuerst müssen die Indizes der Tabellen neu gebaut werden.
- Daraufhin wird die Collation der ganzen Datenbank erneuert.
Bei meiner Recherche bin ich über das reindexdb Kommandozeilentool gestolpert, über welches man eine Re-indizierung durchführen kann. Dabei sollte man darauf achten, dass genug freier Speicher verfügbar ist, denn PostgreSQL dupliziert alle Indizes bzw. schreibt diese separat neu. Erst, wenn der Prozess erfolgreich war, werden die neu generierten Indizes aktiv geschaltet und die alten entfernt. Es wird vorübergehend mehr Speicher benötigt - etwa eine Menge, die der aktuellen Größe aller Indizes entspicht. Zum Glück lässt sich das über PgHero ganz gut abschätzen. Das Tool findet sich unter https://<mastodoninstanz>/pghero/space.
Ich habe also
reindexdb --concurrently --dbname=mastodon_production --jobs=4
ausgeführt, um die Re-indizierung zu starten. Das concurrently flag bewirkt, dass Tabellen nicht lange gelockt bleiben und die Datenbank auch während dieses Prozesses normal benutzt werden kann. Das war mir wichtig, weil ich unbedingt eine Downtime meiner Mastodon-Instanz metalhead.club vermeiden wollte. Der Vorgang dauert darurch zwar etwas länger, aber das war in Ordnung. Über dbname wird der Name der Datenbank mitgegeben und über eine höhere Anzahl jobs lässt sich der Prozess beschleunigen.
Leider tauchte nach etwa einer Stunde die erste Fehlermeldung auf:
reindexdb: error: processing of database "mastodon_production" failed: ERROR: index row size 2712 exceeds btree version 4 maximum 2704 for index "index_preview_cards_on_url_ccnew"
DETAIL: Index row references tuple (598213,4) in relation "preview_cards".
HINT: Values larger than 1/3 of a buffer page cannot be indexed.
Consider a function index of an MD5 hash of the value, or use full text indexing.
Ein Eintrag in der Datenbank war offenbar zu groß, sodass keine Neuindizierung stattfinden konnte. Ich habe das Problem “gelöst”, indem ich den Datenbankeintrag mit der offenbar viel zu langen URL einfach gelöscht habe. Schaden ist dabei nicht entstanden, denn der Eintrag gehört zu den Preview Cards, die vom Server ohnehin nur gecached werden und eine kurze Lebensdauer haben. Zur Löschung habe ich die PostgreSQL Konsole genutzt:
su - postgres
psql mastodon_production
und dann:
DELETE FROM preview_cards WHERE ctid = '(598213,4)';
Danach habe ich die Konsole via \q verlassen, bin zurück zum Root-User gewechselt und habe das reindexdb Kommando erneut aufgerufen. Wieder gab es an einer Stelle einen Eintrag, der zu lang war. Ich habe die eben beschriebene Prozedur wiederholt, bis das reindexdb Kommando schließlich fehlerfrei durchgelaufen war.
Interessanterweise hat die ganze Aktion einige neue Indizes mit der Endung _ccnew hinterlassen. Wieso, ist mir nicht ganz klar. Vielleicht war das auch dem ganzen Gebastel geschuldet, das sich in der Zwischenzeit abgespielt hat (und das ich hier nicht beschreiben will).
Jedenfalls habe ich auch diese nicht mehr benötigten Index-Kopien entfernt, um Speicherplatz freizugeben. Dazu habe ich mir eine Liste von Löschkommandos generieren lassen:
SELECT
'DROP INDEX CONCURRENTLY IF EXISTS '
|| quote_ident(n.nspname)
|| '.'
|| quote_ident(c.relname)
|| ';'
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'i'
AND c.relname ILIKE '%ccnew%';
Die Ausgabe lautete etwa wie folgt:
DROP INDEX CONCURRENTLY IF EXISTS public.index_statuses_20190820_ccnew;
DROP INDEX CONCURRENTLY IF EXISTS public.index_statuses_local_20190824_ccnew;
DROP INDEX CONCURRENTLY IF EXISTS public.index_statuses_on_reblog_of_id_and_account_id_ccnew;
DROP INDEX CONCURRENTLY IF EXISTS public.index_statuses_on_deleted_at_ccnew;
DROP INDEX CONCURRENTLY IF EXISTS public.index_statuses_on_in_reply_to_account_id_ccnew;
DROP INDEX CONCURRENTLY IF EXISTS public.index_statuses_on_in_reply_to_id_ccnew;
DROP INDEX CONCURRENTLY IF EXISTS public.index_statuses_on_uri_ccnew;
Diese DROP Kommandos habe ich einfach der Reihe nach ausgeführt.
Fehlt noch der Abschluss: Das erneuern der Datenbank-Collation:
ALTER DATABASE mastodon_production REFRESH COLLATION VERSION
… und fertig!
Zu meinem Erstaunen wurden durch die ganze Aktion ca 50 GB Speicher frei.