Meine Migration zu Znuny 6.5

Ausgangssituation

Zwei Jahre sind vergangen, seit dem ich schrieb, dass Znuny 6.5 veröffentlicht wurde. Dennoch sind wir bis jetzt mit der alten LTS 6.0.48 gefahren, die schon von der Firma Znuny (nach der Übernahme des Supports der Community-Edition) kam. Gerade der Verbleib der Community-Edition machte uns erst einmal Skeptisch, so dass wir uns dazu entschieden, mit dem Umstieg noch ein wenig zu warten. Durch einige andere Faktoren verzögerte sich dann aber die Migration. Außerdem hatte ich vorher noch ein paar selbst geschriebene Erweiterungen für 6.5 zu portieren. Im Nachhinein stellte sich das aber als trivial heraus.

Diesen Montag war es dann so weit: Unsere Agenten waren bereits alle informiert, dass das Ticket-System am 17 Uhr durch das Update für eine Stunde nicht mehr zu benutzen sein würde. Ich schaltete eine entsprechende Systemwartung um es auch dem letzten Agenten noch einmal ins Gedächtnis zu rufen, bevor ich den Webserver-Dienst ausschaltete.

Ungeplante Versionssprünge

Was ich überhaupt nicht auf dem Schirm hatte war, dass kein direktes Upgrade von 6.0 LTS auf 6.5 LTS möglich ist. Im Vorhinein hatte ich mich natürlich über die anstehenden Schritte informiert, die im Wesentlichen aber deckungsgleich mit den regulären kleinen Updates sind - abgesehen vom Ausführen eines Migrations-Skriptes. Und auch wenn es in der Dokumentation ganz oben in einem eigenen Kasten steht, habe ich es einfach übersehen. Für mich war klar, dass ich (wie in jeder normalen Anwendung) von einer LTS-Version auf die nächste springen kann. Dieses mal aber nicht: Während des Updates merkte ich, dass wir spontan noch “mal eben” auf die Versionen 6.1, 6.2, 6.3 und 6.4 migrieren mussten, bevor wir letztendlich wirklich 6.5 nutzen konnten. Das drückte meine Stimmung leicht, aber durch den geringen Aufwand der jeweiligen Versionssprünge war die Arbeit überschaubar. Ich sah es als eine Fingerübung. Dass es aber nicht ganz so leicht werden würde, sollte auch keine Überraschung sein. Das Migrationsskript scripts/MigrateToZnuny6_{1,2,3,4,5}.pl, welches zum Beispiel Datenbank-Anpassungen durchführt, schlug fast jedes mal fehl.

Fehlende Module

Mit ./bin/otrs.CheckModules.pl prüfte ich für jede Version, dass keine neue Perl-Module als Abhängigkeiten hinzugekommen waren. Dennoch meckerte das Migrationsskript, weil das Paket HTML::Entities nicht gefunden werden konnte. Da ich das passende Paket in nicht im Paketmanager von AlmaLinux fand, installierte ich es systemweit mit cpan install HTML::Entities. Da unsere OTRS-Server hinter einem Proxy liegen, musste ich auch erst einmal raus suchen, wie ich das cpan mitteilen konnte. Immerhin ging das Herunterladen und Kompilieren dann recht schnell und ich konnte mit der nächsten Version weiter machen.

Verwaiste Datensätze

Noch einmal knallte es, als das Migrationsskript neue Fremdschlüssel-Beziehungen in die Datenbank einbauen wollte. Das finde ich ja immer begrüßenswert, schließlich mag ich saubere und konsistente Daten. In unserem Ticket-System ist vorgesehen, dass Tickets auch irgendwann einmal gelöscht werden - soweit eigentlich nichts ungewöhnliches. So wie es aussieht, wurde beim Löschen von Tickets nie (oder zumindest eine Zeit lang) nicht darauf geachtet, auch die History-Einträge zu den Bestandteilen der Tickets zu entfernen. Wenn jetzt also das Migrationsskript neue Fremdschlüssel-Beziehungen einfügen wollte, stolperte es über History-Einträge von Artikeln, dessen Artikel es aber schon nicht mehr gab. Darauf wies mich zumindest ein älterer Forum-Beitrag hin. Ich kann nicht beschreiben, wie happy ich über diesen Forum-Beitrag war. Allerdings merkte ich bei den dort genutzten Queries, dass die Laufzeit bei mir miserabel war:

-- Gib mir alle Ticket-Historien zurück, zu denen es keinen Artikel mehr gibt
SELECT COUNT(*) FROM ticket_history WHERE article_id NOT IN (SELECT id FROM article)

