AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Datenbanken DataSnap mit mehreren Datenbanken
Thema durchsuchen
Ansicht
Themen-Optionen

DataSnap mit mehreren Datenbanken

Ein Thema von Kostas · begonnen am 26. Mär 2015 · letzter Beitrag vom 28. Mär 2015
Antwort Antwort
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.203 Beiträge
 
Delphi 12 Athens
 
#1

AW: DataSnap mit mehreren Datenbanken

  Alt 26. Mär 2015, 17:24
Jupp, daher machen wir das übertragen der DataSets halt "manuell".

CriticalSections und Dergleichen bekommt man nicht in den Code, da die Daten erst nach Ende der Servermethoden übertragen werden und bei DataSets auch noch stückchenweise. (nur so viel, wie der Client grade braucht)
Daher erstellen wir und ein ClientDataSet/MemDataset, nutzen den DataSnapReader zum Kopieren der FeldStruktur und der Inhalte,
dann wird das DataSet übertragen und dem DataSnap wird mitgeteilt, daß es Owner über die Kopie ist ... wird dann von dem freigegeben, wenn fertig.

So können wir die Zugriffe problemlos selber synchronisieren.



Aber wem das zuviel ist, der legt die DataSets/Queries auf die DataSnapModule, lässt für jeden Clienten eine eigene Instanz erstellen. Die Connection kommt da auch mit drauf, wenn sie kein Multithread (Connectionpooling) kann ... dann ist jeder Thread alleine für sich und es gibt auch keine Probleme mehr (außer vielleicht beim RAM)

DataSnap ist (zumindestens im XE) erstaunlich resourcenhungrig, so daß schnell der RAM voll ist, vorallem in einem 32-Bit-Programm.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.371 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: DataSnap mit mehreren Datenbanken

  Alt 26. Mär 2015, 19:25
DataSnap ist (zumindestens im XE) erstaunlich resourcenhungrig, so daß schnell der RAM voll ist, vorallem in einem 32-Bit-Programm.
Wir benutzen Datasnap ebenfalls mit einer FireDAC Connection pro Thread. Bei uns geht es um Systeme mit mehreren hundert parallel verbundenen Clients. Da haben wir relativ schnell auf 64-Bit umgestellt, alles andere machte gar keinen Sinn. Bei etwa 60 Clients mit diversen abgefragten Datenbanken gab es zwar keine Leistungsprobleme, aber der Speicher war schlicht bei 32-Bit voll. Bei 150 verbundenen Clients waren wir dann schon z.B. bei 4,5 GiB RAM (und der SQL Server bei 10 GiB). Zum Glück ist das heute keinerlei Problem mehr...
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!
  Mit Zitat antworten Zitat
Kostas

Registriert seit: 14. Mai 2003
Ort: Gerstrhofen
1.063 Beiträge
 
Delphi 10 Seattle Enterprise
 
#3

AW: DataSnap mit mehreren Datenbanken

  Alt 27. Mär 2015, 12:00
Vielen vielen Dank für die ausführlichen Infos.

habe ich das jetzt wirklich richtig verstanden, wenn der DataSnap Server als 32Bit kompiliert wird,
benötigt DataSnap mehr Speicher als wenn er als 64Bit kompiliert werden würde? Wenn ja, wie hängt das zusammen?


Zum eigentlichen Problem, wenn bei 150 User "nur" 15-20GB RAM verbraten werden, würde mich
das noch nicht aus der Ruhe bringen. Den RAM kann ich wirklich ignorieren.

Mein DataSnap Server wird jetzt nicht so umfangreich aber ein paar Dutzend Querys werden das schon. Ich wollte einfach nur die Querys thematisch auf mehrere DataModule verteilen wegen der Wartbarkeit. Bei Anwendungen mit ein paar hundert Querys alles auf das ServerMethods Datamodul zu packen ist zwar möglich aber eben danach nur sehr schwer wartbar.

In diesem Projekt mit den drei Datenbanken habe ich eh drei TDSServerClass die auch drei ServerMethod Klassen registriert, und wie ich das Verstanden habe bei LifeCycle=Session werden alle drei
ServerMethod Module instanziiert pro Session. Der Zugriff von den Public ServerMethods auf die Querys
währe damit sichergestellt.


Wäre es denkbar dass man nur wegen der Wartbarkeit weitere ServerMethods DataModule anlegen und die
Querys sachlich trennt, sie würde ja alle mit instanziiert pro Session?


