PHP 8: There is no active transaction

  • Affected Version
    WoltLab Suite 5.4

    Hallo,


    bei einem unserer Kunden trat in Verbindung mit dem KittMedia Shop und WSC 5.4 das Problem auf, dass eine Datenbank-Transaktion die automatisch übernommen/ausgeführt (autocommit=1; Standardwert) wird, nun unter PHP 8.0 zu der Fehlermeldung "Could not commit transaction" mit der ursprünglichen PDO-Fehlermeldung "There is no active transaction" führt.


    Eine kurze Recherche zeigte auf, das PHP 8.0 diese vormals unterdrückte Meldung nun ans Tageslicht bringt. Wir haben dem Kunden eine angepasste Database.class.php zur Verfügung gestellt, wo jeweils bei Database::beginTransaction(), Database::commitTransaction() sowie Database::rollBackTransaction() explizit überprüft wird, ob man sich tatsächlich noch in einer laufenden Transaktion befindet (PDO::inTransaction()) und diese nicht schon automatisch übernommen/ausgeführt wurde.


    Das Problem dürfte in allen WSC Version auftreten können die mit PHP 8 kompatibel sind und darunter ausgeführt werden.


    Die ursprüngliche Fehlermeldung lautet wie folgt, ist aber eigentlich für die Erforschung der Ursache uninteressant, da oben genannt:

    Requested URL
    POST /acp/index.php?worker-proxy/&t=REDACTED
    Referrer
    https://REDACTED/acp/index.php?rebuild-data/
    Error Message
    Could not commit transaction
    Type
    wcf\system\database\exception\DatabaseTransactionException
    File (Line)
    /var/www/REDACTED/lib/system/database/Database.class.php (243)
    Stacktrace
    1. /var/www/REDACTED/shop/lib/system/worker/PackageServerRebuildDataWorker.class.php (113): wcf\system\database\Database->commitTransaction(…)
    2. /var/www/REDACTED/lib/acp/action/WorkerProxyAction.class.php (104): kpps\system\worker\PackageServerRebuildDataWorker->execute(…)
    3. /var/www/REDACTED/lib/action/AbstractAction.class.php (53): wcf\acp\action\WorkerProxyAction->execute(…)
    4. /var/www/REDACTED/lib/action/AJAXInvokeAction.class.php (65): wcf\action\AbstractAction->__run(…)
    5. /var/www/REDACTED/lib/system/request/Request.class.php (89): wcf\action\AJAXInvokeAction->__run(…)
    6. /var/www/REDACTED/lib/system/request/RequestHandler.class.php (119): wcf\system\request\Request->execute(…)
    7. /var/www/REDACTED/acp/index.php (11): wcf\system\request\RequestHandler->handle(…)
    Error Message
    There is no active transaction
    Type
    PDOException
    File (Line)
    /var/www/REDACTED/lib/system/database/Database.class.php (226)
    Stacktrace
    1. /var/www/REDACTED/lib/system/database/Database.class.php (226): PDO->commit(…)
    2. /var/www/REDACTED/shop/lib/system/worker/PackageServerRebuildDataWorker.class.php (113): wcf\system\database\Database->commitTransaction(…)
    3. /var/www/REDACTED/lib/acp/action/WorkerProxyAction.class.php (104): kpps\system\worker\PackageServerRebuildDataWorker->execute(…)
    4. /var/www/REDACTED/lib/action/AbstractAction.class.php (53): wcf\acp\action\WorkerProxyAction->execute(…)
    5. /var/www/REDACTED/lib/action/AJAXInvokeAction.class.php (65): wcf\action\AbstractAction->__run(…)
    6. /var/www/REDACTED/lib/system/request/Request.class.php (89): wcf\action\AJAXInvokeAction->__run(…)
    7. /var/www/REDACTED/lib/system/request/RequestHandler.class.php (119): wcf\system\request\Request->execute(…)
    8. /var/www/REDACTED/acp/index.php (11): wcf\system\request\RequestHandler->handle(…)
    • Official Post

    Hallo,


    die Beschreibung klingt so als würde entweder commitTransaction() ohne ein entsprechendes beginTransaction() ausgeführt oder innerhalb der bestehenden Transaktion ein Query abgesendet, der ein implizites Commit ausführt (bspw. ALTER TABLE). Beides ist ein Programmierfehler im entsprechenden Plugin.


    Der Verweis auf autocommit=1 ist nicht schlüssig. Im Stacktrace ist schließlich das explizite commitTransaction() zu sehen.


    Die vorgeschlagene Änderung an der Database.class.php ist falsch, da sie das Symptom des Programmierfehlers nur versteckt.

  • Hallo,


    nein, so doof bin ich definitiv nicht, lieber Tim! Das zeigt auch ein Blick in euren Quellcode, dass das nicht die Ursache1 sein kann.


    Und doch, ich bin mir nach meiner Recherche ziemlich sicher, dass das die Lösung ist. Diese Prüfung haben größere Frameworks ebenfalls implementiert.


    In der entsprechenden Klassendatei unseres Shop wird keine Anweisung genutzt, die ein implizites Commit verursacht. Kann den Quellcode aufgrund der Lizenz nicht posten, aber das kannst du mir gerne glauben.


    Das ist auch kein Verstecken eines Programmierfehlers unsererseits. PHP 8.0 arbeitet an dieser Stelle anders und bringt die entsprechende Meldung zum Vorschein währenddessen Versionen <8 dies geflissentlich hingenommen und ignoriert hat. Nutze die Suchmaschine deines Vertrauens und lese dich ein. Zwischen Meldung vom Kunden und dem heutigen Tag sind insgesamt auch 3 Tage bei mir vergangen, da ich sicher sein wollte, dass das kein Problem im KittMedia Shop ist.


    1: Ihr inkrementiert in beginTransaction() (pro Aufruf) die Variable activeTransactions und führt PDO::commit() bzw. RELEASE SAVEPOINT level innerhalb von commitTransaction() nur aus, wenn activeTransaction > 0 bei Aufruf ist.

    • Official Post

    Hallo,

    Das zeigt auch ein Blick in euren Quellcode, dass das nicht die Ursache1 sein kann.

    fair enough. Deswegen habe ich es auch auf „Feedback Required“ gesetzt.


    Nichtsdestotrotz bleibe ich vorerst dabei, dass die Prüfung nur die Symptome eines anderen Problems versteckt. Wenn der Commit nicht zu dem Zeitpunkt passiert, an dem der Code es erwartet, dann wird die transaktionelle Konsistenz verletzt. So zu tun, als wäre alles in Ordnung, kann da nicht die richtige Lösung sein. PHP 8 meldet das ja nicht ohne Grund als Fehler – es ist schließlich ein Fehler.


    Ich selbst habe die Meldung nicht selbst erlebt und betreibe meine lokalen Instanzen schon seit Ewigkeiten mit PHP 8. Ist der Fehler konsistent reproduzierbar oder war das eine einmalige Sache? In ersterem Falle: Kannst du das Paket für die Fehlerdiagnose privat zur Verfügung stellen (per Konversation an mich)? Es versteht sich von selbst, dass es nur zur Diagnose genutzt wird.

  • Ich selber konnte den Fehler bisher zweimal aus vielen Versuchen lokal reproduzieren. Der Kunde kann es wohl mehrfach hintereinander reproduzieren.


    Kann dir die Pakete gerne zur Verfügung stellen, wenn dich das zur Fehleranalyse weiterbringt. Du musst aber ein klein wenig Einrichtungszeit mit einplanen, da der Worker von unserem Paketserver zur Verfügung gestellt wird und der komplett mit dem Shop verzahnt ist. Ein sofort lauffähiges Konstrukt könnte ich dir über einen MAMP Export zur Verfügung stellen, falls du MAMP im Einsatz hast. Sag mir bitte, was du gerne hättest.


    Dann als kurze Info nebenbei:

    Der Kunde nutzt PHP 8.0.11 mit und als DB-Server das unter Ubuntu verfügbare MySQL-Paket 8.0.26-0ubuntu0.20.04.3. Ich nutze aktuell PHP 8.0.0 unter MAMP und MySQL 5.7.32.

    • Official Post

    Hallo,


    lauffähig wäre zu bevorzugen. Mit einem MAMP-Export kann ich aber leider nichts anfangen. Was funktioniert wäre ein .tar-Archiv vom Dateisystem und ein SQL-Dump mit mysqldump.


    Alternativ nehme ich auch einfach die Pakete und richte es selbst ein.


    In beiden Fällen bräuchte ich noch eine Liste an Schritten zur (mutmaßlichen) Reproduktion des Fehlers.

    Der Kunde nutzt PHP 8.0.11 mit und als DB-Server das unter Ubuntu verfügbare MySQL-Paket 8.0.26-0ubuntu0.20.04.3. Ich nutze aktuell PHP 8.0.0 unter MAMP und MySQL 5.7.32.

    Ich nutze zur Entwicklung Docker, kann also nach Belieben die Versionen wechseln.

    Ich selber konnte den Fehler bisher zweimal aus vielen Versuchen lokal reproduzieren.

    Was du probieren könntest: SET GLOBAL general_log = 'On'; setzen. Der MySQL loggt dann alle Queries (inkl. SELECT) zusammen mit der Verbindungs-ID in eine Logdatei. Bei mir liegt die standardmäßig in /var/lib/mysql/ (also dem Datenverzeichnis). Möglicherweise gibt dieses Query-Log Aufschluss darüber, welcher Query die Transaktion implizit beendet.

  • Alexander Ebert

    Set the Label from Feedback required to Cannot reproduce

Participate now!

Don’t have an account yet? Register yourself now and be a part of our community!