PostgreSQL asynchron

© Sergey Nivens, 123RF

Ausgefeilt

Datenbankabfragen in Perl zu programmieren, ist dank des DBI-Moduls recht einfach. Doch birgt diese Einfachheit auch Tücken.
Mit Hardware-Beschleunigung und schnellem Netz hilft Desktop-Virtualisierung, Administrationsaufwand und Kosten sparen, ADMIN 04/2013 verrät, wie die ... (mehr)

Ein Problem mit Webanwendungen lässt sich in der Praxis oft beobachten: Ohne großen Aufwand (und ohne viel nachzudenken) entsteht eine Webapplikation mit einer Datenbank als Backend. Mit der Zeit wächst der Datenbestand und irgendwann merkt der Anwender, dass das System immer träger und träger wird. Wird er dabei ungeduldig, drückt er auf den Reload-Button, im schlimmsten Fall immer und immer wieder… Zur Abhilfe erzeugt der Datenbankadministrator erst einmal Indices in der Datenbank. Dann wird neue Technik angeschafft. Das hilft – zumindest eine Weile.

Längerfristig kann beides zusammen – eine Datenbank am Rand ihrer Kapazität und ein ungeduldiger Anwender – sehr schnell dazu führen, dass sich die Applikation eine längere Auszeit gönnt.

Ursachenforschung

Die besagte Anwendung ist recht konservativ gestrickt: der Apache-Webserver, Mod-CGI, ein paar CGI-Programme in Perl und eine PostgreSQL-Datenbank. Der Apache-HTTPD bietet die Timeout-Direktive, um mit Denial-of-Service-Problemen dieser Art umzugehen. Damit werden die I/O-Kanäle des Webservers überwacht, zum Beispiel nach dem Start eines CGI-Programms dessen Stdout-Kanal. Kommen während der festgelegten Timeout-Periode keine Daten zurück, beendet der Webserver die Anfrage mit einem Fehler. Beim Freigeben der für die Anfrage belegten Ressourcen stoppt er auch das CGI-Programm. Dazu schickt er ihm zunächst ein SIGTERM-Signal. Falls das nicht hilft, folgt einige Sekunden später ein SIGKILL.

Dieses Schema ist einfach, besitzt aber zwei gravierende Nachteile. Zum einen merkt der Webserver nicht, wenn der Browser die Verbindung zwischenzeitlich beendet, weil er von ihm keine Daten erwartet. Zum anderen reicht es nicht, das CGI-Programm zu beenden, um die in der Datenbank belegten Ressourcen freizugeben.

Es passiert Folgendes: Der Browser schickt eine Anfrage, das CGI-Programm startet und schickt die Datenbankabfrage, die etwas Zeit braucht. Der ungeduldige Benutzer drückt den Knopf zum erneuten Laden der Seite. Der Browser bricht die aktuelle Anfrage ab. Davon merkt der Server aber nichts, weil er gerade auf das CGI-Programm wartet. Dann tritt der Timeout ein, und das Programm wird beendet. Leider merkt die Datenbank davon nichts und arbeitet weiter die SQL-Abfrage ab. Die erneute Anfrage des Benutzers initiiert jedoch eine zweite Abfrage in der Datenbank, die genauso aufwändig ist. Nach ein paar weiteren Klicks steht das System.

Ohne Änderungen an Mod-CGI oder einem zusätzlichen Modul ist es nicht möglich, die Anfrage im Webserver zu beenden, sobald der Browser die Verbindung schließt. Es ist aber machbar, die Datenbankabfrage zu beenden, sobald der Webserver das SIGTERM-Signal schickt. Darum dreht sich der Rest des Artikels. Um die Beispiele nachzuvollziehen, wird eine Datenbank vorausgesetzt, in der die Sprache PLPGSQL aktiviert ist. Beim Schreiben des Artikels wurde die PostgreSQL-Version 8.4 benutzt.

Testumgebung

Wenn Sie zum Testen eine große Datenbank zur Verfügung haben, bei der Sie SQL-Anfragen formulieren können, die ein paar Sekunden oder Minuten dauern, ist das gut. Falls nicht, liefert PostgreSQL die Funktion »pg_sleep« . Ein bisschen näher an der Realität wäre die PLPGSQL-Funktion aus Listing 1. Sie tut zwar auch nichts, verbraucht dabei aber eine Menge Rechenzeit, sodass der zugehörige Prozess im »top« -Output an einer der oberen Stellen zu sehen ist. Zum Testen können Sie die Funktion mit »psql« aufrufen:

Listing 1

burncpu

01 CREATE OR REPLACE FUNCTION burncpu (tm INTERVAL)
02   RETURNS INTERVAL AS $CODE$
03 DECLARE
04   stmp TIMESTAMP := now()+tm;
05   i INT;
06 BEGIN
07   WHILE clock_timestamp()<stmp LOOP
08     i:=1;
09   END LOOP;
10   RETURN clock_timestamp()-now()
11 END;
12 $CODE$ LANGUAGE plpgsql;
r2=> select burncpu('30s');
     burncpu
-----------------
 00:00:30.000053

Das Ergebnis wird erst nach 30 Sekunden geliefert. So lange führt der zugehörige Datenbankprozess die CPU-Lasttabelle an (Abbildung 1).

Abbildung 1: Der burncpu()-Prozess führt die top-Liste an.

Als weitere Zutat benötigen Sie das in Listing 2 abgebildete CGI-Programm. Unter Umständen müssen Sie dafür die Perl-Module »common::sense« , »DBI« und »DBD::Pg« nachinstallieren. Eine aktuelle Linux-Distribution sollte sie in den Software-Repositories führen. Außerdem müssen Sie die Login-Daten für die Datenbank anpassen.

Listing 2

burn0.pl

01 #!/usr/bin/perl
02
03 use common::sense;
04 use DBI;
05
06 print "Status: 200\nContent-Type: text/plain\n\n";
07 $|=1; $|=0;                     # flush
08
09 my $dbh=DBI->connect('dbi:Pg:dbname=r2', 'ipp', undef,
10                      {RaiseError=>1});
11
12 my $sth=$dbh->prepare('select burncpu(?)');
13 $sth->execute((($ENV{QUERY_STRING}+0) || .5).'s');
14
15 while( my $row=$sth->fetchrow_arrayref ) {
16   print "@$row\n";
17 }
comments powered by Disqus

Artikel der Woche

Zertifikatsmanagement mit Certmonger

Zertifikate werden dazu verwendet, um Benutzer, Services und Hardware mit der Hilfe eines signierten Schlüssels zu verifizieren. Hierfür existieren Public-Key-Infrastrukturen (PKI). Aber wie gelangen die Zertifikate eigentlich auf das Ziel-Gerät? Der Open-Source-Tipp in diesem Monat beschreibt, wie Sie für diese Aufgabe das Tool certmonger verwenden können. (mehr)
Einmal pro Woche aktuelle News, kostenlose Artikel und nützliche ADMIN-Tipps.
Ich habe die Datenschutzerklärung gelesen und bin einverstanden.

Linux-Backup

Welche Backup-Lösung setzen Sie für Linux im professionellen Umfeld ein?

  • keine
  • eigene Scripts
  • rsnapshot
  • rdiff-backup
  • Bacula
  • Bareos
  • Borg
  • Duplicity
  • Amanda
  • Burp
  • Clonezilla
  • Acronis
  • Arkeia
  • SEP sesam
  • Veeam
  • Redo Backup
  • Relax-and-Recover
  • andere kommerzielle Software
  • andere Open-Source-Software

Google+

Ausgabe /2017

Microsite