Eigentlich wollte ich in den ServerMethods ausschließlich Methoden für die Clients bereitstellen.
Die sonstige Logik sollte eben auch mehrere weitere DataModuls aufgeteilt werden. Aktuell funktioniert
es so aber ihr meint, es wird Probleme geben.


Wie könnte ich bitte vom ServerMethods ThreadSave auf meine Methode im DataModul zugreifen,
etwa so? Das wäre zu einfach

Delphi-Quellcode:

function TdmServerMethodsZMI.ZMI_GetBauDataSetThreadSave(BauNr: integer): TDataSet;
var
  LDataSet: TDataSet;
begin
  TThread.Synchronize(nil, procedure
    begin
      LDataSet := dmDALZMI.GetBauDataSet(BauNr);
    end);
  Result := LDataSet;
end;



Aktuell habe ich in ein DataModul das:
Delphi-Quellcode:
function TdmDALZMI.GetBauDataSet(BauNr:integer): TDataSet;
begin
  qrGetBau.Active := False;
  qrGetBau.Params[0].Value := BauNr;
  qrGetBau.Active := True;
  result := qrGetBau;
end;
Im ServerMethods DataModul das:

Delphi-Quellcode:
function TdmServerMethodsZMI.ZMI_GetBauDataSet(BauNr: integer): TDataSet;
begin
  result := dmDALZMI.GetBauDataSet(BauNr);
end;




Gruß Kostas
  Mit Zitat antworten Zitat
mkinzler
(Moderator)

Registriert seit: 9. Dez 2005
Ort: Heilbronn
39.851 Beiträge
 
Delphi 11 Alexandria
 
#4

AW: DataSnap mit mehreren Datenbanken

  Alt 27. Mär 2015, 12:09
Zitat:
habe ich das jetzt wirklich richtig verstanden, wenn der DataSnap Server als 32Bit kompiliert wird,
benötigt DataSnap mehr Speicher als wenn er als 64Bit kompiliert werden würde? Wenn ja, wie hängt das zusammen?
Nein, aber man kann weniger Speicher adressieren
Markus Kinzler
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.203 Beiträge
 
Delphi 12 Athens
 
#5

AW: DataSnap mit mehreren Datenbanken

  Alt 27. Mär 2015, 13:28
Jupp, mit 32 Bit ist nach 2 GB Schluß (3,8 GB mit entsprechenden Hacks und wenn alles dafür kompatibel ist)

Zitat:
Delphi-Quellcode:
function TdmServerMethodsZMI.ZMI_GetBauDataSetThreadSave(BauNr: integer): TDataSet;
var
  LDataSet: TDataSet;
begin
  TThread.Synchronize(nil, procedure
    begin
      LDataSet := dmDALZMI.GetBauDataSet(BauNr);
    end);
  Result := LDataSet;
end;
Hier ist nur der Zugriff auf GetBauDataSet abgesichert, aber der Zugriff auf das DataSet ist nicht sicher, da der erst nach dem Function-End im Code vom DataSnap für eine ungewisse Dauer auftritt. (mindestens solange die Übertragung dauert und maximal, solange die Kopie im Client existiert und eine Verbindung zum Apps aufrecht erhält ... z.B. Delayed-Loading oder eine bidirektionale Verbindung)

Drum auch meine Lösung mit dem umkopieren. Das kopierte DataSet wird dann nur für diesen einen Aufruf verwendet und ist somit threadsave (wenn die verwendete TDataSet-Implementation nicht dennoch irgendwelche threadunsicheren globalen Dinge veranstaltet)



Grundsätzlich sicher ist nur, wenn sich DS ums Freigeben kümmert, wenn es nichts Globales im Apps gibt (jede Connection bekommt was Eigenes), bzw. wenn man alles Globales selber "vollständig" absichern kann, und wenn die DataSets auch nicht global sind, sondern werden immer neu erstellt, für die jeweilige Anfrage.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests

