Entwickler-Ecke

Datenbanken - FireDAC Connection Definition Problem


NOS1971 - Do 24.07.14 13:25
Titel: FireDAC Connection Definition Problem
Hallo zusammen,

ich habe ein Problem eine Connection Definiton zu erzeugen für meine multithreaded anwendung. ich nutze folgenden code und es erscheint die im anhang sichtbare fehlermeldung obwohl ich alles laut doku mache.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
 // check if connection definition exists
 if FDManager.IsConnectionDef(ConDefName) then
 begin
  FDManager.DeleteConnectionDef(ConDefName);
 end;
 // create connection and add it
 condef := TStringList.Create;
 condef.Add('Database=' + FResultDataBaseFileName);
 condef.Add('SharedCache=False');
 condef.Add('LockingMode=Normal');
 condef.Add('Synchronous=Full');
 condef.Add('LockingMode=Normal');
 condef.Add('CacheSize=60000');
 condef.Add('BusyTimeOut=30000');
 condef.Add('Pooled=True');
 condef.Add('POOL_MaximumItems=' + IntToStr(FMaxThreadPoolCount + 1));
 FDManager.AddConnectionDef(ConDefName,'SQLite',condef);

 .
 .
 .
 
 // set fdmanager active
 FDManager.Active := True; 

 .
 .
 .

 //dann kommt das erzeugen der threads


Code aus dem thread:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
var
 oConn: TFDConnection;
begin
 FreeOnTerminate := False;
 oConn := TFDConnection.Create(nil);
 oConn.ConnectionDefName := 'SQLite_Threaded_Pooled';
 try
  oConn.Connected := True;
  while not Terminated do
  begin


  end;
 finally
  oConn.Free;
 end;
end;


zuma - Do 24.07.14 13:56

ich hab keine Ahnung von Firedac, aber könnte es sein,
das du im oberen Beispiel deine Connection 'SQLite' mit ConDefName (was auch immer drin steht) benennst
und im unteren Beispiel aber versuchst, eine Connection mit dem Namen 'SQLite_Threaded_Pooled' zu verwenden ?
evtl. besser immer ConDefName nutzen ?
klingt für mich so, als könne man da mehrere Connections ablegen und
dann wählen (über den Namen der einzelnen Connections), welche man nehmen will ?
falls ich völlig daneben lieg, einfach diesen Post ignorieren :mrgreen:

zuma


jaenicke - Do 24.07.14 13:57

Ich weiß nicht warum du die Verbindungsdefinition per Stringliste hinzufügst. Wir machen das so direkt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
var
  oDef: IFDStanConnectionDef;
begin
  oDef := FDManager.ConnectionDefs.FindConnectionDef('SQLite_Threaded_Pooled');
  if not Assigned(oDef) then
    oDef := FDManager.ConnectionDefs.AddConnectionDef;
  oDef.Name := 'SQLite_Threaded_Pooled';
  oDef.DriverID := 'SQLite';
  oDef.Database := FResultDataBaseFileName;
  oDef.UserName := ...;
  oDef.Password := ...;
  oDef.Apply;


user profile iconzuma hat folgendes geschrieben Zum zitierten Posting springen:
ich hab keine Ahnung von Firedac, aber könnte es sein,
das du im oberen Beispiel deine Connection 'SQLite' mit ConDefName (was auch immer drin steht) benennst
und im unteren Beispiel aber versuchst, eine Connection mit dem Namen 'SQLite_Threaded_Pooled' zu verwenden ?
SQLite ist der Name des Treibers.


NOS1971 - Do 24.07.14 17:07

HI,

ok .. habe ich so umgebaut .. so ist die verbindung nicht mehr provate sondern persistent ... aber der fehler lag woander ...

Viele dank :-)


NOS1971 - Do 24.07.14 17:26

Das funktioniert soweit einwandfrei ....

nun bleibt nur noch das problem das wenn ich mehr als einen thread erzeuge die threads nicht mehr korrekt beendet werden und das proggi komplett im nirvana hängt .... ich habs ja shcon aus dem websitespider rausgenommen um mich diesem problem zu widmen aber es haut nicht hin

vielleicht seht ihr hier die kleinigkeit die das problem verursacht ... und ich tippe mal dass es auch ne kleinigkeit ist

hier das hauptobjekt


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
{ TMultiThreadedWebAnalyser }

