Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Problem mit dealock, Interbase, in Service-Programm

12 views
Skip to first unread message

Georg Hübner

unread,
Mar 2, 2013, 10:37:45 AM3/2/13
to
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

unread,
Mar 2, 2013, 11:50:49 AM3/2/13
to

"Georg Hübner" <huebne...@t-online.de> schrieb im Newsbeitrag news:kgt69q$3tc$1...@dont-email.me...
> 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:

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

unread,
Mar 2, 2013, 12:49:41 PM3/2/13
to
Hallo,

>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

Johann Schierl

unread,
Mar 2, 2013, 12:58:22 PM3/2/13
to
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

Arno Garrels

unread,
Mar 2, 2013, 1:48:39 PM3/2/13
to

"Georg Hübner" <huebne...@t-online.de> schrieb im Newsbeitrag news:kgte16$hgb$1...@dont-email.me...
> Hallo,
>
>>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.

> Das Timer-Ereignis wird nur einmal in der Minute aufgerufen.

Das ist natürlich unerheblich.

>
> 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.

> ...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.

>
> 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

unread,
Mar 2, 2013, 3:40:35 PM3/2/13
to
Hallo
> Auch dieses Ereignis feuert im Service-Thread.
>
>>
>> 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)

Georg Hübner

unread,
Mar 2, 2013, 8:02:50 PM3/2/13
to
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

Georg Hübner

unread,
Mar 3, 2013, 4:00:03 AM3/3/13
to
Hallo,

danke für die Tips.

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

Mfg


Georg Hübner

Arno Garrels

unread,
Mar 3, 2013, 12:26:04 PM3/3/13
to
Fritz Westermann wrote:
>> Ä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

unread,
Mar 4, 2013, 10:35:24 AM3/4/13
to
Hallo,

>�ndere das Design, es ist buggy.

ich habe Deinen Rat befolgt und die Form vollst�ndig aus dem Projekt verband.
Alle IB-Komponenten und der Timer sind jetzt Bestandteil der TService-Klasse.

Und es l�uft ohne Exceptions. Bis jetzt keine Fehler.
So soll es sein! :-)

Besten Dank noch mal f�r den Hinweis bez�glich der nicht vorhandenen "Threadsicherheit".
Da w�re ich so schnell nicht drauf gekommen.

Das erkl�rt warscheinlich auch einige Fehler, die in anderen Service-Projekt schon mal
auftreten (manchmal erst nach Monaten :-/ ).

Mfg


Georg H�bner


0 new messages