Geändert von himitsu (27. Mär 2015 um 13:32 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.052 Beiträge
 
Delphi 12 Athens
 
#6

AW: DataSnap mit mehreren Datenbanken

  Alt 27. Mär 2015, 14:02
Mein DataSnap Server wird jetzt nicht so umfangreich aber ein paar Dutzend Querys werden das schon. Ich wollte einfach nur die Querys thematisch auf mehrere DataModule verteilen wegen der Wartbarkeit. Bei Anwendungen mit ein paar hundert Querys alles auf das ServerMethods Datamodul zu packen ist zwar möglich aber eben danach nur sehr schwer wartbar.

In diesem Projekt mit den drei Datenbanken habe ich eh drei TDSServerClass die auch drei ServerMethod Klassen registriert, und wie ich das Verstanden habe bei LifeCycle=Session werden alle drei
ServerMethod Module instanziiert pro Session. Der Zugriff von den Public ServerMethods auf die Querys
währe damit sichergestellt.


Wäre es denkbar dass man nur wegen der Wartbarkeit weitere ServerMethods DataModule anlegen und die
Querys sachlich trennt, sie würde ja alle mit instanziiert pro Session?


Eigentlich wollte ich in den ServerMethods ausschließlich Methoden für die Clients bereitstellen.
Die sonstige Logik sollte eben auch mehrere weitere DataModuls aufgeteilt werden. Aktuell funktioniert
es so aber ihr meint, es wird Probleme geben.
Du kannst die Querys problemlos auf mehrere TDataModule-Klassen (nicht ServerMethods-Module!) aufteilen. Du musst diese halt jeweils zusammen mit der Instanz des ServerMethod-Moduls instantiieren und eine Connection thread-sicher (z.B. über den Connection-Pool wie in dem verlinkten Artikel) zuweisen. Damit entfällt halt die (leider zu bequeme) Möglichkeit, die Verknüpfungen der Komponenten zwischen verschiedenen Datenmodulen schon während des Designs herzustellen. Es muss halt für jede Instanz eines ServerMethod-Moduls auch der ganze Rattenschwanz der Query-Datenmodule instanziert werden.

Wenn du ein TDSServerModule verwendest und mit TDataSetProvider-Instanzen arbeitest, kannst du die jeweiligen DataSetProvider auf den zusätzlichen Datenmodulen über RegisterProvider dem TDSServerModule bekannt machen. Dann sind die im Client hinterher auch wiederzufinden.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Kostas

Registriert seit: 14. Mai 2003
Ort: Gerstrhofen
1.063 Beiträge
 
Delphi 10 Seattle Enterprise
 
#7

AW: DataSnap mit mehreren Datenbanken

  Alt 27. Mär 2015, 14:10
Hallo himitsu,

oh man das klinkt wirklich kompliziert.
Wäre das eine Option?

Delphi-Quellcode:
function TdmServerMethodsZMI.ZMI_GetBauDataSetThreadSave(BauNr: integer): TDataSet;
var
  LDataSet: TDataSet;
begin
  TThread.Synchronize(nil, procedure
    var qrGetBau: TFDQuery;
    begin
      try
        qrGetBau:= TFDQuery.Create(nil);
        qrGetBau.Connection := dmZIMPool.conZMI;

        qrGetBau.SQL.Add('SELECT *');
        qrGetBau.SQL.Add('FROM BAU');
        qrGetBau.SQL.Add('WHERE BAUNR = :BAUNR');
        qrGetBau.Params[0].AsInteger := BauNr;

        qrGetBau.open;
        LDataSet := qrGetBau;
      finally
// qrGetBau.Free;
      end;
    end);
  Result := LDataSet;
end;

Geändert von Kostas (27. Mär 2015 um 15:13 Uhr)
  Mit Zitat antworten Zitat
Kostas

Registriert seit: 14. Mai 2003
Ort: Gerstrhofen
1.063 Beiträge
 
Delphi 10 Seattle Enterprise
 
#8

AW: DataSnap mit mehreren Datenbanken

  Alt 27. Mär 2015, 14:15
[QUOTE=Uwe Raabe;1295081]

Du kannst die Querys problemlos auf mehrere TDataModule-Klassen (nicht ServerMethods-Module!) aufteilen. Du musst diese halt jeweils zusammen mit der Instanz des ServerMethod-Moduls instantiieren und eine Connection thread-sicher (z.B. über den Connection-Pool wie in dem verlinkten Artikel) zuweisen. Damit entfällt halt die (leider zu bequeme) Möglichkeit, die Verknüpfungen der Komponenten zwischen verschiedenen Datenmodulen schon während des Designs herzustellen. Es muss halt für jede Instanz eines ServerMethod-Moduls auch der ganze Rattenschwanz der Query-Datenmodule instanziert werden.

Wenn du ein TDSServerModule verwendest und mit TDataSetProvider-Instanzen arbeitest, kannst du die jeweiligen DataSetProvider auf den zusätzlichen Datenmodulen über RegisterProvider dem TDSServerModule bekannt machen. Dann sind die im Client hinterher auch wiederzufinden.
Hallo Uwe,

das ist doch schon mal ein Lichtblick.
Warum gibt es keine Bücher über DataSnap. Das ist doch ein grosses Thema.
Dankeschön Uwe.

Gruß Kostas
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.203 Beiträge
 
Delphi 12 Athens
 
#9

AW: DataSnap mit mehreren Datenbanken

  Alt 27. Mär 2015, 17:50
Nach dem End geht dann DataSnap daher und läuft über das DataSet, daher ist alles darin, eventuell dennoch nicht threadsicher.

siehe z.B. Optionen ala Delphi-Referenz durchsuchenTFDAutoFetchAll, wo nach dem Open der DB-Zugrif noch nicht beendet sein kann.

Erstmal das Prinzip, ohne irgendeine Synchronisation (Synchronize oder CriticalSection), was man für threadsichere Dinge ala TFileStream benutzen kann.
Delphi-Quellcode:
function TdmServerMethodsZMI.ZMI_GetBauDataSetThreadSave(BauNr: Integer): TDataSet;
begin
  Result := TFDQuery.Create(nil);
  try
    Result.Connection := dmZIMPool.conZMI;
    Result.SQL.Add('SELECT *');
    Result.SQL.Add('FROM BAU');
    Result.SQL.Add('WHERE BAUNR = :BAUNR');
    Result.Params[0].AsInteger := BauNr;
    Result.Open;
  except
    FreeAndNil(Result);
    raise;
  end;
end;
Innerhalb mußt du selber die Sicherheit für deine Ressourcen sorgen
und nach dem End übernimmt das DataSnap (wenn es der Owner ist/wird)

Wenn man alles gekapselt hat, dann ginge es auch direkt, da Constructoren sich intern selber absicher. (wenn es knallt, wird automatisch Free Destroy aufgerufen)
Delphi-Quellcode:
function TdmServerMethodsZMI.ZMI_GetBauDataSetThreadSave(BauNr: Integer): TDataSet;
begin
  Result := TMyQuery.Create(nil, dmZIMPool.conZMI, 'SELECT * FROM BAU WHERE BAUNR = :BAUNR', [BauNr]);
end;
Mit einem Synchronize kann man sich überlegen wie man das absicher. (ersteres, wenn man blind davon ausgeht, daß der Rückweg vom Synchronize immer funktioniert)
Delphi-Quellcode:
function TdmServerMethodsZMI.ZMI_GetBauDataSetThreadSave(BauNr: Integer): TDataSet;
var
  LDataSet: TDataSet;
begin
  TThread.Synchronize(nil, procedure
    begin
      LDataSet := TFDQuery.Create(nil);
      try
        LDataSet.Connection := dmZIMPool.conZMI;
        LDataSet.SQL.Add('SELECT *');
        LDataSet.SQL.Add('FROM BAU');
        LDataSet.SQL.Add('WHERE BAUNR = :BAUNR');
        LDataSet.Params[0].AsInteger := BauNr;
        LDataSet.Open;
      except
        LDataSet.Free;
        raise;
      end;
    end);
  Result := LDataSet;
end;
Delphi-Quellcode:
function TdmServerMethodsZMI.ZMI_GetBauDataSetThreadSave(BauNr: Integer): TDataSet;
var
  LDataSet: TDataSet;
begin
  LDataSet := nil;
  try
    TThread.Synchronize(nil, procedure
      begin
        LDataSet := TFDQuery.Create(nil);
        LDataSet.Connection := dmZIMPool.conZMI;
        LDataSet.SQL.Add('SELECT *');
        LDataSet.SQL.Add('FROM BAU');
        LDataSet.SQL.Add('WHERE BAUNR = :BAUNR');
        LDataSet.Params[0].AsInteger := BauNr;
        LDataSet.Open;
      end);
  except
    LDataSet.Free;
    raise;
  end;
  Result := LDataSet;
end;
PS: Create gehört natürlich grundsätzlich immer vor das Try, denn wenn es im Create knallt, dann ist im Finally/Except die Variable nicht initialisiert,
was einem der Compiler aber auch sagt, wenn man mal liest was der zu sagen kannt.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests

Geändert von himitsu (27. Mär 2015 um 21:10 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#10

AW: DataSnap mit mehreren Datenbanken

  Alt 27. Mär 2015, 19:38
@himitsu

Warum ein try .. except wo ein try .. finally reicht?
Vor allem im letzten Beispiel lieferst du bei einer Exception eine ungültige Referenz zurück.

So ist es sauber:
Delphi-Quellcode:
LDataSet := TFDQuery.Create;
try
  ...
  Result := LDataSet;
  LDataSet := nil;
finally
  LDataSet.Free;
end;
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:36 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz