Das Symptom
Der Monatsbericht wird aus der Datenbank gezogen, eine Abfrage über mehrere große
Tabellen. Sie läuft an, die Last steigt, und nach 30 Sekunden ist Schluss: Die
Anwendung meldet einen Fehler mit dem Wortlaut canceling statement due to statement timeout, der Server läuft weiter, nichts ist abgestürzt. Der Versuch von Hand in
psql, als Administrator, läuft dagegen zwei Minuten und liefert das Ergebnis.
Die Suche beginnt in postgresql.conf. Dort steht nichts von einem Timeout. Also wird der Server verdächtigt, die Netzverbindung, der Pool, das Betriebssystem. Dabei ist die Zahl das Verräterische: Ein Absturz passiert zu zufälligen Zeiten, ein Timeout immer zur selben Sekunde.
Die „Layer 8“-Wahrheit
Der Server tut, was ihm jemand gesagt hat, nur nicht in der Datei, in der man nachsieht. statement_timeout bricht laut Dokumentation jede Anweisung ab, die länger als die angegebene Zeit braucht, gemessen von der Ankunft des Befehls bis zur Fertigstellung durch den Server. Die Voreinstellung ist null, also aus. Ein Abbruch nach exakt 30 Sekunden ist deshalb kein Serverfehler, sondern ein gesetzter Wert.
Dass er in postgresql.conf nicht steht, hat einen Grund, der in der Dokumentation selbst steht: „Setting statement_timeout in postgresql.conf is not recommended because it would affect all sessions.“ Wer den Rat befolgt hat, hat den Wert pro Rolle oder pro Datenbank gesetzt, oder der Verbindungspool und der Treiber der Anwendung schicken ihn beim Verbindungsaufbau mit. Laut Kapitel Setting Parameters überschreiben Werte aus ALTER ROLE und ALTER DATABASE die Konfigurationsdatei und gelten als Vorgabe für jede neue Sitzung dieser Rolle. Der Administrator in psql hat eine andere Rolle, einen anderen Pool und deshalb einen anderen Wert. Dass sein Versuch durchläuft, beweist nicht, dass die Anwendung spinnt, sondern dass sie anders konfiguriert ist.
Der Wert ist in aller Regel absichtlich da. Für die Rolle einer Webanwendung ist ein kurzer statement_timeout ein Schutz: Eine entgleiste Abfrage blockiert nicht minutenlang eine Verbindung aus dem Pool. Das Problem ist nicht der Timeout, sondern dass der Monatsbericht mit derselben Rolle und denselben Regeln läuft wie ein Klick im Browser. Die Dokumentation legt den Ausweg nahe, ohne ihn zu nennen: Werte pro Rolle. Eine Berichtsrolle mit langem Timeout, eine Anwendungsrolle mit kurzem, und für den Einzelfall SET LOCAL innerhalb der Transaktion, wodurch die Änderung mit dem COMMIT wieder verschwindet.
Zwei Feinheiten verhindern falsche Schlüsse. Erstens zählt der Timeout pro Anweisung: „If multiple SQL statements appear in a single simple-query message, the timeout is applied to each statement separately.“ Ein Skript aus vielen kurzen Anweisungen läuft durch, eine einzelne lange nicht, und vor Version 13 war das laut Dokumentation anders. Zweitens gibt es Nachbarn mit ähnlichem Namen: lock_timeout greift nur beim Warten auf Sperren, transaction_timeout beendet die ganze Sitzung, wenn die Transaktion zu lange dauert. Wer die drei verwechselt, sucht beim falschen Parameter.
Am Ende ist die Zahl 30 die beste Spur, die ein Fehler hinterlassen kann. Sie steht irgendwo, mit Einheit oder als 30000 Millisekunden, und SHOW statement_timeout in der richtigen Sitzung zeigt sie. Danach ist es keine Störung mehr, sondern eine Entscheidung, wer wie lange rechnen darf.