constructor TMultiThreadedWebAnalyser.Create;
begin
  inherited Create;
 // create variables, structures and objects
 ResultURLDictionary := TObjectDictionary<string, TURLNodeTreeData>.Create([doOwnsValues]);
 ProgressData := TProgressData.Create;
end;

destructor TMultiThreadedWebAnalyser.Destroy;
begin
 // empty dictionary
 ResultURLDictionary.Free;
 ResultURLDictionary := nil;
 // empty and free progressdata
 FProgressData.Free;
 FProgressData := nil;
  inherited;
end;

procedure TMultiThreadedWebAnalyser.Pause;
begin

end;

procedure TMultiThreadedWebAnalyser.Reset;
begin
 // prepare dictionary
 if ResultURLDictionary <> nil then
 begin
  ResultURLDictionary.Free;
  ResultURLDictionary := nil;
 end;
 ResultURLDictionary := TObjectDictionary<string, TURLNodeTreeData>.Create([doOwnsValues]);
 // set variables;
 FCancelled := false;
 FAnalyserStatus := asIdle;
 FMaxThreadPoolCount := 10;
 FProgressData.Added := 0;
 FProgressData.Analysed := 0;
 FProgressData.ActiveThreads := 0;
 FProgressData.Processed := 0;
end;

procedure TMultiThreadedWebAnalyser.Resume;
begin

end;

procedure TMultiThreadedWebAnalyser.Start;
var
 threadcounter: integer;
 condef: IFDStanConnectionDef;
begin
 // set analyser state
 FAnalyserStatus := asBusy;
 // set cancel flag
 FCancelled := false;
 // check if connection definition exists
 condef := FDManager.ConnectionDefs.FindConnectionDef(ConDefName);
 if not Assigned(condef) then
  condef := FDManager.ConnectionDefs.AddConnectionDef;
 condef.Name := ConDefName;
 condef.DriverID := 'SQLite';
 condef.Database := FResultDataBaseFileName;
 condef.Pooled := true;
 condef.UserName := '';
 condef.Password := '';
 condef.Params.Add('SharedCache=False');
 condef.Params.Add('LockingMode=Normal');
 condef.Params.Add('Synchronous=Full');
 condef.Params.Add('LockingMode=Normal');
 condef.Params.Add('CacheSize=60000');
 condef.Params.Add('BusyTimeOut=30000');
 condef.Params.Add('POOL_MaximumItems=' + IntToStr(FMaxThreadPoolCount + 1));
 condef.Apply;
 // set fdmanager active
 FDManager.Active := True;
 // set lenght of threadpool array
 SetLength(FURLAnalyserThreadPool,FMaxThreadPoolCount);
 // now create each thread
 for threadcounter := 0 to FMaxThreadPoolCount - 1 do
 begin
  // create thread
  FURLAnalyserThreadPool[threadcounter] := TURLAnalyser.Create;
 end;
end;

procedure TMultiThreadedWebAnalyser.Stop;
var
 threadcounter: integer;
begin
 // close connection pool
 FDManager.CloseConnectionDef(ConDefName);
 // terminate each thread
 for threadcounter := FMaxThreadPoolCount - 1 downto 0 do
 begin
  // create thread
  TURLAnalyser(FURLAnalyserThreadPool[threadcounter]).Terminate;
 end;
 // now wait for termination and free each thread
 for threadcounter := FMaxThreadPoolCount - 1 downto 0 do
 begin
  // create thread
  TURLAnalyser(FURLAnalyserThreadPool[threadcounter]).WaitFor;
  TURLAnalyser(FURLAnalyserThreadPool[threadcounter]).Free;
 end;
 // set analyser state
 FAnalyserStatus := asIdle;
end;


und hier der code des/der threads


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
{ TURLAnalyser }

procedure TURLAnalyser.Execute;
var
 oConn: TFDConnection;
begin
 FreeOnTerminate := False;
 oConn := TFDConnection.Create(nil);
 oConn.ConnectionDefName := ConDefName;
 try
  oConn.Connected := True;
  while not Terminated do
  begin


  end;
 finally
  oConn.Free;
 end;
end;


NOS1971 - So 27.07.14 18:06

Also das Threading an sich ist nun stabil .... allerdings gibt es noch ein paar probleme ...

