Performance-Engpass bei der Verarbeitung von GIFs

  • Affected App
    WoltLab Suite Core

    Im Dateianhang befindet sich eine Gif-Datei, welche ich von einem Nutzer erhalten habe und die nicht besonders groß ist, nur 237,27 KB. Jedoch scheint das WSC mit dem Upload dieser Datei große Probleme zu haben. Nachdem eine ganze Weile nach dem Start des Uploads nur der Ladekreis zu sehen ist, erscheint die Meldung "Es ist ein Fehler bei der Verarbeitung aufgetreten, bitte versuche es später erneut.", die Konsole zeigt, dass es einen 503er Server-Fehler gegeben hat. Der wird auch im ACP geloggt:


    Requested URL
    /forum/index.php?ajax-upload/&t=32968a0d920bf6f43078edff3e70c236c8961fa7
    Referrer
    Error Message
    Could not prepare statement 'DELETE FROM wcf1_attachment WHERE attachmentID = ?'
    Type
    wcf\system\database\exception\DatabaseQueryException
    File (Line)
    /www/htdocs/w0175a4b/camp-firefox.de/lib/system/database/Database.class.php (284)
    Stacktrace
    1. /www/htdocs/w0175a4b/camp-firefox.de/lib/data/attachment/AttachmentEditor.class.php (31): wcf\system\database\Database->prepareStatement(…)
    2. /www/htdocs/w0175a4b/camp-firefox.de/lib/system/upload/DefaultUploadFileSaveStrategy.class.php (264): wcf\data\attachment\AttachmentEditor->delete(…)
    3. /www/htdocs/w0175a4b/camp-firefox.de/lib/system/upload/UploadHandler.class.php (107): wcf\system\upload\DefaultUploadFileSaveStrategy->save(…)
    4. /www/htdocs/w0175a4b/camp-firefox.de/lib/data/attachment/AttachmentAction.class.php (148): wcf\system\upload\UploadHandler->saveFiles(…)
    5. /www/htdocs/w0175a4b/camp-firefox.de/lib/data/AbstractDatabaseObjectAction.class.php (205): wcf\data\attachment\AttachmentAction->upload(…)
    6. /www/htdocs/w0175a4b/camp-firefox.de/lib/action/AJAXProxyAction.class.php (75): wcf\data\AbstractDatabaseObjectAction->executeAction(…)
    7. /www/htdocs/w0175a4b/camp-firefox.de/lib/action/AJAXInvokeAction.class.php (94): wcf\action\AJAXProxyAction->invoke(…)
    8. /www/htdocs/w0175a4b/camp-firefox.de/lib/action/AbstractAction.class.php (47): wcf\action\AJAXInvokeAction->execute(…)
    9. /www/htdocs/w0175a4b/camp-firefox.de/lib/action/AJAXInvokeAction.class.php (61): wcf\action\AbstractAction->__run(…)
    10. /www/htdocs/w0175a4b/camp-firefox.de/lib/system/request/Request.class.php (83): wcf\action\AJAXInvokeAction->__run(…)
    11. /www/htdocs/w0175a4b/camp-firefox.de/lib/system/request/RequestHandler.class.php (107): wcf\system\request\Request->execute(…)
    12. /www/htdocs/w0175a4b/camp-firefox.de/forum/index.php (9): wcf\system\request\RequestHandler->handle(…)
    Error Message
    SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
    Type
    PDOException
    File (Line)
    /www/htdocs/w0175a4b/camp-firefox.de/lib/system/database/Database.class.php (279)
    Stacktrace
    1. /www/htdocs/w0175a4b/camp-firefox.de/lib/system/database/Database.class.php (279): PDO->prepare(…)
    2. /www/htdocs/w0175a4b/camp-firefox.de/lib/data/attachment/AttachmentEditor.class.php (31): wcf\system\database\Database->prepareStatement(…)
    3. /www/htdocs/w0175a4b/camp-firefox.de/lib/system/upload/DefaultUploadFileSaveStrategy.class.php (264): wcf\data\attachment\AttachmentEditor->delete(…)
    4. /www/htdocs/w0175a4b/camp-firefox.de/lib/system/upload/UploadHandler.class.php (107): wcf\system\upload\DefaultUploadFileSaveStrategy->save(…)
    5. /www/htdocs/w0175a4b/camp-firefox.de/lib/data/attachment/AttachmentAction.class.php (148): wcf\system\upload\UploadHandler->saveFiles(…)
    6. /www/htdocs/w0175a4b/camp-firefox.de/lib/data/AbstractDatabaseObjectAction.class.php (205): wcf\data\attachment\AttachmentAction->upload(…)
    7. /www/htdocs/w0175a4b/camp-firefox.de/lib/action/AJAXProxyAction.class.php (75): wcf\data\AbstractDatabaseObjectAction->executeAction(…)
    8. /www/htdocs/w0175a4b/camp-firefox.de/lib/action/AJAXInvokeAction.class.php (94): wcf\action\AJAXProxyAction->invoke(…)
    9. /www/htdocs/w0175a4b/camp-firefox.de/lib/action/AbstractAction.class.php (47): wcf\action\AJAXInvokeAction->execute(…)
    10. /www/htdocs/w0175a4b/camp-firefox.de/lib/action/AJAXInvokeAction.class.php (61): wcf\action\AbstractAction->__run(…)
    11. /www/htdocs/w0175a4b/camp-firefox.de/lib/system/request/Request.class.php (83): wcf\action\AJAXInvokeAction->__run(…)
    12. /www/htdocs/w0175a4b/camp-firefox.de/lib/system/request/RequestHandler.class.php (107): wcf\system\request\Request->execute(…)
    13. /www/htdocs/w0175a4b/camp-firefox.de/forum/index.php (9): wcf\system\request\RequestHandler->handle(…)


    Offensichtlich schießt die Datei die Datenbank ab. Der Upload selbst scheint aber zu klappen, denn wenn der Beitrag abgesendet wird, ist die Datei trotzdem da.


    Das ist in diesem Forum insofern reproduzierbar, als dass es auch hier ziemlich lange dauert, die Datei hochzuladen. Allerdings überlebt die Datenbank es hier und es kommt nicht zum Fehler.

    • Official Post

    Das Problem ist einfach, dass die GIF-Verarbeitung sehr zeitaufwändig ist und in deinem Fall die Datenbank-Verbindung auf Grund eines Timeouts vorzeitig geschlossen wird. Die Datenbank selbst ist nicht beeinträchtigt, die Meldung ist einfach nur etwas irreführend.

  • Was gibt es denn so aufwändig zu verarbeiten beziehungsweise gibt es hier Möglichkeiten zur Optimierung? Denn man sollte ja meinen, dass eine 237 KB kleine Grafik in einem gängigen Format problemlos hochgeladen werden kann. Ich kann den Nutzern schlecht erzählen, dass das so eine Schwierigkeit sei. Die halten mich dann ja für bekloppt. ;)

    • Official Post

    Die Dateigröße ist hier irrelevant, das Problem ist die Skalierung. Ein GIF ist im Prinzip nichts anderes als ein Bild gefolgt von den Teilbildern in Form der Veränderungen in jedem weiteren Frame.


    Um ein GIF zu skalieren passiert das folgende:

    1. Gegeben ist ein fiktives GIF mit 50 Frames
    2. Berechnung von 50 Einzelbildern
    3. Skalierung von 50 Einzelbildern auf das gewünschte Format
    4. Berechnung der Unterschiede der 50 Einzelbilder, um daraus das endgültige GIF zu erzeugen

    Die Dateigröße sagt leider überhaupt nichts über den Rechenaufwand aus.


    Das Bottleneck wird hier ImageMagick sein, wir werden uns damit mal befassen und sehen, welche Optimierungsmöglichkeiten wir da haben.

  • Alexander Ebert

    Changed the title of the thread from “Gif-Datei von kleiner Dateigröße überlastet Datenbank beim Upload” to “Performance-Engpass bei der Verarbeitung von GIFs”.
  • Danke, das leuchtet soweit ein.


    Die Frage ist, wieso überhaupt Einzelbilder berechnet werden müssen, und man die Grafik nicht einfach nehmen kann, wie sie ist. Geht es um die Bildskalierung, damit keine zu großen Bilder hochgeladen werden, bzw. um das Erzeugen eines Vorschaubilds, und weil die Bildfolge nicht verloren gehen soll, müssen alle Bilder berechnet werden?


    Jedenfalls Danke für's damit Befassen.

    • Official Post

    Geht es um die Bildskalierung, damit keine zu großen Bilder hochgeladen werden, bzw. um das Erzeugen eines Vorschaubilds, und weil die Bildfolge nicht verloren gehen soll, müssen alle Bilder berechnet werden?

    Bei der Datei dürfte es sich um die Erzeugung der Vorschaugrafik ("Thumbnail") handeln, denn die Grafik selbst ist nicht groß genug um herunterskaliert zu werden. Bildverarbeitung ist und bleibt leider eine der mit Abstand aufwändigsten Prozesse bei Web-Applikationen (Video-Verarbeitung lasse ich mal ganz raus, das ist eine andere Geschichte).

    • Official Post

    Ein Zwischenstand: Wir sind eher zufällig auf ein Issue zu ImageMagick auf GitHub gestoßen, in dem ein ähnliches Problem besprochen wurde. Die Empfehlung im letzten Kommentar durch den Ersteller ist der Aufruf von quantizeImages() vor dem Speichern der Frames als Einzelgrafik.


    Ich habe das mal experimentell lokal umgesetzt und kam zu dem folgenden Ergebnis.


    Originaldatei aus dem Startbeitrag:

    Test.gif (998x438 Px, 237 kB)


    Erzeugung einer Thumbnail-Grafik mit 684x300 Px:

    25,38 Sekunden, 14,1 MB Dateigröße


    Erzeugung einer Thumbnail-Grafik mit 684x300 Px in Kombination mit quantizeImages():

    8,3 Sekunden, 202 kB Dateigröße


    Optisch ist kein Unterschied zwischen den beiden Grafiken erkennbar.


    Testsystem:

    PHP 7.4.13

    imagemagick 7.0.10-48

    macOS 11.0.1

    Intel Core i7-7820HQ @ 2,9 GHz (mit deaktiviertem Turbo)

  • Verzeihung, wenn ich mich in die Diskussion einmische.

    Könnte es sein, dass damit auch das Problem aus diesem Thread gelöst würde?

    ImageMagick wirft Fehler


    Dies wäre sehr hilfreich, da wir dadurch wieder animierte GIFs für Schulungszwecke verwenden könnten.


    Danke im Voraus.

    Grüsse aus Wien, Regards from Vienna

    Saccil

  • Bei der Datei dürfte es sich um die Erzeugung der Vorschaugrafik ("Thumbnail") handeln

    Muss es denn sein, dass die Thumbnails auch wieder "bewegte" GIFs mit allen Frames sind? Könnte man dafür nicht einfach (zumindest als Option im ACP) den ersten Frame als Standbild verwenden?

    Gruß aus Südhessen

    • Official Post

    Cadeyrn Ich habe die Änderung hier online bereits vorab vorgenommen, der PR mit den Änderungen findest du auf GitHub, solltest du dies selbt ausprobieren wollen: https://github.com/WoltLab/WCF/pull/3807


    Saccil Möglicherweise ist das ein Nebeneffekt. Eventuell macht es Sinn, dies nach einer entsprechenden Änderung von uns erneut auszuprobieren, um so mögliche Fehlerquellen auszuschließen. Bitte nutze dann dafür ein neues Thema, um keine Inhalte zu vermischen.


    Donner Das wäre unsere Notlösung gewesen, aber erst einmal versuchen wir Probleme ohne funktionelle Einbußen zu lösen. Denkbar wäre auch die Erzeugung der Vorschaugrafik als Standbild in Kombination mit einer asynchronen Erzeugung der "richtigen" Grafik. Das sprengt aber den Rahmen dieses Themas. Das Standbild zu erzeugen ist übrigens auch ziemlich aufwändig, grob geschätzt würde die in meinem obigen Beispiel auch ca. 2-3 Sekunden dauern, denn zur Ermittlung des ersten Frames muss das gesamte GIF dekonstruiert werden.

    • Official Post

    Der Fix stimmt schon, Grafikverarbeitung ist und bleibt aber unglaublich rechenintensiv, das lässt sich nicht magisch beschleunigen. Durch die Korrektur ist die Laufzeit aber nicht mehr "astronomisch hoch" sondern nur noch das erwartete "bäh, es ist ein GIF"-hoch. Und die resultierende Grafik hat eine realistische Dateigröße.


    Das Bild sind 120 Frames à 800x600 die es zu skalieren gilt, bevor das GIF neu zusammengesetzt und komprimiert werden kann…


    Code
    $> time convert 53c6d53c0a420d130ea23d645533c934.gif -coalesce -resize 256x256 -quantize transparent +dither -colors 256 -deconstruct result.gif
    convert 53c6d53c0a420d130ea23d645533c934.gif -coalesce -resize 256x256     25  29,02s user 2,65s system 579% cpu 5,466 total
  • Ich konnte halt keinen Unterschied bei der Verarbeitung feststellen, mit und ohne den Fix. Ich finde 20 Sekunden durchaus hoch. Vermutlich werde ich die Verarbeitung exorbitant großer Dateien einfach in den ImageMagick Einstellungen unterbinden.

  • Vielleicht kann man im WSC auch die Bearbeitung von größeren GIFs erstmal deaktivieren bis das Problem bei ImageMagick gelöst ist.

    ┌П┐(◉_◉)┌П┐

Participate now!

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