Obwohl der Query recht einfach war, war das Subquery katastrophal: Nach zwei Minuten brach ich das SELECT ab. Die beiden Tabellen hatten einmal 2,8 Millionen und knapp 700.000 Einträge. Wenn das bei meine Abfrage nun ausmultipliziert wird, KANN das auch nicht mit einer vernünftigen Geschwindigkeit laufen. Ich baute ein einfaches JOIN ein und erhielt meine Antwort in einer Sekunde. Diese Situationen lassen mich jedes mal fasziniert zurück - Immer wieder unterschätze ich die Performance von JOINs aufs Neue. Der nächste Schritt fühlte sich irgendwie nicht gut an: Bevor ich irgendwelche Änderungen an der Datenbank machte, legte ich lieber ein weiteres Backup an - nur für alle Fälle. Auch wenn es mir absolut logisch erscheint, die verwaisten Datensätze zu löschen, da sie sowieso zu nichts zugeordnet sind, scheue ich mich eigentlich davor, 1,4 Millionen Datensätze zu löschen. Ich sprang also über meinen Schatten und führte das DELETE aus. Als ich anschließend das Migrationsskript erneut ausführte, fiel mir ein Stein vom Herzen, als es endlich durch lief.

Es gehen keine E-Mails mehr raus

An diesem Punkt dauerte die Migration, vor allem aber die Auszeit für unsere Nutzer, schon zwei Stunden und damit eine Stunde länger, als ursprünglich angekündigt war. Endlich konnte ich aber den Webserver einschalten und unser Ticket-System begrüßte mich mit dem altbekannten Dashboard. Ich klickte erst einmal überall herum und testete auch, ob sich Tickets anlegen und bearbeiten ließen. Alles schien wieder zu laufen. Obwohl es inzwischen kurz vor acht war, meldete sich ein Kollege direkt bei mir, weil er keine Tickets nach außen mehr beantworten konnte: Das Signieren war kaputt. Spannenderweise war beim letzten Schritt im Upgrade wohl der Großteil der Zertifikate und privaten Schlüssel entfernt worden. Immerhin sah ich, dass die Dateien noch im Dateisystem lagen und ich diese einfach mit ./bin/otrs.Console.pl Maint::SMIME::ReindexKeys schnell reindexieren konnte. Ein viel größeres Problem sollte noch folgen. Auch wenn ich mein Test-Ticket beantworten konnte, merkten wir, dass diese Antwort nicht mehr per E-Mail verschickt werden konnte. Das System-Protokoll warf einige nichtssagende Fehlermeldungen. Im Kommunikations-Protokoll sah ich, dass E-Mails vom Mail-Server gelesen, aber keine neuen mehr verschickt werden konnten. Zuerst nahm ich an, ich sei in ein RateLimit vom Mail-Server gekommen. Ein schneller Test in der Kommandozeile zeigte mir aber, dass der Server generell noch Mails senden konnte, nur Znuny schien Probleme zu haben. Eine Fehlermeldung gab es leider nicht. Ich sah im Protokoll sogar, dass der SMTP-Server mit einem guten SMTPCode geantwortet hatte. Was war hier los?

Ist es die Konfiguration?

Mein Verdacht fiel auf die SysConfig: Während des Upgrades hatte ich selber dort zwar nichts verändert, aber bei so einem Sprung war es ja sehr gut möglich, dass neue Konfigurationsparameter eingeführt wurden, oder auch entfernt. Meiner Meinung nach hätte ich dazu zwar etwas in der Dokumentation finden müssen, diese sagte aber gar nichts. Ich verbrachte wirklich einige Stunden damit, die Konfiguration anzupassen und zu prüfen, ob es besser wurde. Als es wirklich spät wurde, nahm ich mir vor, einen Datenbank-Dump vom Vortag und die Sicherung des otrs-Verzeichnisses auf meinen Test-Server zu spielen und die Konfiguration direkt zu vergleichen. Eigentlich hatte ich mir für die Nacht was Besseres vorgestellt, aber es war auch keine Option einfach abzubrechen und am nächsten Tag weiter zu machen. Schließlich würden sich ab 6:30 Uhr ein paar hundert Kollegen einstempeln und uns dann aufs Dach steigen, wenn sie nicht mehr Tickets beantworten können - schließlich geht ein Großteil der Kommunikation einiger Uni-Einrichtungen über dieses Ticket-System. Außerdem war mir klar, wie asozial das gegenüber meiner Kollegin wäre, die den gesammelten Frust abbekommen würde und nichts daran ändern könnte. Ich wollte also den Unterschied in der Konfiguration finden. Sollte das nicht möglich sein, hätte ich später die gesamte Migration zurück nehmen können. Dann hätten wir einen kleinen Datenverlust gehabt, da in der Zwischenzeit wieder einige Nutzer auf dem System waren und die investierte Zeit wäre auch für Nüsse gewesen. Das wollte ich nicht akzeptieren. Das Bereitstellen des alten Produktionsstandes im Test-System kostete mich ungefähr 30 Minuten. Nur um festzustellen, dass die Konfiguration sich nicht verändert hatte. Für einige Einstellungen gab es neue Optionen, diese waren aber nirgends ausgewählt. Mir war also klar: Die Konfiguration hatte sich nicht geändert und war somit auch nicht das Problem.

