WaitForProcess – Blockierungen intelligent vermeiden
Ein Mal kam ein Kollege bei mir vorbei und schilderte folgendes Problem:
Es gibt eine Datenbank, auf welche sowohl intern als auch von einer 3rd – Party – Applikation zugegriffen wird. Leider ist die Applikation etwas fehlerhaft programmiert, was dazu führt, dass sie während ihrer Aktivitäten mehrere Tabellen in den exklusiven Zugriff nimmt. Wenn man nun gleichzeitig eine interne Abfrage startet, kommt es zu sehr langen, scheinbar unerklärlichen Wartezeiten oder gar Deadlocks.
Der Kollege hat mich in dieser Angelegenheit um Hilfe gebeten, um eine interne, per SQL Agent – Job angestoßene, Abfrage fehlerfrei oder mit zumindest erklärbaren Meldungen ausführen zu lassen.
Zum Glück haben wir festgestellt, dass die 3rd – Party – Applikation nicht permanent auf die Datenbank zugreift. Als Lösung habe ich dann ein kurzes Skript implementiert, welches prüft, ob ein bestimmter Prozess läuft (also z. B. unser „Blockierer“) und wenn das der Fall ist, wird erst Mal eine kurze Pause eingelegt. Danach wird die Prüfung wiederholt und so weiter, bis die maximale Anzahl Wiederholungen erreicht ist. Wenn der „Blockierer“ noch da ist, dann wird eine Fehlermeldung generiert. Jetzt mussten wir nur noch den betroffenen SQL Agent – Job aufmachen und unser Skript als den ganz ersten Schritt hinzufügen. Dadurch hatten wir schon unsere Lösung: beim Starten des Jobs wurde erst Mal unser „WaitForProcess“ – Skript gestartet, das folgendes macht:
- Entweder wartet, bis der „Blockierer“ nicht mit dem Server verbunden ist, und die Ausführung des Jobs mit dem nächsten Schritt (der betroffenen Abfrage) fortsetzt.
Oder einen Fehler generiert, wenn die Blockierung zu lange dauert, und dadurch die Jobausführung mit einer verständlichen Meldung abbricht.
Nun einige Hinweise zur Implementierung. Das Skript ist recht allgemein geworden und kann überall eingesetzt werden, zum Beispiel auch als Teil einer gespeicherten Routine. Die Prüfung erfolgt in einer Schleife durch Analyse der Tabelle „sysprocesses“. Dabei werden, um die Überwachung des „Blockierers“ genau einzuschränken, die Namen der Datenbank, des Hosts und des verwendeten Logins geprüft. Anschließendes Warten erfolgt durch die Verwendung eines „waitfor delay“ – T-SQL – Befehls.
Das komplette Skript kann man im Anhang an diesen Beitrag finden. Für einen konkreten Einsatz müssen die Variablen @TargetDBName, @HostName und @LoginName am Anfang des Skriptes gesetzt werden, ggf. auch die Variable @MaxRuns (die die Anzahl der Warteschleifen begrenzt).