Entwickler-Ecke

Datenbanken - Sich füllende DB Tabelle in DBGrid aktuell anzeigen


NOS - Fr 23.09.16 15:12
Titel: Sich füllende DB Tabelle in DBGrid aktuell anzeigen
Hallo zusammen,

ich habe, wie einige schon in dem ein und anderen Post von mir gelesen haben, einen WebsiteAnalyser in Arbeit. Ich crawle also Multithreaded die URL's und speichere die Analysedaten für jede URL in die DB. Zu aktuellen Anzeige habe ich bisher einen TVirtualStringTree genommen was auch super geklappt hat.

Nun möchte ich dem User die Möglichkeit geben hier auch eine URL zu suchen oder zu filtern. Nun stellt sich mir die Frage ob ich:

1. Ein DBGrid nehmen könnte welches sich zur Laufzeit aktualisiert

2. Den Progress während der Analyse ohne Tabelle gestalte und nur die Ergebnistabelle zum Schluss in ein DBGrid lade

3. Filterfunktionalität und Suche zum TVirtualStringTree hinzufügen

Mich würde eure Meinung interessieren und natürlich auch Infos zu Punkt 1 falls dies gehen sollte.

Threadobjekte sind selbstgecodet und als DB nutze ich Firebird embedded mit Connectionpooling.

Ich freue mich auf eure Antworten,

Grüße und Dank,

Andreas


Delete - Fr 23.09.16 22:57

- Nachträglich durch die Entwickler-Ecke gelöscht -


NOS - Sa 24.09.16 19:34

Ok .... dann werde ich das mal so ausprobieren ... vielen Dank :-)


NOS - So 25.09.16 01:47

Vom Prinzip her hat es geklappt .... aber seitdem friert mein gui immer ein obwohl die abfrage im async mode ist ... gibt es da noch etwas zu beachten wenn X Threads in die db schreiben und ich gleichzeitig den progress mit ner connection anzeigen will ?


Delete - So 25.09.16 11:35

- Nachträglich durch die Entwickler-Ecke gelöscht -


NOS - So 25.09.16 11:49

Es friert nach einer Zeit ein ... ich denke es liegt daran dass das eine Refresh noch nicht fertig ist und ich es erneut calle ... gibt es ne Möglichkeit darauf zu warten ???

Es ist so dass es zwischen 1 und 100 Threads gibt die jeweils einen Quellcode von einer URL downloaden, parsen und die ergebnisse nebst der neuen URLs in die DB schreiben. Aus der DB gelesen wird nicht ausser durch die connection / query die die liste aktualisieren soll.

Es ist auch völlig egal ob ich mit einem oder mit 10 threads analysiere


Delete - So 25.09.16 23:53

- Nachträglich durch die Entwickler-Ecke gelöscht -


NOS - Mo 26.09.16 10:06

Moin .... also das ganze Multithreadinggebilde mit idHTTP etc. ist vollkommen ok und läuft stabil.

Wenn ich das Refreshen der Hauptansicht auskommentiere (also das .Refresh des DataSet des DBGrid) läuft auch alles problemlos, sowie vorher mit dem VirtualStringTree auch. Es muss also am Refresh der Hauptansicht liegen die ich eingebaut habe.


jaenicke - Mo 26.09.16 11:17

Der VirtualStringTree kann doch schon selbst filtern. Einfach nur durchiterieren und Filtered für die einzelnen Nodes setzen. Das geht blitzschnell, selbst bei vielen tausend Einträgen.


NOS - Mo 26.09.16 12:09

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Der VirtualStringTree kann doch schon selbst filtern. Einfach nur durchiterieren und Filtered für die einzelnen Nodes setzen. Das geht blitzschnell, selbst bei vielen tausend Einträgen.



Also ist das die bessere Variante als das mit einem DBGrid zu versuchen ?


jaenicke - Di 27.09.16 05:49

In Anbetracht der Tatsache, dass das ja ansonsten schon so funktioniert, würde ich sagen ja.

Ob es insgesamt die beste Lösung ist, kann ich nicht so aus dem Stegreif sagen. Mit großen Datenmengen habe ich bisher nur mit einer VirtualStringTree gearbeitet.


NOS - Di 27.09.16 09:04

Ok ... dann lasse ich erstmal die finger davon .... habe den source mit dem dbgrid gesichert und widme mich anderen dingen :)


Sinspin - Mi 28.09.16 10:10

Von wo aus wird das Refresh aufgerufen? Nicht aus den WorkerThreads, hoffe ich mal. Das musst Du via Timer auf deinem Form machen. Dann kommen die Refesh auch nacheinander und nicht gleichzeitig.
Intervall kannst Du dann ja Nutzerseitig festlegen lassen.


NOS - Mi 28.09.16 10:42

Die Refreshes kamen vom Mainform aus nem Event den ich aus dem Distrubtorthread gecalled habe ... also nach jeder Änderung ein Call ... aber das ist wohl zu schnell hintereinander


NOS - Mi 28.09.16 11:04

Gibt es denn keine Möglichkeit zu warten bis das erste refresh abgearbeitet wurde und dann ein neues zu machen ? Wäre doch das sinnigste bei konstant anwahsenden datenbanken und multithreading ... oder ?


