Das Skript ist simpel. Der aufmerksame Leser mag sich fragen, wozu Zeile 7 gut ist. Sie umgeht einen Bug bei der Verarbeitung der Timeouts von CGI-Programmen, von dem der Apache Server 2.2.21 mit Prefork-Modul betroffen ist. Wenn ein Programm nämlich auf Stdout und Stderr nichts ausgibt, versucht der Webserver erst nach dem Timeout für Stdout auch Stderr zu lesen. Erst wenn für diese Verbindung auch ein Timeout aufgetreten ist, beendet er die Anfrage. Zeile 7 sorgt dafür, dass nur ein Timeout-Intervall nötig ist. Leider wird damit auch der eigentlich richtige HTTP-Fehlercode 504 verhindert. Stattdessen liefert der Webserver eine leere Antwort.
Das Programm installieren Sie als
»burn0.pl
«
in einem Verzeichnis, das für CGI-Skripte konfiguriert ist. Im Folgenden gehe ich davon aus, dass der Webserver auf
»localhost
«
läuft und der URL-Pfad
»/cgi/
«
auf das CGI-Verzeichnis abgebildet wird.
$ curl http://localhost/cgi/burn0.pl 00:00:00.50023
Die Ausgabe zeigt, wie lange der Datenbankprozess Rechenzeit verbraucht hat. Die gewünschte Dauer übergeben Sie als CGI-Parameter:
$ curl http://localhost/cgi/burn0.pl\?3 00:00:03.000207
In meiner
»httpd.conf
«
ist ein Timeout von 5 Sekunden eingestellt. Läuft die SQL Anfrage 4,9 Sekunden, sollte der Server noch eine vernünftige Antwort liefern, bei 5,1 Sekunden schon nicht mehr.
$ time curl http://localhost/cgi/burn0.pl\?4.9 00:00:04.900198 real 0m4.958s user 0m0.003s sys 0m0.006s $ time curl http://localhost/cgi/burn0.pl\?5.1 curl: (52) Empty reply from server real 0m5.044s user 0m0.005s sys 0m0.005s
Um den eingangs beschriebenen Effekt zu beobachten, rufen Sie
»burn0.pl
«
mit dem Parameter 60 auf. Das Curl-Kommando ist nach 5 Sekunden fertig; dem Datenbankprozess können Sie aber eine volle Minute bei der Arbeit zusehen.
Der umsichtige Programmierer sollte also irgendwas unternehmen, damit beim Eintreffen des SIGTERM nicht nur das CGI-Programm beendet wird, sondern auch der Datenbankprozess. Eine Variante wäre, den Prozess einfach zu killen. Da PostgreSQL für jeden Benutzer einen separaten Prozess startet, ist das gleichwertig mit dem Abbruch der aktuellen Transaktion. Dabei sind drei Probleme zu lösen:
Als Lösung für das erste Problem denkt ein erfahrener Perl-Programmierer wahrscheinlich sofort an
»$SIG{TERM}
«
. Leider funktioniert das hier nicht, denn moderne Perl-Versionen stellen asynchrone Signale nur an sicheren Punkten im Programm zu. Mitten im Aufruf einer C-Funktion ist aber kein solcher Punkt. Der Handler wird also erst aufgerufen, wenn
»$sth->execute
«
in Zeile 13 zurückkehrt. Mit
»POSIX::sigaction
«
kann der Signal-Handler so installiert werden, dass das Signal zeitnah zugestellt wird.