TSQLQuery.open erzeugt ein SegFault (aber nur im Thread)

Für Themen zu Datenbanken und Zugriff auf diese. Auch für Datenbankkomponenten.
Antworten
papayawhip
Beiträge: 3
Registriert: Di 1. Sep 2015, 17:52
OS, Lazarus, FPC: Win Laz1.4 FPC 2.7.1
CPU-Target: x86 / x64
Wohnort: Bielefeld

TSQLQuery.open erzeugt ein SegFault (aber nur im Thread)

Beitrag von papayawhip »

Hallo Leute,

ich bräuchte mal einen Denkanstoß.

In der Software läuft ein TCP/SSL Server, der mit den Synapse-Komponenten gebaut ist.
Wenn in einer solchen Verbindung (ein Thread pro aktiver Verbindung) eine Authentifizierung laufen
soll, holt der Server den Benutzer aus der Datenbank (MySQL - mit TMysql55Connection) und soll die
Daten überprüfen.

Wenn ich aber die Abfrage aus diesem TCP Worker-Thread mache, legt sich der Thread mit einem SigSegv
auf die klappe.

Code: Alles auswählen

0046768C 83e801                   sub    $0x1,%eax
0046768F 761e                     jbe    0x4676af <DB$_$TPARAMS_$__$$_PARSESQL$crcB6516233+1263>
00467691 e9b1000000               jmp    0x467747 <DB$_$TPARAMS_$__$$_PARSESQL$crcB6516233+1415>
00467696 8d45ec                   lea    -0x14(%ebp),%eax
00467699 e852bdf9ff               call   0x4033f0 <fpc_ansistr_unique>
0046769E 8b55b4                   mov    -0x4c(%ebp),%edx
004676A1 c64410ff3f               movb   $0x3f,-0x1(%eax,%edx,1)    <-----------------
004676A6 8345b401                 addl   $0x1,-0x4c(%ebp)
004676AA e998000000               jmp    0x467747 <DB$_$TPARAMS_$__$$_PARSESQL$crcB6516233+1415>
 


Wenn der Code (zum Testen) im Main-Thread ausgeführt wird funktioniert es tadellos.

Der gesamte Ablauf sieht ungefähr so aus:

1. SSL TCP Listener-Thread erzeugen
2. Auf Verbindung warten
3. Für die eingehende Verbindung einen Thread starten
4. Auf Datenpaket warten
5. Datenpaket mit Benutzername und Kennwort erhalten
6. Aus dem Datenbankpool eine freie Verbindung rausholen
7. Query absetzen (Hier knallts dann)
8. Verbindung wieder in den Pool werfen

Code, der die Query enthält:

Code: Alles auswählen

function TDBConnection.exec(q: string; p: TDBParams): TSQLQuery;
var op: string;
begin
  Finuse := true;
 
  Global.logger.trace('Exec SQL: ' + q);
 
  query.SQL.Text := q;
 
  if assigned(p) then
    p.exec(query);
 
  try
    op := uppercase(copy(q, 1, 3));
    if op = 'SEL' then
      query.Open
    else if op = 'INS' then
    begin
      query.ExecSQL;
      query.SQL.Text := 'SELECT LAST_INSERT_ID() as id;';
      query.Open;
      insertid := query.Fields[0].AsInteger;
      query.Close;
    end else
    begin
      query.ExecSQL;
    end;
 
  except
    on e:exception do
      Global.logger.error(e.message);
  end;
 
  result := query;
  Finuse := false;
end;               



Hoffe, jemand hat eine Idee, oder stellt mir die passende Frage :)

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: TSQLQuery.open erzeugt ein SegFault (aber nur im Thread)

Beitrag von Michl »

papayawhip hat geschrieben:oder stellt mir die passende Frage :)
Da fallen mir mehr als ein Dutzend Fragen ein, wobei diese zumeist auf dein Design zielen würden.

Bei dem geposteten Code, stellen sich mir am ehesten folgende Fragen:
- Haben alle Threads eigene Querys und/oder Connections?
- was ist Global.logger.trace? Eine eigene Klasse, die mit Strings arbeitet - sind diese mit Critical Sections ausgestattet (aufgrund "fpc_ansistr_unique")?

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

papayawhip
Beiträge: 3
Registriert: Di 1. Sep 2015, 17:52
OS, Lazarus, FPC: Win Laz1.4 FPC 2.7.1
CPU-Target: x86 / x64
Wohnort: Bielefeld

Re: TSQLQuery.open erzeugt ein SegFault (aber nur im Thread)

Beitrag von papayawhip »

Der Logger(eine Klasse) ist ein eigener Thread und sitzt einer globalen Variable.
Wenn etwas geloggt wird landet das mit critical sections in einer Queue und der Thread schreibt das dann weg.

Die Datenbankverbindungen stehen Anwendungsweit zur Verfügung.
D.h. welches Modul auch immer eine Datenbankverbindung benötigt holt sich eine
aus dem Pool (wobei sie threadsicher gesperrt wird) und arbeitet damit.
Dabei werden immer n-beliebige im Pool aufrecht gehalten um schnell liefern zu können.
Das funktioniert in anderen Anwendungen wunderbar.

Witziger weise ist der Absturz von der Query abhängig.
Alles was ein Ergebnis liefert löst den Segfault bei .open aus.
Wenn man aber eine ganz stumpfe Abfrage macht, wie z.B. "SELECT SLEEP(2);"
dann klappt .open und wartet sogar 2 Sekunden.

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: TSQLQuery.open erzeugt ein SegFault (aber nur im Thread)

Beitrag von Michl »

Das sieht nach meinem Wissensstand sauber aus, wobei ich nicht weiss, ob SQLDB überhaupt so verwendet werden darf.

Als Test könntest du noch probieren jeweils eine eigene Connection und Query im Thread dynamsich zu erstellen.
Außerdem könntest du das Verhalten von Zeos noch gegentesten (SQLDB nutze ich bisher nur zu Testzwecken, im Einsatz habe ich Zeos).

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

papayawhip
Beiträge: 3
Registriert: Di 1. Sep 2015, 17:52
OS, Lazarus, FPC: Win Laz1.4 FPC 2.7.1
CPU-Target: x86 / x64
Wohnort: Bielefeld

Re: TSQLQuery.open erzeugt ein SegFault (aber nur im Thread)

Beitrag von papayawhip »

Ich brech' ins essen...

Habe jetzt nochmal ein wenig debuggt und versucht nachzuvollziehen, wo der Fehler überhaupt alles
auftritt.

Im Listener-Thread-Konstruktor habe ich noch ein wenig Code drin, der das SSL Zertifikat abprüft und
den Namen ausgibt:

Code: Alles auswählen

  bio := BioNew(BioSMem);
  BioWrite(bio, data, Length(data));
 
  cert := PEMReadBioX509(bio, nil, nil, nil);
  SetLength(name, 255);
  X509NameOneline(X509GetSubjectName(cert), name, 255);
  Global.logger.notice('Subject Name: '+name);
  X509NameOneline(X509GetIssuerName(cert), name, 255);
  Global.logger.notice('Issuer Name: '+name);
 
  X509Free(cert);
 
  BioFreeAll(bio)


Ich hatte das "SetLength" vergessen, also hat OpenSSL in irgendeinen Speicher geschrieben - ist nur keinem aufgefallen und hat sogar funktioniert.
Anscheinend braucht die MySQL-Verbindung temporär so viel Speicher, dass sich beide Teile ins Gehege kommen.

hat aber echt gut geholfen darüber zu reden - ist ja meißtens so :wink:

Antworten