Cannot execute queries while other unbuffered queries are active

  • Betroffene App
    WoltLab Suite Core

    Hallo,

    einige Benutzer von mir haben immer mal wieder beim Aufklappen der Benachrichtigungen folgenden Fehler:

    Code
    WCF::destruct() Unhandled exception: SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active.  Consider using PDOStatement::fetchAll().  Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
    #0 [...]/httpdocs/wcf/lib/data/DatabaseObjectEditor.class.php(68): wcf\system\database\Database->prepareStatement('UPDATE?wcf1_ses...')
    #1 [...]/httpdocs/wcf/lib/data/acp/session/ACPSessionEditor.class.php(42): wcf\data\DatabaseObjectEditor->update(Array)
    #2 [...]/httpdocs/wcf/lib/system/session/SessionHandler.class.php(857): wcf\data\acp\session\ACPSessionEditor->update(Array)
    #3 [...]/httpdocs/wcf/lib/system/WCF.class.php(175): wcf\system\session\SessionHandler->update()
    #4 [internal function]: wcf\system\WCF::destruct()
    #5 {main}

    Ich habe schon ein bisschen danach gesucht, allerdings nichts gefunden, was im Zusammenhang mit dem WCF für mich Sinn ergibt. Wie man an der Fehlermeldung oben erkennt, wird der Fehler nicht vom registrierten error_handler (der Debug Modus ist deaktiviert, trotzdem sehen die Benutzer die obigen Fehlermeldung). Zudem wird der Fehler auch nicht in den Error Log geschrieben.

    Gibt es Ideen, woher das obigen Problem kommt und wie es zu beheben ist?

  • Habe mir das Plugin angeschaut und konnte darin kein Statement finden, welches nicht mit einer while Schleife vollständig mit fetchArray() ausgelesen wird. Da das Plugin hier vom Store ist, wäre so ein grober Schnitzer wahrscheinlich auch bei der Prüfung aufgefallen.

    Könnte es auch zu Problemen durch eine "falsche" Benutzung von UserNotificationHandler oder UserStorageHandler kommen?

    • Offizieller Beitrag

    Hallo,

    Könnte es auch zu Problemen durch eine "falsche" Benutzung von UserNotificationHandler oder UserStorageHandler kommen?

    vermutlich nicht. Aber ich kann hier jetzt auch nur mutmaßen und sagen, dass mir keine anderen Problemfälle an dieser Stelle bekannt sind. Offensichtlich ist da noch irgendein Query existent und nicht garbage collected, während der Destruktor ausgeführt wird. Das bedeutet, dass dieser Query nicht in einer lokalen Variable gespeichert sein kann, sondern mindestens in einem Attribut.

    Möglicherweise gibt der Benchmark Tipps wo du schauen könntest.

  • Leider kann ich das Problem nicht reproduzieren, sondern es tritt immer mal wieder bei mir und meinen Moderatoren (unabhängig voneinander) auf. Zudem ist es auch nicht mein Plugin, weswegen ich keinen zu tiefen Einblick darin habe, außer den Quellcode überfliegen zu können.

    Was ich interessanter finde: Das Plugin erstellt mithilfe des UserNotificationHandler beim Hinzufügen von Moderationskommentaren eine Benachrichtigung für alle Benutzer, welche die Meldungen sehen können. Der Fehler tritt allerdings unabhängig vom Plugin auf beim Ausklappen der Benachrichtigungen. Daher liegt der Fehler entweder an fehlerhaften Notifications, welche das Plugin einträgt, oder der Fehler kommt doch nicht von diesem Plugin. Hast du spontan eine Idee, ob man etwas falsch machen kann beim hinzufügen einer Benachrichtigung?

    Ich schau mal in der Datenbank nach, ob sich die Benachrichtigungen vom Plugin von den Like- und Quote-Benachrichtigungen unterscheiden.

    Übrigens vielen Dank für deine Hilfe!

    • Offizieller Beitrag

    Hallo,

    es ist nicht notwendigerweise das von dir genannte Plugin daran Schuld (außer natürlich du setzt nur dieses eine Plugin ein). Grundsätzlich halte ich einen Bug im WCF für unwahrscheinlich (sonst hätte sich sicherlich schon jemand gemeldet), ausgeschlossen ist aber auch das nicht.

    Hast du spontan eine Idee, ob man etwas falsch machen kann beim hinzufügen einer Benachrichtigung?

    Würde mich wundern.

  • Ich habe die Ursache eingrenzen können. In der Klasse \wcf\system\user\notification\UserNotificationHandler wird in den Zeilen 366 bis 381 folgendes gemacht:

    Der Fehler tritt in Zeile 377 bei fetchObject() auf. Und zwar bei dieser Row aus dem Result (eine Benachrichtigung von dem Plugin für die Benachrichtigungen für Kommentare in Meldungen):


    Hier eine "normale" Benachrichtigung zum Vergleich:


    Spontan fällt mir als einziger Unterschied [timesTriggered] => 2 auf. Mich wundert zudem, dass ich keinerlei Exception bekomme, sondern einen HTTP ERROR 500. Übrigens tritt der Fehler auch auf der notification-list Seite auf (da letztendlich die gleichen Methoden benutzt werden).

    • Offizieller Beitrag

    Hallo,

    Der Fehler tritt in Zeile 377 bei fetchObject() auf. Und zwar bei dieser Row aus dem Result:

    stellt sich die Frage, warum der Destruktor vom WCF aufgerufen wird. Bastel mal einen „Pokémon-Exception-Handler“ um die Schleife, vermutlich wird da irgendetwas geworfen:

    PHP
    try {
    }
    catch (\Exception $e) {
      var_dump($e);
    }
    catch (\Throwable $e) {
      var_dump($e);
    }
  • Ich habe mich vorhin getäuscht. Das Problem beginnt doch erst an späterer Stelle. Der Stacktrace ist folgender:

    Die Query, welche von der DatabaseObjectList abgeschickt wird ist folgende:

    Diese kommt auf sage und schreibe 49'050 Zeilen im ResultSet, welche alle einzeln gefetcht und als ModerationQueue Objekt instantiiert werden. Das ist der Punkt, an welchem unser Server einen HTTP ERROR 500 wirft.

    Ich habe heute leider nicht mehr die Zeit weiter zu debuggen, woher dieses gewaltige ResultSet kommt.

    • Offizieller Beitrag

    Hallo,

    da dein WCF innerhalb des genannten Plugins (u.a. ModerationQueueDataHandler taucht im Stack Trace auf) die Biege macht kann ich an dieser Stelle keinen Support mehr geben.

    Mich würde zwar noch interessieren, warum die ursprüngliche Fehlermeldung auftaucht: Wenn es eine Exception ist, dann sollte der Stack abgeräumt werden und das Query vom Garbage Collector weg geräumt werden, bevor der Destruktor läuft. Wenn es das Memory Limit war, dann sollte auch der Destruktor nicht mehr aufgerufen werden. Wenn du noch heraus findest, was genau da geschieht, dann würde das meine persönliche Neugierde stillen, ich werde das Thema an dieser Stelle aber als „Ist kein Fehler“ labeln, da hier offensichtlich ein Fehler in dem Plugin vorliegt.

  • 654 Zeilen mit einem count von jeweils 24.

    Edit: Jeweils 24 Zeilen stimmt nicht. Es sind nur ~50 mit count 24, dann etwa 600 mit count 11 und eines mit count 10.

  • Wie im obigen Stacktrace zu sehen ist, wird eine ViewableModerationQueueList benutzt, um an die ViewableModerationQueue Einträge zu kommen. Der Unterschied zwischen der ModerationListPage, in welcher ebenfalls eine ViewableModerationQueueList benutzt wird, und dem Plugin Moderation Queue Notification ist, dass das Plugin die setObjectIDs() Methode vor dem readObjects() benutzt. Das hat zur Folge, dass im Fall der ModerationListPage der Condition Builder der ViewableModerationQueueList bzw. der DatabaseObjectList in der Query verwendet wird. Im Fall des Plugins hingegen wird der Condition Builder nicht benutzt und das ResultSet schwillt massiv an.

    Da ich nicht genau weiß, wie der Aufbau der ModerationQueue funktioniert und wie die Beziehungen zwischen den Datenbanktabellen aussehen, weiß ich nicht genau, wie man den Fehler im Plugin beheben kann.

    @TimWolla
    Da Destruktoren beim Erreichen des Memory Limits nicht ausgeführt werden, kann ich mir den Fehler in der Themenüberschrift auch nicht erklären. Sinnvoll zu erwähnen ist vielleicht, dass die Fehlermeldung nur beim Aufklappen der Benachrichtigungen erschienen ist. Hat man die NotificationListPage aufgerufen, erhielt man immer einen HTTP ERROR 500.

    • Offizieller Beitrag

    Da ich nicht genau weiß, wie der Aufbau der ModerationQueue funktioniert und wie die Beziehungen zwischen den Datenbanktabellen aussehen, weiß ich nicht genau, wie man den Fehler im Plugin beheben kann.

    moderation_queue_to_user ist eine Auflistung aller Benutzer pro Queue die auf die Moderation zugreifen können, mit isAffected wird bestimmt ob der Benutzer tatsächlich Zugriff auf den jeweiligen Queue-Eintrag hat.

  • Danke

    In der ViewableModerationQueueList wird im Konstruktor diese Bedingung hinzugefügt:

    PHP
    $this->getConditionBuilder()->add("moderation_queue_to_user.queueID = moderation_queue.queueID");

    Da durch das setObjectIDs() im Plugin dann beim Aufruf von readObjects() nicht der Condition Builder benutzt wird, fällt diese Bedingung in der Query weg. Kommt daher das extrem große Result Set, weil beim JOIN zwischen moderation_queue_to_user und moderation_queue nicht nur die zueinander gehörenden Queues gejoined werden, sondern jede Queue mit jedem Eintrag in moderation_queue_to_user?

    • Offizieller Beitrag

    Kommt daher das extrem große Result Set

    Prinzipiell möglich da ja ein FULL JOIN gemacht wird und es keine Bedingung für die moderation_queue-Tabelle gibt, im Endeffekt hast du dann ein kartesisches Produkt.

    Am einfachsten wäre es wohl statt setObjectIDs einfach die IDs per Condition-Builder zu setzen.

  • @Fabii
    Ich habe bei mir folgendes eingebaut:

    In der Klasse wcf\system\moderation\queue\ModerationQueueDataHandler in der Methode getQueue anstelle von

    PHP
    $queueList->setObjectIDs($this->queueIDs);

    das hier

    Code
    $queueList->getConditionBuilder()->add($queueList->getDatabaseTableAlias().".".$queueList->getDatabaseTableIndexName()." IN (?)", array($this->queueIDs));


    Es scheint alles noch immer so zu funktionieren, wie es soll. Das ResultSet in der ViewableModerationList ist mit dem alten Code aktuell über 15'000 Rows groß. Mit dem neuen Code entspricht die Anzahl Rows der Anzahl ausstehender Benachrichtigungen.

    Edit:
    @Alexander Ebert
    Ist dies in gewisser Weise aber nicht doch ein Fehler vom WCF? Die ViewableModerationQueueList ist eine Klasse vom WCF und die Nutzung der setObjectIDs() Methode führt offensichtlich zu einem falschen oder zumindest sehr unperformanten Ergebnis. Das es im WCF selbst nicht zu dem Problem kommt, weil an keiner Stelle setObjectIDs() von ViewableModerationQueueList benutzt wird, schließt ja nicht aus, dass es nicht trotzdem falsch ist.

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!