
Dies ist der zweite Artikel in einer Reihe darüber, wie wir bei Citymobil die Stabilität des Dienstes erhöht haben (den ersten können Sie
hier lesen). In diesem Artikel werde ich auf die Besonderheiten der Unfallanalyse eingehen. Aber vorher werde ich einen Punkt behandeln, über den ich im Voraus nachdenken musste, und im ersten Artikel behandeln, aber ich habe nicht darüber nachgedacht. Und worüber ich aus dem Feedback der Leser gelernt habe. Der zweite Artikel gibt mir die Möglichkeit, diesen lästigen Defekt zu beseitigen.
0. Prolog
Einer der Leser stellte eine sehr faire Frage: "Was ist im Backend eines Taxidienstes schwierig?" Die Frage ist gut. Ich habe mich letzten Sommer gefragt, bevor ich anfing, in Citymobil zu arbeiten. Ich dachte dann "denke, ein Taxi, eine Anwendung mit drei Knöpfen." Was könnte daran kompliziert sein? Es stellte sich jedoch heraus, dass dies ein sehr hochtechnologischer Service und ein komplexes Produkt ist. Um zumindest klar zu machen, worum es geht und worum es sich wirklich um einen großen technologischen Koloss handelt, werde ich auf einige Bereiche der Produktaktivitäten von Citymobil eingehen:
- Preisgestaltung. Das Preisteam kümmert sich zu jedem Zeitpunkt und zu jedem Zeitpunkt um Preisprobleme. Der Preis wird durch Vorhersage des Gleichgewichts von Angebot und Nachfrage auf der Grundlage von Statistiken und anderen Daten ermittelt. All dies macht einen großen, komplexen und sich ständig weiterentwickelnden Service aus, der auf maschinellem Lernen basiert.
- Preisgestaltung. Die Implementierung verschiedener Zahlungsmethoden, die Logik der Zuschläge nach der Reise, das Einbehalten von Geldern auf Bankkarten, die Abrechnung, die Interaktion mit Partnern und Fahrern.
- Auftragsverteilung. An welche Maschine soll der Kundenauftrag verteilt werden? Zum Beispiel ist die Verteilungsoption für die nächstgelegene nicht die beste, um die Anzahl der Fahrten zu erhöhen. Eine korrektere Option besteht darin, Kunden und Autos so zu vergleichen, dass die Anzahl der Fahrten angesichts der Wahrscheinlichkeit einer Stornierung durch diesen bestimmten Kunden unter diesen Bedingungen (weil es lange dauert) und der Stornierung oder Sabotage der Bestellung durch diesen Fahrer (weil es zu lange oder zu niedrig dauert) maximiert wird überprüfen).
- Geo. Alles, was mit der Suche und dem Durchsuchen von Adressen, Landepunkten, der Anpassung der Lieferzeit zu tun hat (unsere Partner, Lieferanten von Karten und Staus geben nicht immer genaue Informationen über ETA unter Berücksichtigung von Staus), verbessert die Genauigkeit der direkten und umgekehrten Geokodierung und verbessert die Genauigkeit der Maschine. Es gibt viel Arbeit mit Daten, viele Analysen, viele Dienste, die auf maschinellem Lernen basieren.
- Betrugsbekämpfung. Der Unterschied im Preis einer Reise für einen Passagier und für einen Fahrer (zum Beispiel auf Kurzreisen) schafft einen wirtschaftlichen Anreiz für Betrüger, die versuchen, unser Geld zu stehlen. Die Bekämpfung von Betrug ähnelt in gewisser Weise der Bekämpfung von Spam im E-Mail-Dienst - Vollständigkeit und Genauigkeit sind wichtig. Es ist notwendig, die maximale Anzahl von Betrügern zu blockieren (Vollständigkeit), aber gute Benutzer sollten nicht mit Betrügern verwechselt werden (Genauigkeit).
- Motivation der Fahrer. Das Fahrermotivationsteam entwickelt alles, was mit der Steigerung der Nutzung unserer Plattform durch Fahrer und der Fahrertreue aufgrund verschiedener Motivationsarten zu tun hat. Machen Sie zum Beispiel X-Trips und erhalten Sie dafür zusätzliche Y-Rubel. Oder kaufen Sie eine Schicht für Z Rubel und fahren Sie ohne Provision.
- Backend-Treiberanwendung. Eine Liste der Bestellungen, eine Nachfragekarte (ein Hinweis, wo Sie zum Fahrer gehen müssen, um Ihren Umsatz zu maximieren), Statusänderungen von prokidyvaniya, ein Kommunikationssystem mit Fahrern und vieles mehr.
- Backend-Client-Anwendung (dies ist wahrscheinlich der offensichtlichste Teil und wird normalerweise als Taxi-Backend verstanden): Bestellungen aufgeben, Status über das Ändern des Bestellstatus scrollen, die Bewegung von Autos auf der Karte auf der Bestellung und bei Lieferung sicherstellen, Backend-Tipps und usw.
Das ist die ganze Spitze des Eisbergs. Funktionalität ist viel mehr. Die benutzerfreundliche Oberfläche verbirgt einen riesigen Unterwasserteil des Eisbergs.
Und jetzt zurück zu den Unfällen. In den sechs Monaten der Unfallgeschichte haben wir folgende Kategorisierung zusammengestellt:
- schlechte Veröffentlichung, 500. Fehler;
- schlechte Version, suboptimaler Code, Belastung der Basis;
- erfolgloser manueller Eingriff in das System;
- Osterei;
- äußere Ursachen;
- schlechte Version, defekte Funktionalität.
Im Folgenden werde ich aufschreiben, welche Schlussfolgerungen wir zu den häufigsten Arten von Unfällen gezogen haben.
1. Schlechte Veröffentlichung, 500. Fehler
Fast unser gesamtes Backend ist in PHP geschrieben, einer interpretierten Sprache mit schwacher Typisierung. Es kommt vor, dass Sie den Code einführen und er aufgrund eines Fehlers im Namen der Klasse oder Funktion abstürzt. Und dies ist nur ein Beispiel, wenn der 500. Fehler auftritt. Es kann auch im Falle eines logischen Fehlers im Code auftreten. leckte den falschen Ast; versehentlich den Ordner mit dem Code gelöscht; im Code verbleibende temporäre Artefakte, die zum Testen benötigt werden; hat die Struktur der Tabellen nicht gemäß dem neuen Code geändert; hat die erforderlichen Cron-Skripte nicht neu gestartet oder gestoppt.
Wir haben in mehreren Phasen nacheinander mit diesem Problem zu kämpfen. Verlorene Fahrten aufgrund einer schlechten Freisetzung sind offensichtlich proportional zur Nutzungsdauer. Das heißt, wir müssen unser Bestes tun, um sicherzustellen, dass eine schlechte Version so wenig wie möglich in Betrieb ist. Jede Änderung im Entwicklungsprozess, die die durchschnittliche Zeit, die benötigt wird, um eine fehlerhafte Version in Betrieb zu nehmen, um mindestens 1 Sekunde verkürzt, ist für das Unternehmen positiv und muss implementiert werden.
Eine schlechte Veröffentlichung oder ein Produktionsunfall durchläuft im Allgemeinen zwei Zustände, die wir als "passive Phase" und "aktive Phase" bezeichnet haben. Die passive Phase ist, wenn wir den Unfall noch nicht kennen. Die aktive Phase ist, wenn wir bereits Bescheid wissen. Der Unfall beginnt im passiven Stadium, und im Laufe der Zeit, wenn wir davon erfahren, geht der Unfall in das aktive Stadium über - wir beginnen, ihn zu bekämpfen: Zuerst diagnostizieren wir ihn und reparieren ihn dann.
Um die Dauer eines Produktionsunfalls zu verkürzen, muss die durchschnittliche Dauer sowohl der passiven als auch der aktiven Phase verkürzt werden. Das gleiche gilt für eine schlechte Veröffentlichung, weil es an sich eine Art Unfall ist.
Wir begannen, unseren aktuellen Prozess der Reparatur von Unfällen zu analysieren. Die fehlerhaften Releases, auf die wir zum Zeitpunkt des Beginns der Analyse gestoßen sind, führten zu einem (vollständigen oder teilweisen) Leerlaufdurchschnitt von 20 bis 25 Minuten. Die passive Phase dauerte normalerweise 15 Minuten, die aktive 10 Minuten. Während der passiven Phase wurden Benutzerbeschwerden vom Contact Center bearbeitet, und nach einer gewissen Schwelle beschwerte sich das Contact Center über allgemeine Chats in Slack. Manchmal beschwerte sich einer der Angestellten, wenn er kein Taxi bestellen konnte. Eine Mitarbeiterbeschwerde war für uns ein Signal für ein ernstes Problem. Nach dem Übergang einer fehlerhaften Version in die aktive Phase begannen wir mit der Diagnose des Problems, analysierten die neuesten Versionen, verschiedene Grafiken und Protokolle, um die Unfallursache zu ermitteln. Nachdem wir den Grund herausgefunden hatten, haben wir den Code zurückgesetzt, wenn das fehlerhafte Release zuletzt gepumpt wurde, oder einen neuen Rollback mit der Umkehrung des Commits für das fehlerhafte Release durchgeführt.
Hier ist ein Prozess, um mit schlechten Releases umzugehen, wir mussten uns verbessern.
1.1. Passive Stufenreduzierung
Zunächst haben wir festgestellt, dass wir, wenn eine schlechte Version mit 500 Fehlern einhergeht, ohne Beanstandung verstehen können, dass ein Problem aufgetreten ist. Glücklicherweise wurden alle 500. Fehler in New Relic aufgezeichnet (dies ist eines der von uns verwendeten Überwachungssysteme), und es blieben nur SMS- und IVR-Benachrichtigungen über das Überschreiten einer bestimmten Häufigkeit von „fünfhundert“ (im Laufe der Zeit wurde der Schwellenwert ständig gesenkt).
Dies führte dazu, dass die aktive Phase des Unfalls wie "Schlechte Freigabe, 500. Fehler" fast unmittelbar nach der Freilassung begann. Der Prozess im Falle eines Unfalls sah folgendermaßen aus:
- Der Programmierer stellt den Code bereit.
- Die Freilassung führt zu einem Unfall (massive 500er).
- SMS kommt an.
- Programmierer und Administratoren beginnen zu verstehen (manchmal nicht sofort, aber nach 2-3 Minuten: SMS können verzögert sein, der Ton auf dem Telefon kann ausgeschaltet sein und die Kultur der sofortigen Aktionen nach SMS kann nicht an einem Tag erscheinen).
- Die aktive Phase des Unfalls beginnt, die die gleichen 10 Minuten wie zuvor dauert.
Somit wurde die passive Stufe von 15 Minuten auf 3 Minuten reduziert.
1.2. Weitere Reduzierung der passiven Stufe
Trotz der Reduzierung der passiven Phase auf 3 Minuten hat uns selbst eine so kurze passive Phase mehr gestört als die aktive, da wir in der aktiven Phase bereits etwas tun, um das Problem zu lösen, und in der passiven Phase funktioniert der Service nicht ganz oder teilweise, sondern „ Männer wissen es nicht. "
Um die passive Phase weiter zu reduzieren, haben wir beschlossen, nach jeder Veröffentlichung drei Minuten Entwicklerzeit zu opfern. Die Idee war sehr einfach: Sie rollen den Code aus und sehen sich drei Minuten lang New Relic, Sentry, Kibana an, um festzustellen, ob 500 Fehler vorliegen. Sobald Sie dort ein Problem sehen, gehen Sie a priori davon aus, dass es mit Ihrem Code zusammenhängt, und Sie beginnen zu verstehen.
Wir haben drei Minuten basierend auf Statistiken ausgewählt: Manchmal traten Probleme mit einer Verzögerung von 1-2 Minuten in den Charts auf, aber es gab nie mehr als drei Minuten.
Diese Regel wurde in do's & dont's aufgezeichnet. Anfangs wurde es nicht immer ausgeführt, aber nach und nach gewöhnten sich die Entwickler an die Regel der elementaren Hygiene: Das morgendliche Zähneputzen ist ebenfalls Zeitverschwendung, aber Sie müssen dies tun.
Infolgedessen wurde die passive Phase auf 1 Minute reduziert (die Zeitpläne waren manchmal noch zu spät). Als angenehme Überraschung reduzierte dies gleichzeitig die aktive Bühne. Immerhin stößt der Entwickler auf ein Problem in gutem Zustand und ist bereit, seinen Code sofort zurückzusetzen. Obwohl dies nicht immer hilft, weil Das Problem könnte durch den parallel gerollten Code eines anderen entstanden sein. Trotzdem wurde die aktive Phase im Durchschnitt auf 5 Minuten reduziert.
1.3. Weitere Reduzierung im aktiven Stadium
Mehr oder weniger zufrieden mit einer Minute der passiven Phase, begannen wir über eine weitere Reduzierung der aktiven Phase nachzudenken. Zunächst haben wir uns mit der Geschichte der Probleme befasst (dies ist der Eckpfeiler beim Aufbau unserer Stabilität!) Und festgestellt, dass wir in vielen Fällen nicht sofort zurücksetzen, weil wir nicht verstehen, auf welche Version wir zurücksetzen sollen, da es viele parallele Releases gibt. Um dieses Problem zu lösen, haben wir die folgende Regel eingeführt (und in do's & dont's aufgezeichnet): Vor der Veröffentlichung schreiben Sie in Slack in den Chat, wofür Sie rollen und wofür, und im Falle eines Unfalls schreiben Sie in den Chat "Unfall, nicht rollen!". Darüber hinaus haben wir begonnen, automatisch per SMS über die Release-Fakten zu berichten, um diejenigen zu benachrichtigen, die nicht am Chat teilnehmen.
Diese einfache Regel reduzierte die Anzahl der Freisetzungen bereits bei Unfällen stark und reduzierte die aktive Phase - von 5 Minuten auf 3 Minuten.
1.4. Eine noch stärkere Reduzierung des aktiven Stadiums
Trotz der Tatsache, dass wir im Chat vor allen Veröffentlichungen und Abstürzen gewarnt haben, traten manchmal Rennbedingungen auf - einer schrieb über die Veröffentlichung und der andere wurde bereits in diesem Moment eingeführt; oder der Unfall begann, sie schrieben im Chat darüber und jemand rollte gerade einen neuen Code aus. Diese Umstände verlängerten die Diagnose. Um dieses Problem zu lösen, haben wir ein automatisches Verbot paralleler Releases implementiert. Die Idee ist sehr einfach: Nach jeder Veröffentlichung verbietet das CI / CD-System jedem, die nächsten 5 Minuten auszurollen, mit Ausnahme des Autors der letzten Veröffentlichung (damit er bei Bedarf Hotfix rollen oder rollen kann) und mehrerer besonders erfahrener Entwickler (im Notfall). Darüber hinaus verbietet das CI / CD-System die Einführung während eines Unfalls (dh vom Zeitpunkt des Eingangs der Benachrichtigung über den Beginn des Unfalls bis zum Zeitpunkt des Eingangs der Benachrichtigung über dessen Abschluss).
So wurde der Prozess wie folgt: Der Entwickler rollt aus, überwacht die Diagramme drei Minuten lang und danach kann zwei weitere Minuten lang niemand mehr etwas rollen. Wenn es ein Problem gibt, setzt der Entwickler die Version zurück. Diese Regel vereinfachte die Diagnose radikal und die Gesamtdauer der aktiven und passiven Stadien verringerte sich von 3 + 1 = 4 Minuten auf 1 + 1 = 2 Minuten.
Aber zwei Minuten des Unfalls sind viel. Deshalb haben wir den Prozess weiter optimiert.
1.5. Automatische Crash-Erkennung und Rollback
Wir haben lange darüber nachgedacht, wie wir die Unfalldauer aufgrund schlechter Freisetzungen verkürzen können. Sie haben sogar versucht, sich zu zwingen, in
tail -f error_log | grep 500
zu suchen
tail -f error_log | grep 500
. Aber am Ende entschieden sich alle für eine automatische Kardinallösung.
Kurz gesagt, dies ist ein automatischer Rollback. Wir haben einen separaten Webserver eingerichtet, auf dem wir zehnmal weniger Last vom Balancer geladen haben als auf anderen Webservern. Jede Version wurde vom CI / CD-System automatisch auf diesem separaten Server bereitgestellt (wir nannten sie Preprod, obwohl trotz des Namens die tatsächliche Last von realen Benutzern dorthin ging). Und dann hat die Automatisierung
tail -f error_log | grep 500
tail -f error_log | grep 500
. Wenn innerhalb einer Minute kein 500. Fehler aufgetreten ist, hat das CI / CD den neuen Code in der Produktion bereitgestellt. Wenn Fehler aufgetreten sind, hat das System sofort alles zurückgesetzt. Gleichzeitig wurden auf Balancer-Ebene alle Anforderungen, die mit 500 Fehlern auf dem Preprod abgeschlossen wurden, auf einen der Produktionsserver dupliziert.
Diese Maßnahme reduzierte den Effekt der fünfhundertsten Releases auf Null. Gleichzeitig haben wir im Falle von Fehlern in der Automatisierung die Regel drei Minuten lang nicht abgebrochen, um die Diagramme zu überwachen. Das ist alles über schlechte Veröffentlichungen und 500. Bugs. Wir fahren mit der nächsten Art von Unfall fort.
2. Schlechte Version, suboptimaler Code, Grundlast
Ich beginne gleich mit einem konkreten Beispiel für einen Unfall dieser Art. Wir haben die Optimierung eingeführt: Wir haben der SQL-Abfrage
USE INDEX
hinzugefügt, während wir diese beschleunigten kurzen Abfragen wie in der Produktion getestet haben, aber lange Abfragen haben sich verlangsamt. Die Verlangsamung langer Abfragen wurde nur in der Produktion festgestellt. Infolgedessen wird die gesamte Master-Basis durch den Strom langer Anforderungen eine Stunde lang belastet. Wir haben die Funktionsweise von
USE INDEX
genau verstanden, es in der Datei do's & dont beschrieben und Entwickler vor Missbrauch gewarnt. Wir haben die Abfrage auch analysiert und festgestellt, dass sie hauptsächlich historische Daten zurückgibt. Dies bedeutet, dass sie für historische Abfragen auf einem separaten Replikat ausgeführt werden kann. Selbst wenn dieses Replikat unter Last liegt, wird das Geschäft nicht aufhören.
Nach diesem Vorfall stießen wir immer noch auf ähnliche Probleme und beschlossen irgendwann, das Problem systematisch anzugehen. Wir haben den gesamten Code mit einem häufigen Kamm gescannt und alle Anforderungen, die dort gestellt werden können, an das Replikat ausgeführt, ohne die Qualität des Dienstes zu beeinträchtigen. Gleichzeitig haben wir die Replikate selbst nach Kritikalitätsstufen unterteilt, damit der Ausfall eines dieser Replikate den Dienst nicht stoppt. Als Ergebnis kamen wir zu einer Architektur, die die folgenden Grundlagen hat:
- Master-Basis (für Schreibvorgänge und für Abfragen, die für die Datenaktualität überkritisch sind);
- Produktionsreplik (für kurze Abfragen, die für die Datenaktualität etwas weniger kritisch sind);
- Replik zur Berechnung von Preisverhältnissen, die sogenannte Surge Pricing. Diese Replik kann um 30-60 Sekunden zurückbleiben - dies ist nicht kritisch, die Koeffizienten ändern sich nicht so oft, und wenn diese Replik fällt, wird der Dienst nicht gestoppt, nur die Preise entsprechen nicht ganz dem Gleichgewicht zwischen Angebot und Nachfrage.
- Replikat für das Admin-Panel von Geschäftsbenutzern und das Contact Center (wenn es fällt, steigt das Hauptgeschäft nicht, aber der Support funktioniert nicht und wir können die Einstellungen nicht vorübergehend anzeigen und ändern);
- viele Repliken für die Analyse;
- MPP-Datenbank für umfangreiche Analysen mit vollständigen Slices nach historischen Daten.
Diese Architektur gab uns mehr Raum für Wachstum und reduzierte die Anzahl der Abstürze aufgrund suboptimaler SQL-Abfragen um eine Größenordnung. Aber sie ist immer noch alles andere als ideal. Pläne für Sharding, damit Sie Aktualisierungen und Löschungen sowie kurze Abfragen skalieren können, die für die Aktualität dieser Daten überkritisch sind. Der Sicherheitsspielraum von MySQL ist nicht unendlich. Bald brauchen wir schwere Artillerie in Form eines Tarantools. Darüber wird in den folgenden Artikeln gefordert!
Während des Versuchs mit nicht optimalem Code und nicht optimalen Anforderungen haben wir Folgendes festgestellt: Es ist besser, Nichtoptimalitäten vor und nicht nach der Veröffentlichung zu beseitigen. Dies verringert das Unfallrisiko und den Zeitaufwand für die Optimierung durch Entwickler. Denn wenn der Code bereits heruntergeladen wurde und darüber hinaus neue Versionen vorhanden sind, ist die Optimierung viel schwieriger. Aus diesem Grund haben wir eine obligatorische Codeprüfung auf Optimalität eingeführt. Es wird von den erfahrensten Entwicklern durchgeführt, in der Tat von unseren Spezialeinheiten.
Darüber hinaus haben wir begonnen, bei do's & dont die besten Methoden zur Codeoptimierung zu sammeln, die in unserer Realität funktionieren. Sie sind unten aufgeführt. Bitte nehmen Sie diese Praktiken nicht als absolute Wahrheit wahr und versuchen Sie nicht, sie blind in sich selbst zu wiederholen. Jede Methode ist nur für eine bestimmte Situation und ein bestimmtes Unternehmen sinnvoll. Sie werden hier nur zum Beispiel angegeben, damit die Einzelheiten klar sind:
- Wenn die SQL-Abfrage nicht vom aktuellen Benutzer abhängt (z. B. eine Bedarfskarte für Fahrer, in der die Mindestauslösungsraten und Koeffizienten für Polygone angegeben sind), muss diese Abfrage von cron mit einer bestimmten Häufigkeit durchgeführt werden (in unserem Fall reicht einmal pro Minute aus). Schreiben Sie das Ergebnis in den Cache (Memcached oder Redis), der bereits im Produktionscode verwendet wird.
- Wenn die SQL-Abfrage mit Daten arbeitet, deren Rückstand für das Geschäft nicht kritisch ist, sollte das Ergebnis mit einer TTL zwischengespeichert werden (z. B. 30 Sekunden). Und dann in nachfolgenden Anfragen aus dem Cache lesen.
- Wenn Sie im Zusammenhang mit der Verarbeitung einer Anforderung im Web (in unserem Fall im Zusammenhang mit der Implementierung einer bestimmten Servermethode in PHP) eine SQL-Abfrage durchführen möchten, müssen Sie sicherstellen, dass diese Daten bei keiner anderen SQL-Abfrage „angekommen“ sind (und ob sie durch Code weiter kommen werden). Gleiches gilt für den Zugriff auf den Cache: Er kann auch mit Anfragen überflutet werden, wenn Sie dies wünschen. Wenn die Daten also bereits aus dem Cache "angekommen" sind, müssen Sie nicht in den Cache Ihres Hauses gehen und ihn entnehmen, der bereits weggenommen wurde.
- Wenn Sie im Kontext der Abfrageverarbeitung im Web eine Funktion aufrufen möchten, müssen Sie sicherstellen, dass in den Giblets kein zusätzlicher SQL-Abfrage- oder Cache-Zugriff erfolgt. Wenn das Aufrufen einer solchen Funktion unvermeidbar ist, müssen Sie sicherstellen, dass sie nicht geändert oder ihre Logik aufgelöst werden kann, um keine unnötigen Abfragen an die Datenbanken / Caches durchzuführen.
- Wenn Sie weiterhin in SQL wechseln müssen, müssen Sie sicherstellen, dass Sie die erforderlichen Felder im Code nicht zu den bereits im Code vorhandenen Abfragen hinzufügen können.
3. Erfolgloser manueller Eingriff in das System
Beispiele für solche Unfälle: ein erfolgloser ALTER (der die Datenbank überlastete oder eine Replikatverzögerung hervorrief) oder ein erfolgloser DROP (der in MySQL auf einen Fehler stieß und die Datenbank blockierte, als eine neue Tabelle gelöscht wurde); schwere Anfrage nach einem Meister, der versehentlich von Hand gemacht wurde; Wir haben unter Last am Server gearbeitet, obwohl wir dachten, dass er arbeitslos ist.
Um Stürze aus diesen Gründen zu minimieren, ist es leider notwendig, die Art des Unfalls jedes Mal zu verstehen. Wir haben die allgemeine Regel noch nicht gefunden. Versuchen Sie es erneut mit den Beispielen. Nehmen wir an, irgendwann haben die Stoßkoeffizienten aufgehört zu arbeiten (sie multiplizieren den Preis der Reise am Ort und zum Zeitpunkt der erhöhten Nachfrage). Der Grund war, dass auf dem Datenbankreplikat, aus dem die Daten zur Berechnung der Koeffizienten stammten, das Python-Skript funktionierte, das den gesamten Speicher verbrauchte, und das Replikat ausfiel. Das Skript wurde lange ausgeführt und funktionierte nur zur Vereinfachung auf einem Replikat. Das Problem wurde durch einen Neustart des Skripts behoben. Die Schlussfolgerungen lauteten wie folgt: Führen Sie keine Skripte von Drittanbietern auf einem Computer mit einer Datenbank aus (aufgezeichnet in do's & dont's, andernfalls ist dies eine leere Aufnahme!). Überwachen Sie das Ende des Speichers auf einem Computer mit einem Replikat und benachrichtigen Sie per SMS, wenn der Speicher bald aufgebraucht ist.
Es ist sehr wichtig, immer Schlussfolgerungen zu ziehen und nicht in eine angenehme Situation zu geraten. "Sie haben ein Problem gesehen, es behoben und vergessen." Ein qualitativ hochwertiger Service kann nur aufgebaut werden, wenn Schlussfolgerungen gezogen werden. Darüber hinaus sind SMS-Warnungen sehr wichtig - sie setzen die Servicequalität auf ein höheres Niveau als bisher, verhindern ein Absinken und verbessern die Zuverlässigkeit weiter. Als Kletterer aus jedem stabilen Zustand zieht er sich hoch und ist in einem weiteren stabilen Zustand fixiert, jedoch in einer höheren Höhe.
Überwachung und Alarmierung mit unsichtbaren, aber starren Eisenhaken schneiden in den Felsen der Unsicherheit und lassen uns niemals unter das von uns eingestellte Stabilitätsniveau fallen, das wir ständig nur anheben.
4. Osterei
Was wir das "Osterei" nennen, ist eine Zeitbombe, die es schon lange gibt, auf die wir aber nicht gestoßen sind. Außerhalb dieses Artikels bezieht sich dieser Begriff auf eine nicht dokumentierte Funktion, die absichtlich erstellt wurde. In unserem Fall ist dies überhaupt keine Funktion, sondern ein Fehler, der jedoch wie eine Zeitbombe funktioniert und ein Nebeneffekt guter Absichten ist.
Zum Beispiel: Überlauf 32 Bit
auto_increment
; Nichtoptimalität im Code / in der Konfiguration, "Schuss" aufgrund der Last; Ein verzögertes Replikat (normalerweise entweder aufgrund einer suboptimalen Anforderung für ein Replikat, das durch ein neues Verwendungsmuster ausgelöst wurde, oder einer höheren Auslastung oder aufgrund eines suboptimalen UPDATE auf dem Master, das durch ein neues Lademuster ausgelöst und das Replikat geladen wurde).
Eine andere beliebte Art von Osterei ist nicht optimaler Code und insbesondere nicht optimale SQL-Abfrage. Zuvor war die Tabelle kleiner und die Last geringer - die Abfrage funktionierte gut. Und mit der Zunahme der Tabelle, zeitlich linear und Lastwachstum, zeitlich linear, wuchs der DBMS-Ressourcenverbrauch quadratisch. Normalerweise führt dies zu einem scharfen negativen Effekt: Alles war „ok“ und Knall.
Seltenere Szenarien sind eine Kombination aus Käfer- und Ostereiern. Eine Veröffentlichung mit einem Fehler führte zu einer Vergrößerung der Tabelle oder einer Vergrößerung der Anzahl der Datensätze in einer Tabelle eines bestimmten Typs, und ein bereits vorhandenes Osterei verursachte eine übermäßige Belastung der Datenbank aufgrund langsamerer Abfragen dieser überwucherten Tabelle.
Wir hatten aber auch Ostereier, die nichts mit der Ladung zu tun hatten. Beispiel:
auto increment
32-Bit
auto increment
: Nach zwei und einigen Milliarden Datensätzen in der Tabelle werden keine Einfügungen mehr ausgeführt. Das Feld der
auto increment
in der modernen Welt muss also auf 64-Bit erweitert werden. Wir haben diese Lektion gut gelernt.
Wie gehe ich mit Ostereiern um? Die Antwort ist einfach: a) Suchen Sie nach alten "Eiern" und b) verhindern Sie, dass neue erscheinen. Wir versuchen beide Punkte zu erfüllen. Die Suche nach alten „Eiern“ in unserem Land ist mit einer ständigen Codeoptimierung verbunden. Wir haben zwei der erfahrensten Entwickler für die nahezu Vollzeitoptimierung identifiziert. Sie finden in slow.log-Abfragen, die die meisten Datenbankressourcen verbrauchen, diese Abfragen und den Code um sie herum. Wir reduzieren die Wahrscheinlichkeit neuer Eier, indem wir den Optimalitätscode jedes Commits durch das oben erwähnte Sensei Rezrabotchiki überprüfen. Ihre Aufgabe ist es, auf Fehler hinzuweisen, die sich auf die Leistung auswirken. Erklären Sie, wie Sie es besser machen können, und übertragen Sie Wissen an andere Entwickler.
Irgendwann nach dem nächsten Osterei stellten wir fest, dass die Suche nach langsamen Abfragen gut ist, aber es lohnt sich, zusätzlich nach Abfragen zu suchen, die langsam aussehen, aber schnell funktionieren. Dies sind nur die nächsten Kandidaten, um im Falle eines explosiven Wachstums des nächsten Tisches alles zu platzieren.
5. Externe Ursachen
Dies sind Gründe, von denen wir glauben, dass sie von uns schlecht kontrolliert werden. Zum Beispiel:
- Trab mit Google Maps. Sie können dies umgehen, indem Sie die Nutzung dieses Dienstes überwachen, ein bestimmtes Lastniveau beobachten, das Wachstum der Last im Voraus planen und eine Erweiterung des Dienstes erwerben.
- Der Fall des Netzwerks im Rechenzentrum. Sie können sich fortbewegen, indem Sie eine Kopie des Dienstes im Backup-Rechenzentrum ablegen.
- Zahlungsdienstunfall. Sie können die Reservierung von Zahlungsdiensten umgehen.
- Fehlerhafte Verkehrsblockierung durch den DDoS-Schutzdienst. Sie können dies umgehen, indem Sie den Standard-DDoS-Schutzdienst deaktivieren und nur im Falle eines DDoS-Angriffs aktivieren.
Da die Beseitigung einer externen Ursache (per Definition) ein langwieriges und teures Unterfangen ist, haben wir gerade damit begonnen, Statistiken über Unfälle aufgrund externer Ursachen zu sammeln und auf die Anhäufung kritischer Massen zu warten. Es gibt kein Rezept zur Bestimmung der kritischen Masse. Es funktioniert nur Intuition. Wenn wir beispielsweise aufgrund von Problemen mit dem DDoS-Steuerungsdienst fünfmal in voller Ausfallzeit waren, wird es mit jedem nächsten Tropfen immer schärfer, die Frage nach einer Alternative zu stellen.
Auf der anderen Seite, wenn Sie es irgendwie schaffen können, dass es mit einem unzugänglichen externen Dienst funktioniert, dann tun wir es definitiv. Und dies hilft uns bei der Obduktion jedes Sturzes. Es muss immer eine Schlussfolgerung geben. Das heißt, Sie wollen immer nicht wollen, aber Sie können eine Problemumgehung finden.
6. Schlechte Veröffentlichung, fehlerhafte Funktionalität
Dies ist die unangenehmste Art von Unfall. Die einzige Art von Unfall, die für andere Symptome als Benutzer- / Geschäftsbeschwerden nicht sichtbar ist. Daher kann ein solcher Unfall, insbesondere wenn er nicht groß ist, lange Zeit unbemerkt in der Produktion auftreten.
Alle anderen Arten von Unfällen ähneln mehr oder weniger der „schlechten Freigabe, 500. Fehler“. Es ist nur so, dass der Auslöser keine Freigabe ist, sondern eine Last, eine manuelle Bedienung oder ein Problem auf der Seite eines externen Dienstes.
Um die Methode zur Behandlung dieser Art von Unfall zu beschreiben, genügt es, sich an eine bärtige Anekdote zu erinnern:
Mathematik und Physik wurde die gleiche Aufgabe angeboten: einen Wasserkocher kochen. Hilfsmittel sind vorhanden: Herd, Wasserkocher, Wasserhahn mit Wasser, Streichhölzer. Beide gießen abwechselnd Wasser in den Wasserkocher, schalten das Gas ein, zünden es an und setzen den Wasserkocher in Brand. Dann wurde die Aufgabe vereinfacht: Ein mit Wasser gefüllter Wasserkocher und ein Herd mit brennendem Gas wurden vorgeschlagen. Das Ziel ist das gleiche - Wasser zu kochen. Der Physiker setzt den Wasserkocher in Brand. Der Mathematiker gießt Wasser aus dem Wasserkocher, stellt das Gas ab und sagt: "Die Aufgabe wurde auf die vorherige reduziert." anekdotov.net
Diese Art von Unfall muss auf jeden Fall auf „schlechte Freisetzung, 500. Fehler“ reduziert werden. Idealerweise, wenn Fehler im Code als Fehler im Protokoll gespeichert wurden. Nun, oder zumindest Spuren in der Datenbank hinterlassen. Anhand dieser Spuren können Sie erkennen, dass ein Fehler aufgetreten ist, und sofort alarmieren. Wie kann man dazu beitragen? Wir haben begonnen, jeden größeren Fehler zu analysieren und Lösungen anzubieten. Welche Art von Überwachung / SMS-Warnung kann durchgeführt werden, damit sich dieser Fehler sofort auf die gleiche Weise wie der 500. Fehler manifestiert.
6.1. Beispiel
Es gab massive Beschwerden: Bestellungen, die über Apple Pay bezahlt wurden, werden nicht geschlossen. Sie begannen zu verstehen, das Problem wurde wiederholt. Wir haben den Grund gefunden: Wir haben das
expire date
für Bankkarten bei der Interaktion mit dem Erwerb verbessert. Infolgedessen haben sie begonnen, es speziell für Zahlungen über Apple Pay in dem Format zu übertragen, das vom Zahlungsverarbeitungsdienst erwartet wurde (tatsächlich ist eines behandelbar). etwas anderes verstümmeln), so dass alle Zahlungen über Apple Pay zurückgingen. Schnell behoben, ausgerollt, verschwand das Problem. Aber sie "lebten" 45 Minuten mit dem Problem.
Um den Spuren dieses Problems zu folgen, haben wir die Anzahl der fehlgeschlagenen Zahlungen über Apple Pay überwacht und eine SMS / IVR-Warnung mit einem Schwellenwert ungleich Null erstellt (da fehlgeschlagene Zahlungen aus Sicht des Dienstes die Norm sind, z. B. wenn der Kunde kein Geld auf der Karte hat oder die Karte blockiert ist). . Ab diesem Moment, wenn der Schwellenwert überschritten wird, lernen wir sofort das Problem kennen. Wenn die neue Version JEDES Problem in der Apple Pay-Verarbeitung verursacht, das zu einer teilweisen Inoperabilität des Dienstes führt, werden wir sofort davon erfahren, indem wir die Version überwachen und innerhalb von drei Minuten zurücksetzen (wie oben beschrieben, wie der manuelle Rolling-Prozess funktioniert). Es waren 45 Minuten Teilausfallzeit, es wurden 3 Minuten. Gewinn
6.2. Andere Beispiele
Wir haben die Optimierung der Liste der Bestellungen für Fahrer eingeführt. Ein Fehler hat sich in den Code eingeschlichen. Infolgedessen wurde den Fahrern in einigen Fällen die Liste der Bestellungen nicht angezeigt (sie war leer). Sie haben zufällig von dem Fehler erfahren - einer der Mitarbeiter hat die Anwendung des Fahrers geprüft. Schnell zurückgerollt. Als Schlussfolgerung aus dem Unfall haben wir ein Diagramm der durchschnittlichen Anzahl von Bestellungen in der Liste der Fahrer gemäß der Datenbank erstellt, das Diagramm einen Monat lang rückwirkend betrachtet, dort einen Fehler festgestellt und eine SMS-Warnung für die SQL-Abfrage erstellt, die dieses Diagramm bildet, wenn die durchschnittliche Anzahl von Bestellungen eingeht Die Liste unter dem ausgewählten Schwellenwert basiert auf dem historischen Minimum für den Monat.
Die Logik der Rückgabe von Cashback an Benutzer für Reisen wurde geändert. Einschließlich an die falsche Benutzergruppe verteilt. Wir haben das Problem behoben, einen Zeitplan für ausgezahlte Cashbacks erstellt, dort einen starken Anstieg festgestellt und festgestellt, dass es noch nie ein solches Wachstum gegeben hat. Wir haben eine SMS-Benachrichtigung gesendet.
Mit der Freigabe wurde die Funktionalität zum Schließen von Bestellungen unterbrochen (die Bestellung wurde für immer geschlossen, die Zahlung per Karte funktionierte nicht, die Fahrer forderten die Zahlung in bar von den Kunden). Das Problem betrug 1,5 Stunden (insgesamt passive und aktive Stadien). Das Problem haben wir vom Contact Center für Beschwerden erfahren. Sie nahmen eine Korrektur vor, überwachten und alarmierten die Schließzeit von Aufträgen mit Schwellenwerten, die aus dem Studium historischer Grafiken ermittelt wurden.
Wie Sie sehen können, ist die Herangehensweise an diese Art von Unfall immer dieselbe:
- Rollen Sie die Freigabe aus.
- Erfahren Sie mehr über das Problem.
- Repariere es.
- Wir bestimmen, anhand welcher Spuren (in der Datenbank, in den Protokollen, in Kiban) Sie die Anzeichen des Problems herausfinden können.
- Wir zeichnen diese Zeichen.
- Wir spulen es in die Vergangenheit zurück und schauen uns Bursts / Falls an.
- Wir wählen den richtigen Schwellenwert für die Warnung.
- Wenn ein Problem erneut auftritt, erfahren wir es sofort durch eine Warnung.
Das Schöne an dieser Methode: Eine große Klasse von Problemen wird sofort mit einem Diagramm und einer Warnung geschlossen (Beispiele für Problemklassen: Nichtabschluss von Bestellungen, zusätzliche Boni, Nichtzahlung über Apple Pay usw.).
Im Laufe der Zeit haben wir das Erstellen von Warnungen und die Überwachung für jeden größeren Fehler zu einem Teil der Entwicklungskultur gemacht. Um zu verhindern, dass diese Kultur verloren geht, haben wir sie ein wenig formalisiert. Für jeden Unfall forderten sie von sich selbst einen Bericht. Ein Bericht ist ein ausgefülltes Formular mit Antworten auf die folgenden Fragen: Grundursache, Eliminierungsmethode, Auswirkungen auf das Geschäft, Schlussfolgerungen. Alle Artikel sind erforderlich. Wenn Sie es wollen oder nicht, werden Sie daher die Schlussfolgerungen schreiben. Diese Prozessänderung wurde natürlich von do's & dont's aufgeschrieben.
7. Kotan
, , -, . - ( , ) «». «». :-)
«» :
. — , . , ( ), ( ) , . ( , ).
. , . , : — , — . , « 500- 1 %» — . « 500- 1 %, - , - , - » — . , . ( ). , : , «», , , , . — . ( , ). .
. . , ( , , ), , : , , , .
. , , ( , ).
8. ?
— . . : , . , , . , , , .. — , — ! , . , , ? , , .. , , .
. . ( , ), , : , , , . , , . . . -, , . , , , — : .
9.
, .
? | ? |
---|
.
| .
|
( ) post-mortem.
| .
|
do's & dont's.
| , , .
|
, 5 .
| .
|
, .
| .
|
.
| .
|
| .
|
.
| .
|
.
| .
|
SMS/IVR- .
| .
|
( ) .
| .
|
.
| - .
|
( — slow.log).
| - « ».
|
.
| .
|
.
| .
|
.
| , , .
|
«» — .
| , .
|
.
| .
|
, ! , , , , !