Discussion:
Problem mit dealock, Interbase, in Service-Programm
(zu alt für eine Antwort)
Georg Hübner
2013-03-02 15:37:45 UTC
Permalink
Hallo,

ich benutze für mein Programm Interbase als Datenbank und greife über Stored procedures darauf zu.
Das Problem ist, wenn ich den Code in einem Service laufen lasse bekomme ich immer eine "deadlock-Meldung".
Lasse ich den Code in einer normalen Anwendung laufen, gibt es keine Exceptions.
Alles läuft dann so wie es soll, ohne Fehler.

Ich weis leider keinen Rat, wie ich das Ganze als Service fehlerfrei und dauerhaft zum Laufen bekomme. :-(
Hat jemand eine Idee?

Die Datenbankaktion werden über ein Timer-Event gesteuert:
procedure TForm1.Timer1Timer(Sender: TObject);
var
AktDateTime: TMyDateTime;
begin
AktDateTime:=GetAktDateTime;

if Stundenwechsel then
begin
SaveDBStundendaten; //Insert Stundendaten, in dieser Sub-Routine tritt die Exception auf
UpdateDBTagesdaten; //Update Tagesdaten, hier keine Exception
UpdateDBMonatsdaten; //Update Monatsdaten, hier keine Exception

FLastDataDateTime.Stunde:=AktDateTime.Stunde;
end;

// ab hier wird noch getestet. Im der normalen Anwendung gab es keine Probleme
if Tageswechsel then
begin
SaveDBTagesdaten; //Insert Tagesdaten, Delete Stundendaten

FLastDataDateTime.Tag:=AktDateTime.Tag;
end;

if Monatswechsel then
begin
SaveDBMonatdaten; //Insert Monatsdaten

FLastDataDateTime.Monat:=AktDateTime.Monat;
FLastDataDateTime.Jahr:=AktDateTime.Jahr;
end;
end;


Hier die Procedure für DB-Zugriff:
procedure TForm1.SaveDBStundendaten;
begin
try
IBStoredProcInsStd.ExecProc;
IBTransactionInsStd.Commit;
except
on E: Exception do
begin
WriteErrFile(E.Message,' / Unit MainServer: procedure SaveDBStundendaten');
IBTransactionInsStd.Rollback;
end;
end;
end;

Die Exceprion-Meldung:
deadlock update conflicts with concurrent update In Modul: / Unit MainServer: procedure SaveDBStundendaten


Der vollständighalber hier noch die aufgerufene Stored procedures:
/* Stored procedures */
ALTER PROCEDURE "INSERT_STUNDENDATEN"
AS
declare variable Date_D Date;
declare variable Time_T Time;
declare variable JAH INTEGER;
declare variable MON INTEGER;
declare variable TAH INTEGER;
declare variable STU INTEGER;

declare variable S_Int INTEGER;
declare variable C_Int INTEGER;
declare variable E_Int INTEGER;

BEGIN
/* aktuelle Zeit holen */
Date_D = CURRENT_DATE;
Time_T = CURRENT_TIME;
JAH=Extract(year FROM Date_D);
MON=Extract(month FROM Date_D);
TAH=Extract(day FROM Date_D);
STU=Extract(hour FROM Time_T);

/* alle gesammelten Daten holen und Durchschnitt ermitteln */
Select Sum(Inhalt), Count(Inhalt)
From StdInhalt
Into :S_Int, :C_Int;

if (C_Int > 0) then
begin
E_Int = S_INT / C_Int; /* Durchschnitt berechnen */
Delete From StdInhalt; /* alle "gesammelten" Daten löschen */
end
else
begin
Select Inhalt /* wenn keine gesammelten Daten vorhanden, dann aktuellen Wert holen */
From Aktuell
Where LfdNr = 1
Into :E_Int;
end

/* neuen Stundendatensatz anlegen */
Insert Into Stundendaten (jahr, monat, tag, stunde, inhalt)
Values(:"JAH", :"MON", :"TAH", :"STU", :E_Int);

suspend;
END

Mfg

Georg Hübner
Arno Garrels
2013-03-02 16:50:49 UTC
Permalink
Post by Georg Hübner
Hallo,
ich benutze für mein Programm Interbase als Datenbank und greife über Stored procedures darauf zu.
Das Problem ist, wenn ich den Code in einem Service laufen lasse bekomme ich immer eine "deadlock-Meldung".
Lasse ich den Code in einer normalen Anwendung laufen, gibt es keine Exceptions.
Alles läuft dann so wie es soll, ohne Fehler.
Ich weis leider keinen Rat, wie ich das Ganze als Service fehlerfrei und dauerhaft zum Laufen bekomme. :-(
Hat jemand eine Idee?
Schuss ins Blaue: Vermutlich ein Thread-Problem. Das Timer-Ereignis
wird in einem anderen Thread-Kontext als dem der Datenbank-Connection
gefeuert.
TTimer erzeugt ein verstecktes Fenster beim Create und das Ereignis
OnTimer wird immer im Kontext desjenigen Threads gefeuert, in dem
das Fenster zuvor erzeugt wurde. TService besitzt standardmäßig einen
Service-Thread und einen Haupt-Thread.
--
Arno
Georg Hübner
2013-03-02 17:49:41 UTC
Permalink
Hallo,
Post by Arno Garrels
Schuss ins Blaue: Vermutlich ein Thread-Problem. Das Timer-Ereignis
wird in einem anderen Thread-Kontext als dem der Datenbank-Connection
gefeuert.
TTimer erzeugt ein verstecktes Fenster beim Create und das Ereignis
OnTimer wird immer im Kontext desjenigen Threads gefeuert, in dem
das Fenster zuvor erzeugt wurde. TService besitzt standardmäßig einen
Service-Thread und einen Haupt-Thread.
Nein, ich glaube nicht, dass Threads hiebei keine Rolle spielen. In beiden
Fällen wird eine Form verwendet auf der der Timer und die IB-Komponenten
plaziert sind. Hier spielt sich alles im selben Hauptthread der Form ab.
Das Timer-Ereignis wird nur einmal in der Minute aufgerufen.

Beim Service wird im Start-Ereignis die Form erst erzeugt...

procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
Form1:=TForm.Create(self);
end;

...und beim Stop-Ereignis wieder frei gegeben.

procedure TTankService.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
Form1.Free;
end;

Diese Methode hat sich bis jetzt bewährt und hat den Vorteil, dass man
die Form nur einmal aufbaut und sie in beiden Fällen unverädert einsetzen kann.

Die Ursache muß beim Datenzugriff liegen.
Ich werde mal die Zeile: "Delete From StdInhalt;" aus der Stored procedure
heraus nehmen und separat aufrufen. Vielleicht hilft das.

Zur Ergänzung:
- Entwicklungsumgebung, Win7 64Bit Pro, Delphi XE2
- Einsatz- und Testumgebung, Windows Server 2008 Standard R2

Mfg

Georg Hübner
Arno Garrels
2013-03-02 18:48:39 UTC
Permalink
Post by Georg Hübner
Hallo,
Post by Arno Garrels
Schuss ins Blaue: Vermutlich ein Thread-Problem. Das Timer-Ereignis
wird in einem anderen Thread-Kontext als dem der Datenbank-Connection
gefeuert.
TTimer erzeugt ein verstecktes Fenster beim Create und das Ereignis
OnTimer wird immer im Kontext desjenigen Threads gefeuert, in dem
das Fenster zuvor erzeugt wurde. TService besitzt standardmäßig einen
Service-Thread und einen Haupt-Thread.
Nein, ich glaube nicht, dass Threads hiebei keine Rolle spielen. In beiden
Fällen wird eine Form verwendet auf der der Timer und die IB-Komponenten
plaziert sind. Hier spielt sich alles im selben Hauptthread der Form ab.
Nein, denn du erzeugst deine Form im ServiceThread im Ereignis TService1.ServiceStart.
Grundsätzlich ist das, insbesondere in neueren Delphiversionen problematisch,
denn die VCL ist nicht **threadsicher**. Jede Verwendung von Forms ausserhalb
des Hauptthreads kann zu ernsthaften Problemen führen.
Denn schon bei der Initialisierung der Forms-Unit wird mindestens ein
Fenster erzeugt (seit D2007?), was die Benutzung der Forms.pas (VCL) auf den
Hauptthread beschränkt.
Post by Georg Hübner
Das Timer-Ereignis wird nur einmal in der Minute aufgerufen.
Das ist natürlich unerheblich.
Post by Georg Hübner
Beim Service wird im Start-Ereignis die Form erst erzeugt...
procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
Form1:=TForm.Create(self);
end;
Dieses Ereignis feuert im Service-Thread, nicht im Haupt-Thread.
Post by Georg Hübner
...und beim Stop-Ereignis wieder frei gegeben.
procedure TTankService.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
Form1.Free;
end;
Auch dieses Ereignis feuert im Service-Thread.
Post by Georg Hübner
Diese Methode hat sich bis jetzt bewährt und hat den Vorteil, dass man
die Form nur einmal aufbaut und sie in beiden Fällen unverädert einsetzen kann.
Ändere das Design, es ist buggy.
--
Arno
Fritz Westermann
2013-03-02 20:40:35 UTC
Permalink
Hallo
Post by Arno Garrels
Auch dieses Ereignis feuert im Service-Thread.
Post by Georg Hübner
Diese Methode hat sich bis jetzt bewährt und hat den Vorteil, dass man
die Form nur einmal aufbaut und sie in beiden Fällen unverädert einsetzen kann.
Ändere das Design, es ist buggy.
Kann ich nur unterstützen,
Die Vcl speziell Forms und co hat in einem Service nichts verloren, dass
macht nur Probleme

Fritz (der selber leidvoll erfahren musste)
Arno Garrels
2013-03-03 17:26:04 UTC
Permalink
Post by Fritz Westermann
Post by Arno Garrels
Ändere das Design, es ist buggy.
Kann ich nur unterstützen,
Die Vcl speziell Forms und co hat in einem Service nichts verloren,
dass macht nur Probleme
Solange die VCL / Forms ausschließlich im Kontext des Haupt-Threads
verwendet wird, sehe ich kein Problem.
--
Arno
Georg Hübner
2013-03-03 09:00:03 UTC
Permalink
Hallo,

danke für die Tips.

Ich werde Deine Hinweise auf jeden Fall beherzigen und entsprechend ausprobieren.

Mfg


Georg Hübner
Georg Hübner
2013-03-04 15:35:24 UTC
Permalink
Dieser beitrag ist möglicherweise unangemessen. Klicken sie auf, um es anzuzeigen.
Johann Schierl
2013-03-02 17:58:22 UTC
Permalink
Auch Hallo,

da mir hier schon viel geholfen wurde rein durch das mitlesen, versuche
ich jetzt auch mal einen Tipp zu geben.

Ich verwende ebenfalls SQL Datenbankabfragen in Services,
früher mal mit Timer auf die habe ich inzwischen ganz verzichtet.
In einem separaten Workthread mache ich meine Überprüfungen:

procedure WorkThread.Execute;
begin
CoInitialize(nil);
// sonstige Einstellungen
try
while not Terminated do begin
try
// mach was z. B. wenn Notwendig einen Datenbankprocedure
sleep (100);
except
on E: Exception do Log_Write ( E.Message );
end;
end;
finally
// alles Freigeben
CoUninitialize;
end;
end;

Bei den Datenbankverbindung mache ich alles in einer eigenen Procedure:

procedure WorjThread.Datenbank;
var Datanbankverbindung;
Storedprocedure;

begin
Datenbankverbindung erstellen;
Storedprocedure erstellen;
try
Datenbankverbindung open;
Storedprocedure befüllen;
try
Storedprocedure ausführen;
except
on E: Exception do Log_Write ( E.Message );
end;
finally
// Storedprocedure Freigeben und löschen
// Datenbankverbindung Freigeben und löschen
end;
end;

So das eben alles für den Datenbankzugriff nur in dieser Procedure
enthalten ist. Try finally und try except sollten natürlich öfter
vorkommen. Vielleicht hilft dir das

mfg

Johann
Georg Hübner
2013-03-03 01:02:50 UTC
Permalink
Hallo,

habe gerade feststellen müssen, dass der Service sich ab 00:00:11
komplett aufgehängt hat.

Es gab aber keinen ErrLog-Eintrag, der dies genauer erklären konnte. :-(
In der ErrLog-Datei stand nur der stündliche Eintrag:

deadlock update conflicts with concurrent update In Modul: / Unit MainServer: procedure SaveDBStundendaten

Ich konnte den Service, bzw. den Prozess nur noch über den Task-Manager
stoppen.

Wie gesagt, in einer normalen Application
läuft das Programm (auch über den Tageswechsel) problemlos durch.

Hintergrund der ganzen Geschichte ist, dass das Projekt bisher über einer 32Bit Service-Anwendung (Delphi7-Entwicklung)
mit MS-Access Datenbank seit 2006 problemlos läuft. MS-Accsess reicht eigentlich völlig aus für dieses Projekt.
Interbase bietet sich nur wegen der 64Bit Entwicklung an.

Da ich nun mal gerne alles auf 64Bit portieren wollte, blieb es mir nicht erspart, auch die DB zu wechseln.
MS stellt leider keine 64Bit Treiber für Access bereit. :-(

Aber es muß doch eine Möglichkeit geben Datenbankzugriffe mit Intebase ohne die von mir beschriebenen
Probleme aus einem Service heraus zu realisieren.

Vielleicht liegt es auch daran, wie ich diese IB-Komponenten einsetze. Irgenetwas übersehe ich. :-/

Mfg

Georg Hübner
Lesen Sie weiter auf narkive:
Loading...