Nersgatt - Mi 28.09.16 11:56

user profile iconNOS hat folgendes geschrieben Zum zitierten Posting springen:
Die Refreshes kamen vom Mainform aus nem Event den ich aus dem Distrubtorthread gecalled habe ... also nach jeder Änderung ein Call ... aber das ist wohl zu schnell hintereinander


Wenn Du im Thread einen Event auslöst, dann wird der Eventhandler im Threadkontext abgearbeitet!
Lösen kannst Du das, indem Du den Eventhandler mit Synchronize aufrufst. Mit einer anonymen Methoden ist das Probemlos und schnell zu machen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure TMeinThread.DoWeltformelGefunden;
begin

  if assigned(FOnWeltformelGefunden) then
  begin
    Synchronize(procedure
      begin
        FOnWeltformelGefunden;
      end);
  end;

end;


Auf diese Art wird der Eventhandler im Kontext des Mainthreads aufgerufen. Dort kannst Du dann auch problemlos Sachen machen, die Du innerhalb des Threads nicht machen dürftest (Zugriffe auf Steuerelemente zum Beispiel).


NOS - Mi 28.09.16 13:50

@Nersgatt

vielleicht habe ich das falsch ausgedrückt ... also der Distributorthread verteilt die URLs zur Analyse an die URLWorkerthreads. Jedes mal wenn der Distributorsthread neue Arbeit verteilt mache ich ein PostMessage. Im Mainform wird die Message dann empfangen und hier das Refresh des FdQuery ausgeführt.

Mir geht es darum festzustellen ob das Refresh des Query schon abgearbeitet ist oder nicht. Dann so friert irgendwann alles ein weil stängi FdQuery.Refresh gecalled wird.


jaenicke - Mi 28.09.16 13:54

Wenn das Refresh im Hauptthread ausgeführt wird, kann es auch nicht parallel mehrfach ausgelöst werden. Allerdings natürlich schnell hintereinander. Deshalb würde ich dort einen Lastenausgleich einbauen.

Zum Beispiel indem du einen Timer startest und diesen erst das Refresh ausführen lässt. Kommen vor dessen Ausführung noch weitere Refreshanforderungen an, werden diese verschluckt. Das mag nicht die schönste Lösung sein, aber eine einfache.


NOS - Mi 28.09.16 15:37

Ich habe jetzt den TFdQuery.AfterRefesh genommen und ein Flag gesetzt ... somit läuft immer nur ein refresh .... sieht etwas ruckelig aus von den intervallen wenn es aktualisiert aber schonmal stabil bisher :-) kann man die "Flüssigkeit" des refresh beeinflussen oder ist das einfach so ?


Sinspin - Mi 28.09.16 16:39

Verhinderst Du damit jetzt dass, während ein Refresh läuft, weitere Messages gepostet werden können? Denn sonst stapelt es sich ja weiter wie bisher und alles Refreshes werden abgearbeitet auch wenn schon lange der Endstand erreicht und angezeigt ist.


NOS - Mi 28.09.16 16:45

Also die Message an sich kommt in dem Hauptthread an aber dort lässt das Flag nur dann den Refresh zu wenn der vorherige zurückgekommen ist.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TfrmWebSiteAnalyser.UpdateURLList;
begin
 if DomainCrawler.URLListQuery.Active and DomainCrawler.DoRefresh then
 begin
  DomainCrawler.DoRefresh := false;
  DomainCrawler.URLListQuery.Refresh;
 end;
end;


Delete - Do 29.09.16 02:35

- Nachträglich durch die Entwickler-Ecke gelöscht -


NOS - Do 29.09.16 10:10

Hi Frühlingsrolle :-)

Hier ergibt sich bei mir die Problematik dass ich in den Threads mehrere Querys habe die bestimmte Teile der Analyse schreiben um nicht jedes mal, bei nur einer query, das prepare und parsen des sql statements und der params zu haben.

Im Moment scheint es so dass es einen Stack Overflow gibt bei langsamen und älteren rechnern. Ich finde nur keinen Ansatz momentan das Problem zu finden.


jaenicke - Do 29.09.16 10:59

Da sollte der Stacktrace ja weiterhelfen. Den bekommst du z.B. mit MadExcept oder Eurekalog.


NOS - Do 29.09.16 11:38

Hi Jaenicke :-)

probiere ich mal aus


Sinspin - Do 29.09.16 14:14

user profile iconNOS hat folgendes geschrieben Zum zitierten Posting springen:
Im Moment scheint es so dass es einen Stack Overflow gibt bei langsamen und älteren rechnern. Ich finde nur keinen Ansatz momentan das Problem zu finden.

Gibt es wirklich einen Stack Overflow? Ich vermute eher das zu viele Nachrichten kommen. Ich kenne ein paar Fälle bei denen das am Ende dann mit einem Stack Overflow endet.
Problem scheint nach wie vor zu sein dass Du an der falschen Stelle entscheidest ob Refresh geht oder nicht.
Du kannst Events aus deinem Thread schicken die anzeigen dass da noch was läuft. Aber Refresh solltest du wirklich via Timer direkt vom Form aus organisieren. Probiers mal ;-)

