Einzelnen Beitrag anzeigen

Benedikt Magnus

Registriert seit: 6. Jul 2012
Ort: Bonn
190 Beiträge
 
FreePascal / Lazarus
 
#1

Speicherleck mit SqlDB (und Indy-TCP-Server) bei Datenbankabfrage

  Alt 29. Feb 2016, 16:29
Datenbank: MySQL • Version: 5.5 • Zugriff über: SqlDB
Erstmal hallo zusammen,

normalerweise durchforste ich, wenn ich beim Programmieren nicht weiterkomme, solange die Delphipraxis, Google etc., bis ich das Problem gelöst habe. Daher ist dies seit fast vier Jahren, da ich einen Account habe, auch meine erste Frage. Seit Tagen schlage ich mich damit rum und komme einfach nicht weiter!

Das sind die Gegebenheiten:

Ich programmiere unter Lazarus (genau Typhon) für Linux x64 einen Spielserver für ein Spiel, das ich vor einiger Zeit programmiert habe. Dieser Server nutzt den TCP-Server der Indykomponenten. Die Klienten (IdTCPClient) verbinden sich nun mit diesem Server, der daraufhin für jede Verbindung eine Datenbankverbindung per SqlDB zu meiner MySQL-Datenbank auf demselben Hostsystem anlegt. Dies geschieht über drei Komponenten (TMySQL55Connection, TSQLTransaction, TSQLQuery), die erstellt und in einem Record zusammengefasst werden (Firstem ist der Name des Spiels):
Delphi-Quellcode:
type
  PFiRe = ^TFirstemRecord;
  TFirstemRecord = record
    Name: String;
    Spiel: String;
    Angemeldet: Boolean;
    Verbindung: TMySQL55Connection;
    Anfrage: TSQLQuery;
    Transaktion: TSQLTransaction;
    Context: TIdContext;
  end;
Der Zeiger auf dieses Record wird im Tag der Verbindung zum Client gespeichert. Das Ganze sieht dann so aus:
Delphi-Quellcode:
procedure TFirstemServer.TCPServerConnect(AContext: TIdContext);
var
  A: PFiRe;
begin
  //Anmkerkung an die Delphipraxis: Die Verbindung zwischen Client und Server findet per SSL(IdServerIOHandlerSSLOpenSSL) verschlüsselt statt.
  if (AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase) then
    TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough := false;

  Writeln('Klient verbunden: ' + AContext.Binding.PeerIP + ':' + IntToStr(AContext.Binding.PeerPort));

  AContext.Connection.Tag := Int64(New(PFiRe));
  A := PFiRe(AContext.Connection.Tag);
  A^.Context := AContext;
  A^.Angemeldet := false;

  //MySQL-Verbindungskomponente erzeugen:
  A^.Verbindung := TMySQL55Connection.Create(nil);

  with A^.Verbindung do
  begin
    HostName := SQL_Adresse;
    DatabaseName := SQL_Datenbankname;
    UserName := SQL_Nutzername;
    Password := SQL_Nutzerpasswort;
    CharSet := 'UTF8';
  end;

  //SQL-Transaktionskomponente erzeugen:
  A^.Transaktion := TSQLTransaction.Create(nil);

  //SQL-Querykomponente erzeugen:
  A^.Anfrage := TSQLQuery.Create(nil);
  
  A^.Anfrage.UniDirectional := true; //Soll angeblich ein Speicherleck in Bezug auf das Caching verhindern -> Pustekuchen.

  //Zuweisungen:
  A^.Verbindung.Transaction := A^.Transaktion;
  A^.Transaktion.DataBase := A^.Verbindung;
  A^.Anfrage.DataBase := A^.Verbindung;
  A^.Anfrage.Transaction := A^.Transaktion;
end;
Nun zu meinem eigentlichen Problem:

Wenn ein Klient sich verbindet und die Verbindung kurz darauf wieder trennt, ohne eine Aktion ausgeführt zu haben, führt dies zu keinerlei Speicherlecks. Wenn er aber dann z.B. eine Anmeldung durchführt, woraufhin der Server die Zugangsdaten aus der MySQL-Datenbank überprüft, erzeugt dies merkwürdige Speicherlecks (die ich gleich näher erläutern werde). Hier der Quelltext, bei dem das Speicherleck erzeugt wird:
Delphi-Quellcode:
with A^.Anfrage do
begin
  SQL.Text := 'SELECT passwort FROM nutzer WHERE name = :NAME';
  Params.ParamByName('NAME').AsString := Nutzername;
  Open;
  Kennwort_db := FieldByName('passwort').AsString;
  Close;
end;
Lasse ich dieses Stück draußen, entstehen ebenfalls keine Speicherlecks. Und ich verstehe nicht warum.

Nun um das Ganze noch verrückter zu machen:
Die Speicherlecks werte ich mithilfe der Heaptrc-Unit von Lazarus aus und erhalte folgendes Ergebnis:
Code:
Call trace for block [Wechselnde Adresse] size [immer um die 30]
  [$48153B] line 185 of source/IdContext.pas
  [$4F53C1] line 136 of source/IdTask.pas
  [$4DEC7D] line 697 of source/IdThread.pas
  [$4DDFCF] line 428 of source/IdThread.pas
Und das Ganze 23 mal. Manchmal steht die letzte Zeile doppelt dort. Indyversion ist 10.6.2.20.
Die Zeilen führen alle letztlich zu einer abstrakten Run-Methode und einem Boolean.
Warum löst eine SQL-Verbindung Speicherlecks in Indy aus?


Ich hoffe, ihr könnt mir helfen.

Benedikt
  Mit Zitat antworten Zitat