Zuständigkeiten:
Mails: Jan Köpf
Tickets: Nicolas Wimmer & Jan Köpf
Calls: Raimund Machacek & Jan Köpf
Datagrabbing allgemein:
Mails:
Datagrabbing von Mails:
- Ursprüngliche Variante (extra Script):
- Abrufen aller Mails von Mailserver
- Filter eigenschaften anwenden (z.b. Mail Privat / Mail Facebook usw.)
- Falls Mail nicht Filtereigenschaften entspricht --> verwerfen, ansonsten weiter wie folgt:
- Abchecken alle Header Informationen auf Statuseigenschaften (z.b. gelesen / markiert usw.)
- Abgleich aller Mails mit Mails bereits in Datenbank hinterlegt - vergleichswert Message-ID (Unique)
- Falls Mail noch nicht in Datenbank --> eintragen in Datenbank mit eingegangenem Zeitstempel + Status und Message-ID
- Nach ca. 5 Minuten wiederhole Vorgang.
- Neue Variante:
- Dovecot (Mailserver) führt Script aus bei jedem neuen Mail welches den Filtereigenschaften entspricht
- Script übernimmt Mailheader und trägt neues Mail in Datenbank mit Statusinformationen ein
- Falls Mailclients Statusänderungen verursachen (z.b. als gelesen/erledigt markieren) --> Mailserver führt script aus
- Script übernimmt Mailheader informationen und lässt Statuseigenschaften in Datenbank aktualisieren.
Tickets:
Datagrabbing von Tickets:
- Jedes abgeschickte Mail an Support Gruppe löst ein Script aus.
- Script übernimmt Ticketinformationen und je nach Filtereigenschaften (z.b. Filtert ob Business oder Privatkunde)
- Script trägt Zeitpunkt des eintreffens + Eigenschaften des Tickets in Datenbank ein oder aktualisiert diese, falls Ticket bereits vorhanden
"Stale" Datagrabbing Vor- und Nachteile (alte Methode)
- Jederzeit wiederherstellbar
- Performance
- viele Spezialfälle -> beeinträchtigt Datenkonsistenz
- Genauigkeit
"Live" Datagrabbing Vor- und Nachteile
- Performance
- Datenkonsistenz
- Genauigkeit
- kaum Spezialfälle
- Zeitnahe Fehlerbehebung verpflichtend
Wiederherstellung von Daten:
Wiederherstellung von Mails:
- Alle Mails des gesamten Jahres abrufen
- Message-IDs von eingehenden zu ausgehenden Mails vergleichen und Zeitstempel in Datenbank festhalten
- Supporter direkt Fragen, welches Mail, wann bearbeitet wurde.
- Vergleich mit gesendeten Mails und Zeitstempeln
Wiederherstellung von Tickets:
- Alle Tickets des gesamten Jahres abrufen
- Bisherige TTS Logs pro Ticket abgleichen und mit zutreffenden Eigenschaften (z.b. lediglich Tickets an Support oder Tickettyp: .z.b. Privat festhalten)
Technische-Details zu Datagrabbing und Performance
Grund: Im Allgemeinen ist die Datenkonsistenz, sowie die Genauigkeit der Statistik selbst, von der Nachvollziehbarkeit der einzelnen aufzuzeichnenden Ereignisse direkt abhängig.
Im Falle der Mailstatistik gibt es zwei Identifikationsmerkmale, die ein Mail einzigartig und damit eindeutig nachvollziehbar macht.
Es gibt die so genannte message-id (Message-ID, MESSAGE-ID) in jedem Mailheader. Diese wird einzigartig von jedem Mailserver vor dem versandt vergeben.
Zusätzlich dazu ziehen wir noch die so genannte Dovecot-ID heran. Eine ID welche vom Mailserver intern pro Mailbox/Ordner/Mail vergeben wird und ist eine einzigartige fortlaufende Ganzzahl.
Ursprünglich wurden mittels IMAP-Search (RFC-3501) alle Mails eines einzelnen Tages abgerufen, danach mit gewissen Bedingungen verglichen (Absender/Empfänger -> zur evaluierung des Mailtypes = Business/Social Media/Privat) und
anschließend wurde der Mailankunfts-Zeitpunkt samt Message-ID und Mailtyp in einer Datenbank hinterlegt. Um nun die Zeitspanne für die SLA-Berechnung zu erhalten, müssen alle Mails ebenfalls einen Endzeitpunkt beinhalten.
Dieser kann jedoch nur ermittelt werden, wenn regelmäßig alle Mails von einem Tag abgerufen und erneut verglichen werden. Zur ermittlung des Mailstatus wurden die bereits im Support genutzten Thunderbird-Stati (extra IMAP-Flags) verwendet.
Warum nun alle Mails eines ganzen Tages für die Statusüberprüfung abrufen und nicht nur die bereits eingetragenen Mails aus der Datenbank?
Wie in RFC-3501 (Page 51) bzw. in RFC-2822 (Section 3.6.4) nachzulesen ist, kann das Header-Field Message-ID in verschieden Varianten geschrieben werden. Da jedoch IMAP-Search unter PHP einigen Beschränkungen unterliegt,
ist es nicht möglich zuverlässig die Mail via Message-ID abzurufen. Praxistests über Monate hinweg mit PHP-7.1 - 7.3 / PHP-8 und der IMAP2007-Library zeigen das die Message-IDs zwar eindeutig einzigartig sind,
jedoch nicht immer zuverlässig mittels IMAP-Search abgefragt werden können.
Nachdem nun alle Mails eines Tages, mehrmals abgerufen werden müssen (anfängliches Zeitinterval 5min) - ist demnach auch die Serverlast (sowohl am Mailserver als auch am SLA-Berechnungs-Server) sehr hoch.
Ein weiterer Nachteil ist beispielsweise: was passiert bei Löschung eines Mails (weil z.b. Spam) oder bei Verschiebung eines Mails in einem anderen Unterordner als "INBOX".
Der Fallback
Als Fallback wurden die so genannten Dovecot-IDs herangezogen.
Wie kommt man nun zu den "Dovecot-IDs"?
Da diese nicht im Mailheader vorhanden sind, wurde hierzu direkt der Dovecot (Mailserver) befragt. Es wurden also zuerst alle Message-IDs eines Tages abgefragt und anschließend anhand dieser, die dazugehörige Dovcot-ID in der Datenbank hinterlegt.
Diese waren/sind eindeutig jedem Mail innerhalb einer Mailbox zugewiesen und bieten ebenfalls die Möglichkeit via IMAP-Search (UID) einen direkten Abruf der Mail vom Mailserver.
Diese Methode ist sowohl performance freundlich, als auch zuverlässig. Der große Nachteil hierbei ist jedoch immer noch das andauernde Abfragen der Mails nach einem bestimmten Zeitinterval und damit das
aktive aktualisieren von so genannter Stale-Data (Statischer Informationen).
Die neue Methode
Seit Dovecot Version 2.13 gibt es nun auch die Möglichkeit einer API im Dovecot, welche mittels der Skriptsprache LUA arbeitet. Diese Event-Basierende API läuft direkt als Plugin im Dovecot selbst und kann somit zu jederzeit vom Mailserver direkt ausgeführt werden.
Beispiel:
Ein neues Mail kommt in die Box -> Dieses Event löst einen POST mit dem gesamten Mailheader samt Dovecot-ID an eine Webapi aus -> die Webapi weist der Mail anhand von Kriterien (z.b. Absender/Empfänger) einen Mailtyp zu und trägt diesen in die Datenbank ein.
Ein Mail wird vom Support bearbeitet -> Supporter markiert mittels einem IMAP-Flag im Thunderbird das Mail als erledigt -> Dieses Event löst wieder einen POST aus mit Dovcot-ID und Status erledigt -> Die Webapi übernimmt und trägt den exakten Zeitpunkt in die Datenbank ein.
Damit werden nun 2 Probleme auf einmal beseitigt. Die Serverlasten sind minimal und die Endzeitpunkte jedes Mails sind genau und nicht abhängig von Zeitintervallen.
Jedes Mail kann ebenfalls genau nachvollzogen werden und damit wäre dann auch die konsistenz gewähleistet.
Zusätzlich dazu werden alle Mails gezählt und nicht nur jene, welche sich genau an die RFC-Spezifikationen halten bzw. welche die selbe Deklarationen der message-ids haben.
Damit haben wir nun auch Live-Data anstatt Stale-Data und entgehen damit einigen Sonderfällen wie z.b. Löschung oder Verschiebung.
Wiederherstellung
Um gleich bei den Mails zu bleiben, wie oben in den einzelschritten beschrieben, ist ein Abruf aller Mails eines ganzen Jahres nötig. (Nachteil: großer Lastaufwand)
Zusätzlich dazu kann wie in RFC-2822 (Section 3.6.4) mittels Mailheader die Einträge "in-reply-to" sowie "references" herangezogen werden um festzustellen, welche Mails beantwortet und wann diese erledigt wurden.
Alle anderen Mails, welche beispielsweise via Ticket erledigt wurden - würden demnach auch keinen entsprechenden "in-reply-to" sowie "references" Einträge im Mailheader besitzen.
Hierzu müssten dann die Supporter direkt befragt werden, wann / welches Mail erledigt wurde. Dies wäre natürlich extrem unzuverlässig. (Beispielsweise bei bereits gekündigten Mitarbeitern)
Tickets
Ein ähnliches Problem gibt es im TTS. Die einzige Möglichkeit hierbei Ticket-Start sowie Endzeitpunkt festzustellen ist über den TTS-Log. Dieser beinhaltet pro Ticket fast alle Ereignisse.
Auch hier gibt es einige Sonderfälle zu beachten:
- Ticket wird vom letzten Bearbeiter einer anderen Abteilung, erneut bearbeitet
- Ticket wird vom letzten Bearbeiter einer anderen Abteilung, irrtümlich an den Support geschickt
- Ticket wird vom letzten Bearbeiter einer anderen Abteilung, mehrere Bearbeitungen zur gleichen Zeit
- ....
Um nur die häufigsten Sonderfälle zu nennen. Auch hierbei liegt das Hauptproblem bei einer Stale-Data Statistik beim Lastaufwand und der Nachvollziehbarkeit eines jeden Tickets und dessen Bearbeitung.
Ein gutes Beispiel für einen Sonderfall wäre TT1165255 -> laut Status 26.Jan.2021, 17:29 wurde das Ticket von Support an eine andere Gruppe geschickt, anschließend wurde dieses von der Gruppe Support editiert.
Danach wurde das Ticket wieder von Support an eine andere Gruppe geschickt.
Dies bedeutet nun folgendes:
- Startzeitpunkt 1: 26.Jan.2021, 17:29
- Endzeitpunkt 1: ???
- Startzeitpunkt 2: ???
- Endzeitpunkt 2: 05.Feb.2021, 18:25
Natürlich kann für diesen Spezialfall eine Ausnahme geschrieben werden. Jedoch stellt sich hierbei die Frage, welche Zeit gilt für die SLA Berechnung?
Nimmt man Startzeitpunkt 1 und Endzeitpunkt 2, hätte man eine Zeitspanne, in welcher eindeutig eine andere Abteilung für die Weiterverarbeitung des Tickets zuständig war.
Alternativ könnte man den Log durchsuchen und den nächsten Eintrag, welcher nicht von Support kommt, als Endzeitpunkt heranziehen. (also 01.Feb.2021, 10:34 )
Was passiert jedoch mit Endzeitpunkt 2?
-> Soll dieser ganz verworfen werden? (Beeinträchtigung der Statistikkonsistenz)
-> Soll dieser als Startzeitpunkt den nächsten Supporteintrag nehmen (05.Feb.2021, 18:25 )? (Beeinträchtigung der Genauigkeit)
Dies war jedoch nur eine der Ausnahmen und umso länger die Bearbeitung eines Tickets dauert, desto mehr verliert die Statistik an Genauigkeit und Aussagekraft.
Die Lösung
Hierbei bietet die Statistik aus Live-Data wieder eine Lösung. Bei jedem Klick auf "Ticket Weiterführen" oder "Ticket Schließen", wird ein neuer Zeitstempel samt Ticketid und Tickettyp (Business/Privat) an eine Webapi geschickt.
Je nachdem wer das Ticket, wohin schickt - wird ein Start oder ein Endzeitpunkt in der Datenbank festgehalten. Die Ausnahme "Ticket schließen", setzt dabei für alle Einträge einer Ticket-ID den Endzeitpunkt.
Damit wird sichergestellt, dass alle Tickets ab einem gewissen Punkt auch erledigt sind und keines "ewig" offen bleibt.
Fehlermarge
Natürlich ist keine der hier vorgestellten Varianten Perfekt. Darum arbeite ich bereits an einem Hybrid, welcher gesicherte Informationen aus Live-Data (welche garantiert richtig sind) heranzieht und
alle Übrigen, fehlenden Einträge sofern möglich aus Stale-Data hinzufügt.
Derzeit beobachtete Fehlermarge liegt bei ca. +/- 1 Fall pro Woche. Für Mails und Tickets.
Im Vergleich dazu, die Fehlermarge aus reinen Stale-Data Informationen, kann bis zu +/- 1 Fall Pro Tag sein.
Der hybrid wird voraussichtlich +/- 1 Fall Monat Fehlermarge haben.
Ergänzung:
Unter +/- 1 Fall, versteht man einen Fall zu viel bzw. einen Fall zu wenig, wurden in der Berechnung mit einbezogen.
Aufwandsabschätzung Wiederherstellung
- Mails (Jun 2021): mindestens 5 Werktage
- Mails (2021 komplett): mindestens ~80 Werktage (je nach Dienstplan/Krankenstände usw.)
- Tickets (2021 komplett): mindestens 5 Werktage
Calls
Datagrabbing von Calls:
- Ursprüngliche Variante (Callstats via MySQL-DB):
Differenzen zwischen aktueller Statistik und ehem. Statistik (IAS-Export)
Dies betrifft lediglich die Call-Statistik, im speziellen die Anzahl der Gesamtanrufe. Die Daten für die IAS-Statistik wird aus der MySQL-Datenbank ausgelesen. Diese Datenbank unterscheidet sich jedoch von der originalen Datenbank auf welcher der Asterisk-Server (Telefonie-Server) läuft.
Die Unterschiede beziehen sich auf die anonymisierung der Supporter-Daten, beispielsweise Login/Logout-Zeiten oder Durchwahlen.
Grundlegend werden alle Anrufe und deren Dauer, sowie Telefonnummern usw. vom Asterisk in einer Datenbank festgehalten. Dieser hat nach enigen Jahren Einsatzzeit viele Updates erhalten und dabei wurde die Datenbank-Stuktur sowie Datentypen immer wieder angepasst.
Die Übertragung der Anonymisierten Daten zur Statistikdatenbank erfolgt via Script. Da sich jedoch der Datentyp von den aufgezeichneten Anrufzeiten mehrmals verändert hatte, wurden diese leicht Verändert in der Statistikdatenbank hinterlegt.
Die Differenzen liegen hierbei bei wenigen Sekunden, diese jedoch reichen aus, um die Gesamtanzahl der Calls stark abzuändern.
Das Hauptproblem liegt hierbei beim Datentyp in der Statistikdatenbank. Diese bekam bei der Umrechnung vom Originalen Datentyp in einen kompatiblen einige Anrufe gar nicht. Der Fehler ist abhängig von der Anzahl der gleichzeitigen bzw. knapp hintereinander geführten eingehenden Anrufen und daher lediglich bei Ausfällen ersichtlich.
Zu knapp hintereinander folgende eingehende Anrufe, wurden ofmals nicht gezählt und kamen damit nie in die Statistikdatenbank.
Beispiele hierfür gibt es am 02.11.2021, in der Stundenweisen Ansicht. Hierbei fällt auf, dass bei mäßigem Anrufvolumen die Calls fast immer (+1 Call) mit der bisherigen Statistik übereinstimmen. Sobald ein größerer Ausfall verzeichnet wird und damit das Callvolumen ansteigt, wird die Differenz beider Statistiken deutlich sichtbar.
Dies führt natürlich zu deutlich sichtbaren Differnzen in der Monatsstatistik. (Teilweise 20+ Calls)
Die neue Statistik hat diesen Fehler nicht und berücksichtigt noch andere Faktoren, welche die Statistik abändern könnten und ist damit auch weit akkurater als die bisherige. Siehe auch: "Stundenübergreifende Calls".
- Neue Variante (Callstats via pgSQL-DB):
- Datenimport via Script (berücksichtigung von Stundenübergreifenden Calls / Anhand Call-ID)
- Rohdaten via Views angepasst für verschiedenste Zeiträume (Täglich/Monatlich)
- Erfassung der Daten und gegenrechnung (für Malus neutrale Stunden) durch Interface
- Datendarstellung im Interface
Stundenübergreifende Calls
Ein wichtiges Kapitel bei der Callstatistik sind all jene Calls in den Randbereichen des Erfassungsintervals. Da es sich in der Statistik um eine Stundenweite auflösung handelt, muss die Statistik mindestens Minutenweise geführt werden.
Aktuell wird diese im Backend sekundenweise geführt und eine möglichst genaue Statistik zu gewährleisten. Die Randbereiche sind hierbei die Anfänge / Enden jeder Stunde.
Problemstellung:
Ein denkbares Szenario wäre ein Call, welcher um 08:59:00 ankommt und 20 Sekunden später abgehoben wird.
Dieser Call wird dann 3 Minuten später abgeschlossen und aufgelegt.
Nun wird als Endzeitpunkt 09:02:20 festgehalten.
Versucht man nun eine Berechnung von 08:00-09:00, unter berücksichtigung von Malusneutralen Stunden auszuführen, dann wird dies zwangsläufig zu einigen Problemen Führen.
- Für welche Stunde zählt der Call?
- Wie berechnet man die Möglichkeit einer malusneutralen Stunde?
- Wie wirkt sich das auf Tages/Monatsübergreifende Anrufe aus?
Wie bei Mail/Ticketstatistiken wird beim ersten Problem, immer die Ankunft des Calls gezählt. Also in diesem Beispiel 08:59:00. Dieser Zählt nun für den Betrachtungszeitraum von 08:00-09:00.
Da die Berechnung Stundenweise arbeitet, versucht das Script anhand der Call-ID die Endzeit im nächsten Betrachtungszeitraum zu finden.
Ebenfalls wird vom Script, jede Call-ID ignoriert, welche nur Endzeitpunkte/Abhebezeitpunkte und keine Ankunftszeit in dem aktuellen Betrachtungszeitraum haben. (Also Calls welche von der vorangegangen Stunde stammen)
Im Gegensatz zur alten Statistik, bei welcher dies nur bedingt berücksichtigt wurde, gibt es hier ebenfalls bei größeren Betrachtungszeiträumen (Tage/Monate) für diese Fälle spezielle Ausnahmen.
Anschließend schreibt das Script die Differenzen zwischen eingehenden Anruf/Abheben/Auflegen in Sekunden in die Datenbank.
Nach Abschluss eines Betrachtungszeitraumes wird dieser mit dem gegebenen Forcast verglichen und anschließend als Malusneutral oder nicht Markiert.
Die Datenbank selbst verfügt über einige Views, um den Betrachtungszeitraum anzupassen und zwischen Bereinigter und Absoluter Werte unterscheiden zu können. Den direkten Vergleich zwischen diesen Werten übernimmt dann das Interface.