MediaWiki-Upgrade: update.php bricht mit „DBReadOnlyError: Database is read-only“ ab
Wer ein Wiki vor dem Upgrade mit $wgReadOnly sperrt, sperrt bei aktuellen MediaWiki-Versionen auch update.php aus. Eine Ausnahme für die Kommandozeile hält Besucher draußen und lässt die Wartungsskripte schreiben. Dazu drei Stolperfallen nach dem Upgrade: Login, Interwiki und ein überschriebenes Logo.
$ git log --graph Der Weg zur Lösung
-
Problem
update.php bricht mit DBReadOnlyError ab
Beim Upgrade auf MediaWiki 1.46 scheiterte update.php sofort mit „Database is read-only“.
-
Irrweg
Schreibschutz vor dem Update gesetzt
Ich hatte das Wiki wie empfohlen mit $wgReadOnly gesperrt und angenommen, Wartungsskripte seien davon ausgenommen.
-
Erkenntnis
Auch update.php hält sich an $wgReadOnly
Der Fehler kam beim ersten Schreibzugriff, die Datenbank blieb unverändert.
-
Lösung
Schreibschutz nur fürs Web
Mit PHP_SAPI === "cli" ? false : "…" dürfen Wartungsskripte schreiben, Besucher sehen das Wiki weiter gesperrt.
-
Erkenntnis
Nacharbeiten: Login, Interwiki, Logo
Login erst nach Aufheben des Schreibschutzes testen, Interwiki aus LocalSettings entfernen, Logo dauerhaft im Upload-Ordner ablegen.
Beim Upgrade eines MediaWiki auf 1.46 per Shell bin ich so vorgegangen, wie es viele Anleitungen empfehlen: Wiki mit $wgReadOnly sperren, Backup ziehen, neue Version entpacken, Ordner tauschen, update.php ausführen. Der letzte Schritt brach sofort ab:
Going to run database updates for wikidb
Depending on the size of your database this may take a while!
Creating existencelinks table...Wikimedia\Rdbms\DBReadOnlyError from line 683 of /var/www/wiki/includes/libs/Rdbms/Database/Database.php: Database is read-only: Wartung: Upgrade läuft
#0 /var/www/wiki/includes/libs/Rdbms/Database/Database.php(635): Wikimedia\Rdbms\Database->executeQuery()
…
#9 /var/www/wiki/maintenance/update.php(191): MediaWiki\Installer\DatabaseUpdater->doUpdates()
Das Problem
Ich war davon ausgegangen, dass $wgReadOnly nur Bearbeitungen über die Weboberfläche verhindert und Wartungsskripte davon unberührt bleiben. Bei MediaWiki 1.46 stimmt das nicht: Auch update.php hält sich an den Schreibschutz und scheitert beim ersten Schreibzugriff. Ab welcher Version genau das so ist, habe ich nicht geprüft.
Die gute Nachricht: Der Fehler kommt beim allerersten Schreibversuch. Die Datenbank war danach unverändert, ein Zurückspielen des Dumps war nicht nötig.
Die Lösung
Der Schreibschutz soll für Besucher gelten, nicht für die Kommandozeile. PHP verrät über PHP_SAPI, wie ein Skript gestartet wurde. Statt der üblichen Zeile kommt diese in die LocalSettings.php:
$wgReadOnly = "Wartung: Upgrade läuft";
$wgReadOnly = PHP_SAPI === "cli" ? false : "Wartung: Upgrade läuft"; # upgrade-sperre
Der Kommentar am Ende dient als Markierung, damit sich die Zeile später sicher wieder entfernen lässt. Der Ablauf auf der Shell:
cd /var/www/wiki
echo '$wgReadOnly = PHP_SAPI === "cli" ? false : "Wartung: Upgrade läuft"; # upgrade-sperre' >> LocalSettings.php
php -l LocalSettings.php
php maintenance/run.php update --quick
sed -i '/# upgrade-sperre$/d' LocalSettings.php
php maintenance/run.php runJobs
Wer die neue Version in einen eigenen Ordner entpackt und die LocalSettings.php dorthin kopiert, nimmt die Zeile automatisch mit. Entfernt wird sie dann im neuen Ordner.
Warum das funktioniert
Wartungsskripte laufen über die PHP-Kommandozeile, PHP_SAPI ist dort cli. Seitenaufrufe kommen über PHP-FPM oder das Apache-Modul und haben einen anderen Wert. MediaWiki wertet false als „nicht schreibgeschützt“. Die Kommandozeile darf also schreiben, die Weboberfläche bleibt gesperrt. Ich habe die Zeile mit php -l geprüft und auf der Kommandozeile ausgewertet, dort ergab sie false. Danach lief update.php durch.
Stolperfallen
Login testen erst ohne Schreibschutz
Nach dem Upgrade habe ich das Wiki im Browser getestet, solange der Schreibschutz fürs Web noch aktiv war. Die Anmeldung scheiterte mit der Meldung, dass Cookies nicht aktiviert seien. Vermutlich kann MediaWiki die Sitzung nicht speichern, solange die Datenbank für Webzugriffe gesperrt ist. Den Zusammenhang habe ich nicht abschließend geprüft. Ich würde den Schreibschutz deshalb vor dem Login-Test aufheben und erst dann testen.
Interwiki steckt seit 1.44 im Core
Die Extension Interwiki wurde mit MediaWiki 1.44 in den Core übernommen. Der Release-Branch der Extension enthielt bei mir keine extension.json mehr. Die Zeile wfLoadExtension( 'Interwiki' ); muss aus der LocalSettings.php raus. Spezial:Interwiki gibt es weiterhin.
Eigenes Logo unter dem Namen des Platzhalters
Wer sein Logo einfach als resources/assets/change-your-logo.svg gespeichert hat, bekommt nach dem Upgrade wieder den Platzhalter. Die Datei gehört zum Paket und wird ersetzt. Dauerhaft sicher liegt das Logo im Upload-Ordner, den jedes Upgrade übernimmt:
mkdir -p /var/www/wiki/images/logo
cp logo.svg /var/www/wiki/images/logo/logo.svg
$wgLogos = [
'1x' => "$wgScriptPath/images/logo/logo.svg",
'icon' => "$wgScriptPath/images/logo/logo.svg",
];
Eigene Farben gehören aus demselben Grund nicht in die Skin-Dateien, sondern auf die Wiki-Seite MediaWiki:Common.css oder die passende Skin-Seite. Die liegt in der Datenbank und übersteht jedes Upgrade.
Prüfskripte mit grep -q in der Pipe
Wer das Backup per Skript mit set -o pipefail prüft, sollte tar tzf archiv.tar.gz | grep -q LocalSettings.php vermeiden. grep -q beendet sich beim ersten Treffer, tar bekommt beim Weiterschreiben ein SIGPIPE, und die Pipe meldet Code 141. Bei einem großen Archiv sieht ein vollständiges Backup dann wie ein fehlerhaftes aus. Abhilfe: die Inhaltsliste erst in eine Datei schreiben und darin suchen.