ich habe nun in jedem thread eine connection und dazu 2 queries .... ist das ok oder darf es nur eine sein ?

ich nutze eine um das aktuelle item aus der db zu holen welches ich bearbeite ... das halte ich offen und mache eine analyse ... am ende der analyse aktualisiere ich es ... in einer procedure die aus der analyse aufgerufen wird und zum thread gehört adde ich über eine zweite wuery daten in die db

weiterhin stelle ich fest das trotz laufender threads nur einer einen gültigen datensatz zum bearbeiten aus der db holt ... als wenn der rest geblockt ist ...

any idea ?


hk - So 03.08.14 16:43

user profile iconNOS1971 hat folgendes geschrieben Zum zitierten Posting springen:

ich habe nun in jedem thread eine connection und dazu 2 queries .... ist das ok oder darf es nur eine sein ?


Hallo. Eine Connection je Thread ist richtig, siehe auch
http://docwiki.embarcadero.com/RADStudio/XE6/de/Multithreading_%28FireDAC%29
"Die Standardvereinfachung besteht im Erstellen und Verwenden eines zugehörigen Verbindungsobjekts zur Arbeit mit der Datenbank für jeden Thread. In diesem Fall ist keine zusätzliche Serialisierung erforderlich."

Ich habe auch so meine Probleme beim ersten Umgang mit FireDAC. Zum Beispiel


Delphi-Quelltext
1:
2:
3:
4:
if FDManager.IsConnectionDef('MSSQL_Connection'then
  FDManager.DeleteConnectionDef('MSSQL_Connection');
if FDManager.IsConnectionDef('MSSQL_Connection'then
  showmessage('immer noch vorhanden');  // => und es ist noch vorhanden!


Gruß, hk


NOS1971 - So 03.08.14 17:13

also bei mir will das einfach nicht hinhauen und ich habe wirklich keinen plan warum das mit dem pooling nicht funktioniert.

Das mit den Conectiondefinitions habe ich so gemacht


Delphi-Quelltext
1:
2:
var
 condef: IFDStanConnectionDef;



Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
 // close fdmanager
 FDManager.Close;
 // check if connection definition exists
 condef := FDManager.ConnectionDefs.FindConnectionDef(ConDefName);
 if not Assigned(condef) then
  condef := FDManager.ConnectionDefs.AddConnectionDef;
 // add connection definition
 condef.Name := ConDefName;
 condef.DriverID := 'SQLite';
 condef.Database := FResultDataBaseFileName;
 condef.Pooled := true;
 condef.UserName := '';
 condef.Password := '';
 condef.Params.Add('SharedCache=False');
 condef.Params.Add('LockingMode=Normal');
 condef.Params.Add('Synchronous=Full');
 condef.Params.Add('LockingMode=Normal');
 condef.Params.Add('CacheSize=60000');
 condef.Params.Add('BusyTimeOut=30000');
 condef.Params.Add('POOL_MaximumItems=' + IntToStr(FMaxThreadPoolCount + 1));
 condef.Apply;
 // set fdmanager active
 FDManager.Active := True;


das funktioniert super ... ich habe so das gefühl das das schreiben aus einem query im thread über die connection des threads probleme macht solange das andere query einen datensatz über die gleiche connection offen hält.


hk - So 03.08.14 17:24

user profile iconNOS1971 hat folgendes geschrieben Zum zitierten Posting springen:

ich habe so das gefühl das das schreiben aus einem query im thread über die connection des threads probleme macht solange das andere query einen datensatz über die gleiche connection offen hält.


Greifen die beide Queries auf dieselbe Tabelle zu? Dann könnte es wohl Probleme geben.
Ich muss mich aber auch erst noch in FireDAC einarbeiten.


NOS1971 - So 03.08.14 19:20

Ja .... beide Queries greifen auf die selbe tabelle zu ...

es ist eine Webseitenanalysesoftware und der kern, also quasi der crawler, ist multithreaded .... so hole ich mir quasi mit der einem query des threads die nächste zu analysierende url aus der tabelle und halte den datensatz offen während die andere query die daten der gefundenen urls in die tabelle schreibt.

da habe ich auch noch keine wirklich funktionierende lösung gefunden ... auch ohne firedac lief das nicht besonders sauber ... ich dachte eigentlich es wird durch die query nur der datensatz geblockt und nicht die tabelle.