€: Krankes Quote geheilt.
€2: Schönheit verbessert.


NOS - Do 29.09.16 14:25

Ok .... checke ich gleich mal ab ob das mit einem timer was bringt


NOS - Do 29.09.16 21:09

user profile iconSinspin hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconNOS hat folgendes geschrieben Zum zitierten Posting springen:
Im Moment scheint es so dass es einen Stack Overflow gibt bei langsamen und älteren rechnern. Ich finde nur keinen Ansatz momentan das Problem zu finden.

Gibt es wirklich einen Stack Overflow? Ich vermute eher das zu viele Nachrichten kommen. Ich kenne ein paar Fälle bei denen das am Ende dann mit einem Stack Overflow endet.
Problem scheint nach wie vor zu sein dass Du an der falschen Stelle entscheidest ob Refresh geht oder nicht.
Du kannst Events aus deinem Thread schicken die anzeigen dass da noch was läuft. Aber Refresh solltest du wirklich via Timer direkt vom Form aus organisieren. Probiers mal ;-)


Also es ist tatsächlich so dass ich durch den Timer keine Stack Overflows mehr habe .... es scheint auch mit dem ResourceOptions.CmdExecTimeout und der Anzahl der Refreshes zu tun zu haben ....
mir ist nur schleierhaft warum es nicht so funzt dass man ein Refresh macht udn danach ein Flag setzt oder was auch immer um festzulegen dass ein neues Refresh gemacht werden kann ... das muss doch gehen


Delete - Do 29.09.16 23:29

- Nachträglich durch die Entwickler-Ecke gelöscht -


NOS - Fr 30.09.16 09:22

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Es muss doch garnicht so umständlich sein. Hast du dich garnicht an meinem Beispiel versucht? :gruebel:


Hi und guten Morgen,

aber sicher habe ich mir dein beispiel angeschaut und auch darauf geantwortet (weiter oben). Zum einen ist der Aufbau bei mir aus Performancegründen anders (mehrere TFdQueries für das schreiben in verschiedene Tabellen) und dazu komt noch das Du die Threads ja im eigentlichen Sinne nicht parallel arbeiten hast sondern nacheinander.

Hier gehts aber um Webseiten die jenseits der 200.000 URL's liegen und die Threads laufen alle parallel mehrere Stunden. Da sieht es schon ganz anders aus.


Delete - Fr 30.09.16 09:58

- Nachträglich durch die Entwickler-Ecke gelöscht -


NOS - Fr 30.09.16 10:12

Die Problematik fängt ja da an wo die Datenmenge zu groß wird. Also wenn du dein Beispiel mal so veränderst dass es permanent schreibt und die threads nicht hintereinander ablaufen wirst du sehen dass das mehr probleme macht denke ich.


jaenicke - Fr 30.09.16 21:13

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Auch wenn du es so machst, so landen die Datensätze nicht gleichzeitig in die DB, sondern nacheinander, wie bei mir.
Doch, bei ihm schreiben mehrere Threads wirklich parallel. Das verzahnt die DB dann natürlich zu einer Sequenz von Einträgen, aber die Daten der einzelnen Threads laufen parallel an der DB auf.


NOS - Fr 30.09.16 21:18

In dem Fall vielleicht schon, aber bei großen Datenmengen wird das doch zeitkritisch oder bin ich da auf dem Holzweg ? Wenn die Threads dauernd daten in die db schreiben sieht es doch schon anders aus. Außerdem wirkt doch das Connectionpooling von Firebird da auch noch mit rein oder bin ich da auf dem Holzweg ?


NOS - Sa 01.10.16 15:32

So ... also abstürzen bzw. Stack Overflows gibt es nun nicht mehr .... der Refresh funktioniert auch jeweils nacheinander .... im Moment teste ich in wie weit das die Analysegeschwindigkeit an sich beeinflusst an einer großen URL (bundestag.de)

Ich habe gesehen dass das DBGrid die Datensätze intern irgendwie umstellt oder sehe ich das falsch ? Wenn ich in dem SQL statement kein order by nutze etc. müsste es doch in der reihenfolge des addens angezeigt werden, also inkrementierte ID ... aber es sieht so aus .... sortiert das grid irgendwo ?


Delete - Sa 01.10.16 16:59

- Nachträglich durch die Entwickler-Ecke gelöscht -


NOS - Sa 01.10.16 17:55

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Das dürfte wohl eher an den Threads liegen, so wie es jaenicke beschrieben hat:
Zitat:
[...] Das verzahnt die DB dann natürlich zu einer Sequenz von Einträgen [...]


Aber die URL 16530 an 28ter Stelle ? Soviele URLs sind zu dem Zeitpunkt der Analyse noch nicht einmal gefunden und zur DB geadded ... die taucht irgendwann während des aktualisierens auf


Delete - Sa 01.10.16 22:56

- Nachträglich durch die Entwickler-Ecke gelöscht -


NOS - Sa 01.10.16 23:12

Werde ich morgen mal checken .... danke und gute nacht :-)