Ein Bug im SMTP-Modul

Während dieser Zeit begleitete mich ein Kollege, der in der Zwischenzeit nach Hause gefahren war und mich von dort unterstützte. Auf seine Idee hin begannen wir nun also, den Perl-Code der verschiedenen Versionen zu vergleichen. Wir hatten in den vergangenen Stunden sowieso schon an diversen Stellen im Code Debug-Meldungen eingebaut, in der Hoffnung, die Ursache zu finden. Nun verglichen wir den Code der Module, die wir im Verdacht hatten. Und tatsächlich fiel im etwas auf, was mir durch die Lappen gegangen war: Ob das E-Mail-Versenden erfolgreich oder fehlerhaft war, wurde in einem Hash unter Success abgespeichert. An späterer Stelle wurde allerdings auf Successful geprüft. Also wirklich ein kleiner und vor allem dummer Fehler, der es nie in Produktion schaffen sollte - wenn man behauptet, dass es Tests gibt, die sowas abdecken. Es war inzwischen ein Uhr Nachts und ich konnte es nicht glauben, dass uns diese Kleinigkeit so lange auf Trab gehalten hat. Ich fügte im Code unseres Produktions-Servers eben die drei Byte hinzu, leerte den Cache und sah direkt, wie die angesammelten MailQueues im Kommunikations-Protokoll erfolgreich abgearbeitet wurden. Ich fühlte eine Mischung zwischen Erleichterung und Empörung über diesen Fehler. Aber auf der Anderen Seite: Mir selber sind schon so viele dumme Fehler passiert, da sitze ich vermutlich im Glashaus. Ich sah mir das Ticket-System noch ein paar Minuten an und ging nach Hause. Irgendwie war mir etwas unwohl dabei, aber nun war wirklich alles getan, was wir erst mal tun konnten. Sollte jetzt noch ein Fehler passieren, dann wäre es halt so.

Aufräum-Arbeiten

Am nächsten Morgen rief mich die Kollegin (und Ticket-System-Betreuerin) an und wir tauschten uns erst einmal aus, was da alles passiert war. Unseren Agenten schickten wir eine Admin-Benachrichtigung, da einige Ticket-Antworten ja nicht verschickt werden konnten und Znuny in der Zwischenzeit aufgegeben hatte, es noch einmal zu versenden. In diesen Fällen mussten die Agenten nur einmal auf “Erneut versenden” klicken, aber das musste erst einmal kommuniziert werden. Meine Erkenntnis wollte ich natürlich erst mal mit der Welt teilen, weil es für mich auch nur schwer vorstellbar war, dass andere Menschen auf der Welt die Znuny LTS Version 6.5.17 einsetzten, ohne über dieses Problem zu stolpern. Ich stellte einen Pull-Request auf GitHub, sah aber auch, dass das Problem am Vortag (!) bereits aufgefallen war und Znuny an einer Lösung saß. Diese Lösung wurde einen Tag später veröffentlicht. Hätte ich also unser Upgrade zu Znuny 6.5 zwei Tage später gemacht, wären mir da echt einige Stunden Arbeit erspart geblieben. Dies ist die Änderung zwischen Znuny 6.5.17 und 6.5.18. Man sieht: Die Behebung dieses Fehlers ist alles, was die neue Version mit sich bringt. Das bedeutet auch, dass es gravierend genug war, ein neues Release zu veröffentlichen. Das ist ja auch etwas Positives.

Fazit

Am Ende war ich echt froh, als nach fast einem Tag unser Ticket-System wieder stabil lief, nun aber auf der aktuellsten LTS-Version. Auch Änderungen wie die neuen Fremdschlüssel finde ich eine echt gute Sache - auch wenn es bei der Einführung einmal geknallt hat.

Und so ein Bug: Gut - das kann passieren. Ist ärgerlich, aber: Shit happens.