
Ceci est le deuxième article d'une série sur la façon dont nous, chez Citymobil, avons augmenté la stabilité du service (vous pouvez lire le premier
ici ). Dans cet article, je vais approfondir les spécificités de l'analyse des accidents. Mais avant cela, je vais couvrir un point que je devais penser à l'avance et couvrir dans le premier article, mais je n'y ai pas pensé. Et dont j'ai appris des retours des lecteurs. Le deuxième article me donne une chance d'éliminer ce défaut gênant.
0. Prologue
Un des lecteurs a posé une question très juste: "Qu'est-ce qui est difficile dans le backend d'un service de taxi?" La question est bonne. Je me suis posé la question l'été dernier avant de commencer à travailler chez Citymobil. J'ai alors pensé "pensez, un taxi, une application à trois boutons". Qu'est-ce qui pourrait être compliqué? Mais il s'est avéré qu'il s'agit d'un service de très haute technologie et d'un produit complexe. Afin au moins de préciser de quoi il s'agit et de ce qu'il s'agit vraiment d'un grand colosse technologique, je vais vous parler de plusieurs domaines d'activités de Citymobil:
- Tarification. L'équipe de tarification traite les problèmes de prix à tout moment et à tout moment. Le prix est déterminé en prédisant l'équilibre de l'offre et de la demande sur la base de statistiques et d'autres données. Tout cela fait un service vaste, complexe et en constante évolution basé sur l'apprentissage automatique.
- Tarification. La mise en place de différents modes de paiement, la logique des surtaxes après le voyage, la retenue des fonds sur les cartes bancaires, la facturation, l'interaction avec les partenaires et les chauffeurs.
- Distribution des commandes. À quelle machine distribuer la commande client? Par exemple, l'option de distribution pour la plus proche n'est pas la meilleure en termes d'augmentation du nombre de voyages. Une option plus correcte consiste à comparer les clients et les voitures de manière à maximiser le nombre de voyages, compte tenu de la probabilité d'annulation par ce client particulier dans ces conditions (car cela prend beaucoup de temps) et d'annulation ou de sabotage de la commande par ce conducteur (car cela prend trop de temps ou trop peu de temps). vérifier).
- Geo. Tout ce qui concerne la recherche et la plus grande adresse, les points d'atterrissage, l'ajustement des délais de livraison (nos partenaires, fournisseurs de cartes et embouteillages ne donnent pas toujours des informations précises sur l'ETA en tenant compte des embouteillages), l'amélioration de la précision du géocodage direct et inverse, l'amélioration de la précision de la machine. Il y a beaucoup de travail avec les données, beaucoup d'analyses, beaucoup de services basés sur l'apprentissage automatique.
- Antifraud. La différence de prix d'un voyage pour un passager et pour un chauffeur (par exemple, pour de courts trajets) crée une incitation économique pour les fraudeurs qui tentent de voler notre argent. La lutte contre la fraude est quelque peu similaire à la lutte contre le spam dans le service de messagerie - l'exhaustivité et l'exactitude sont importantes. Il est nécessaire de bloquer le nombre maximum de fraudeurs (exhaustivité), mais les bons utilisateurs ne doivent pas être confondus avec des fraudeurs (précision).
- Motivation des chauffeurs. L'équipe de motivation des conducteurs est engagée dans le développement de tout ce qui concerne l'augmentation de l'utilisation de notre plateforme par les conducteurs et la fidélisation des conducteurs en raison de différents types de motivation. Par exemple, faites X voyages et obtenez un Y roubles supplémentaires pour cela. Ou achetez un poste pour Z roubles et roulez sans commission.
- Application de pilote backend. Une liste de commandes, une carte de la demande (une indication où aller au chauffeur pour maximiser vos revenus), les changements de statut de Prokidyvaniya, un système de communication avec les chauffeurs et bien plus encore.
- Application client backend (c'est probablement la partie la plus évidente, et ce qui est généralement compris comme un backend de taxi): passer des commandes, faire défiler les statuts pour changer le statut de la commande, assurer le mouvement des voitures sur la carte sur la commande et à la livraison, des conseils sur le backend et etc.
Ce n'est là que la pointe de l'iceberg. La fonctionnalité est bien plus. L'interface conviviale cache une énorme partie sous-marine de l'iceberg.
Et maintenant, revenons aux accidents. Au cours des six mois de l'histoire des accidents, nous avons compilé la catégorisation suivante:
- mauvaise version, 500e erreurs;
- mauvaise version, code sous-optimal, charge sur la base;
- intervention manuelle infructueuse dans le système;
- œuf de Pâques;
- causes externes;
- mauvaise version, fonctionnalité cassée.
Ci-dessous, je noterai les conclusions que nous avons tirées sur les types d'accidents les plus courants.
1. Mauvaise version, 500e erreurs
Presque tout notre backend est écrit en PHP, un langage interprété avec un typage faible. Il arrive que vous déployiez le code et qu'il se bloque en raison d'une erreur dans le nom de la classe ou de la fonction. Et ce n'est qu'un exemple lorsque la 500e erreur apparaît. Il peut également apparaître en cas d'erreur logique dans le code; léché la mauvaise branche; accidentellement supprimé le dossier avec le code; laissé dans le code des artefacts temporaires nécessaires pour les tests; n'a pas modifié la structure des tableaux conformément au nouveau code; n'a pas redémarré ni arrêté les scripts cron nécessaires.
Nous avons lutté avec ce problème séquentiellement en plusieurs étapes. Les déplacements perdus en raison d'une mauvaise libération sont évidemment proportionnels à la durée d'utilisation. C'est-à-dire que nous devons faire de notre mieux pour nous assurer qu'une libération médiocre fonctionne le moins possible. Tout changement dans le processus de développement qui réduit le temps moyen nécessaire à la mise en service d'une mauvaise version d'au moins 1 seconde est positif pour l'entreprise et doit être mis en œuvre.
Une mauvaise version ou tout accident de production passe généralement par deux états, que nous avons appelés «stade passif» et «stade actif». La phase passive est celle où nous ne sommes pas encore au courant de l'accident. La phase active est lorsque nous sommes déjà au courant. L'accident commence au stade passif, et au fil du temps, lorsque nous le découvrons, l'accident passe au stade actif - nous commençons à le combattre: nous le diagnostiquons d'abord, puis le réparons.
Pour réduire la durée de tout accident de production, il est nécessaire de réduire la durée moyenne des phases passive et active. Il en va de même pour une mauvaise version, car c'est en soi une sorte d'accident.
Nous avons commencé à analyser notre processus actuel de réparation des accidents. Les mauvaises rejets que nous avons rencontrés au moment du début de l'analyse ont entraîné une moyenne (totale ou partielle) inactive de 20 à 25 minutes. La phase passive prenait généralement 15 minutes, la phase active 10 minutes. Pendant la phase passive, les plaintes des utilisateurs ont commencé à être traitées par le centre de contact, et après un certain seuil, le centre de contact s'est plaint des conversations générales dans Slack. Parfois, l'un des employés se plaignait de ne pas pouvoir commander un taxi. Une plainte des employés nous a signalé un problème grave. Après la transition d'une mauvaise version à la phase active, nous avons commencé à diagnostiquer le problème, analysé les dernières versions, divers graphiques et journaux pour déterminer la cause de l'accident. Après avoir découvert la raison, nous avons annulé le code si la mauvaise version a été pompée en dernier, ou avons effectué une nouvelle restauration avec un reverse commit de mauvaise version.
Voici un processus pour gérer les mauvaises versions, nous avons dû nous améliorer.
1.1. Réduction du stade passif
Tout d'abord, nous avons remarqué que si une mauvaise version s'accompagne de 500 erreurs, nous pouvons comprendre sans se plaindre qu'un problème est survenu. Heureusement, toutes les 500e erreurs ont été enregistrées dans New Relic (c'est l'un des systèmes de surveillance que nous utilisons), et il ne restait plus qu'à visser les notifications SMS et IVR sur le dépassement d'une certaine fréquence de «cinq cents» (au fil du temps, le seuil a été constamment réduit).
Cela a conduit au fait que la phase active de l'accident telle que "Mauvaise libération, 500ème erreur" a commencé presque immédiatement après la libération. Le processus en cas d'accident a commencé à ressembler à ceci:
- Le programmeur déploie le code.
- La libération conduit à un accident (500s massives).
- SMS arrive.
- Les programmeurs et les administrateurs commencent à comprendre (parfois pas immédiatement, mais après 2-3 minutes: les SMS peuvent être retardés, le son du téléphone peut être désactivé et la culture des actions immédiates après les SMS ne peut pas apparaître en une journée).
- La phase active de l'accident commence, qui dure les mêmes 10 minutes qu'avant.
Ainsi, la phase passive est passée de 15 minutes à 3.
1.2. Réduction supplémentaire de la phase passive
Malgré la réduction de la phase passive à 3 minutes, même une phase passive aussi courte nous dérangeait plus que la phase active, car pendant la phase active, nous faisons déjà quelque chose pour résoudre le problème, et pendant la phase passive, le service ne fonctionne pas en tout ou en partie, mais « les hommes ne savent pas. "
Pour réduire davantage la phase passive, nous avons décidé de sacrifier trois minutes de temps de développement après chaque version. L'idée était très simple: vous déployez le code et regardez New Relic, Sentry, Kibana pendant trois minutes pour voir s'il y a 500 erreurs. Dès que vous y voyez un problème, vous supposez a priori qu'il est lié à votre code et vous commencez à comprendre.
Nous avons choisi trois minutes sur la base des statistiques: parfois des problèmes sont apparus sur les cartes avec un retard de 1-2 minutes, mais il n'y a jamais eu plus de trois minutes.
Cette règle a été enregistrée dans les choses à faire et à ne pas faire. Au début, cela n'a pas toujours été exécuté, mais progressivement les développeurs se sont habitués à la règle de l'hygiène élémentaire: se brosser les dents le matin est également une perte de temps, mais vous devez le faire.
En conséquence, la phase passive a été réduite à 1 minute (les horaires étaient parfois encore en retard). Comme une agréable surprise, cela a simultanément réduit la phase active. Après tout, le développeur rencontre un problème en bon état et est prêt à restaurer immédiatement son code. Bien que cela n'aide pas toujours, car le problème aurait pu survenir en raison du code de quelqu'un d'autre qui était déployé en parallèle. Mais, néanmoins, la scène active a été réduite en moyenne à 5 minutes.
1.3. Réduction supplémentaire de la phase active
Plus ou moins satisfaits d'une minute de la phase passive, nous avons commencé à penser à une nouvelle réduction de la phase active. Tout d'abord, nous avons prêté attention à l'historique des problèmes (c'est la pierre angulaire de la construction de notre stabilité!) Et nous avons constaté que dans de nombreux cas, nous ne rétrogradons pas immédiatement parce que nous ne comprenons pas à quelle version revenir, car il existe de nombreuses versions parallèles. Pour résoudre ce problème, nous avons introduit la règle suivante (et l'avons enregistrée dans les choses à faire et à ne pas faire): avant la sortie, vous écrivez dans le chat dans Slack, ce pour quoi vous roulez et quoi, et en cas d'accident, vous écrivez dans le chat "accident, ne roule pas!". De plus, nous avons commencé à signaler automatiquement par SMS les faits concernant la publication pour informer ceux qui n'entrent pas dans le chat.
Cette règle simple a fortement réduit le nombre de rejets déjà lors d'accidents et réduit la phase active - de 5 minutes à 3.
1.4. Une réduction encore plus importante de la phase active
Malgré le fait que nous ayons averti dans le chat de toutes les versions et plantages, parfois des conditions de concurrence apparaissaient - l'une a écrit sur la sortie, et l'autre déjà déployée à ce moment; ou l'accident a commencé, ils ont écrit à ce sujet dans le chat, et quelqu'un vient de déployer un nouveau code. Ces circonstances ont allongé le diagnostic. Pour résoudre ce problème, nous avons implémenté une interdiction automatique des versions parallèles. L'idée est très simple: après chaque version, le système CI / CD interdit à tout le monde de se déployer pendant les 5 prochaines minutes, à l'exception de l'auteur de la dernière version (afin qu'il puisse rouler ou rouler le correctif si nécessaire) et plusieurs développeurs particulièrement expérimentés (en cas d'urgence). De plus, le système CI / CD interdit le déploiement lors d'un accident (c'est-à-dire du moment de la réception de la notification du début de l'accident au moment de la réception de la notification de son achèvement).
Ainsi, le processus est devenu comme ceci: le développeur déploie, surveille les graphiques pendant trois minutes, et ensuite pendant deux minutes de plus, personne ne peut rien déployer. En cas de problème, le développeur annule la version. Cette règle a radicalement simplifié le diagnostic et la durée totale des stades actif et passif est passée de 3 + 1 = 4 minutes à 1 + 1 = 2 minutes.
Mais deux minutes de l'accident, c'est beaucoup. Par conséquent, nous avons continué d'optimiser le processus.
1.5. Détection et restauration automatiques des collisions
Nous réfléchissons depuis longtemps à la façon de réduire la durée de l'accident dû à de mauvaises rejets. Ils ont même essayé de se forcer à regarder dans
tail -f error_log | grep 500
tail -f error_log | grep 500
. Mais finalement, ils se sont tous mis d'accord sur une solution automatique cardinale.
En bref, il s'agit d'une restauration automatique. Nous avons configuré un serveur Web distinct, sur lequel nous avons chargé 10 fois moins de charge de l'équilibreur que sur d'autres serveurs Web. Chaque version a été automatiquement déployée par le système CI / CD sur ce serveur séparé (nous l'avons appelé préprod, bien que, malgré son nom, la charge réelle des utilisateurs réels y soit allée). Et puis l'automatisation a fait
tail -f error_log | grep 500
tail -f error_log | grep 500
. Si aucune 500e erreur ne s'est produite en une minute, le CI / CD a déployé le nouveau code en production. Si des erreurs sont apparues, le système a immédiatement tout annulé. Dans le même temps, au niveau de l'équilibreur, toutes les demandes traitées avec 500 erreurs sur le préprod ont été dupliquées sur l'un des serveurs de production.
Cette mesure a réduit à zéro l'effet des cinq centièmes rejets. Dans le même temps, en cas de bugs dans l'automatisation, nous n'avons pas annulé la règle pendant trois minutes pour surveiller les graphiques. C'est tout sur les mauvaises versions et le 500e bogue. Nous procédons au prochain type d'accident.
2. Mauvaise version, code sous-optimal, charge de base
Je vais commencer tout de suite par un exemple concret d'un accident de ce type. Nous avons déployé l'optimisation: nous avons ajouté
USE INDEX
à la requête SQL, lors du test de ces requêtes courtes accélérées, comme en production, mais les requêtes longues ont ralenti. Le ralentissement des longues requêtes n'a été observé qu'en production. En conséquence, le flux de longues demandes a mis toute la base principale pendant une heure. Nous avons bien compris comment fonctionne
USE INDEX
, l'avons décrit dans le fichier des choses à faire et à ne pas faire et avons mis les développeurs en garde contre toute utilisation abusive. Nous avons également analysé la requête et réalisé qu'elle renvoie principalement des données historiques, ce qui signifie qu'elle peut être exécutée sur une réplique distincte pour les requêtes historiques. Même si cette réplique est sous charge, l'entreprise ne s'arrêtera pas.
Après cet incident, nous avons toujours rencontré des problèmes similaires et, à un moment donné, nous avons décidé d'aborder le problème de manière systématique. Nous avons scanné l'intégralité du code avec un peigne fréquent et effectué sur la réplique toutes les demandes qui peuvent y être faites sans compromettre la qualité du service. Dans le même temps, nous avons divisé les répliques elles-mêmes en fonction des niveaux de criticité afin que la chute de l'une d'entre elles n'arrête pas le service. En conséquence, nous sommes arrivés à une architecture qui a les bases suivantes:
- base principale (pour les opérations d'écriture et pour les requêtes qui sont supercritiques à la fraîcheur des données);
- réplique de production (pour les requêtes courtes qui sont un peu moins critiques pour la fraîcheur des données);
- réplique pour le calcul des ratios de prix, le soi-disant prix de surtension. Cette réplique peut accuser un retard de 30 à 60 secondes - ce n'est pas critique, les coefficients ne changent pas si souvent, et si cette réplique tombe, le service ne s'arrêtera pas, juste les prix ne correspondront pas tout à fait à l'équilibre entre l'offre et la demande;
- réplique pour le panneau d'administration des utilisateurs professionnels et le centre de contact (si elle tombe, l'activité principale ne augmentera pas, mais le support ne fonctionnera pas et nous ne pourrons pas afficher et modifier temporairement les paramètres);
- de nombreuses répliques pour l'analyse;
- Base de données MPP pour l'analyse lourde avec des tranches complètes selon les données historiques.
Cette architecture nous a donné plus d'espace pour la croissance et a réduit le nombre de plantages d'un ordre de grandeur en raison de requêtes SQL sous-optimales. Mais elle est encore loin d'être idéale. Prévoit de faire du sharding afin que vous puissiez mettre à jour les mises à jour et les suppressions, ainsi que de courtes requêtes supercritiques à la fraîcheur de ces données. La marge de sécurité de MySQL n'est pas infinie. Bientôt, nous aurons besoin d'artillerie lourde sous la forme d'un Tarantool. À ce sujet sera nécessaire dans les articles suivants!
Au cours de l'essai avec du code et des requêtes non optimaux, nous avons réalisé ce qui suit: il est préférable d'éliminer toute non-optimalité avant la publication, et non après. Cela réduit le risque d'accident et réduit le temps consacré par les développeurs à l'optimisation. Parce que si le code a déjà été téléchargé et qu'il y a de nouvelles versions, il est beaucoup plus difficile à optimiser. En conséquence, nous avons introduit une vérification de code obligatoire pour l'optimalité. Elle est menée par les développeurs les plus expérimentés, en fait, nos forces spéciales.
De plus, nous avons commencé à collecter chez do's & don't's les meilleures méthodes d'optimisation de code qui fonctionnent dans nos réalités, elles sont listées ci-dessous. Veuillez ne pas percevoir ces pratiques comme une vérité absolue et n'essayez pas de les répéter aveuglément en vous-mêmes. Chaque méthode n'a de sens que pour une situation spécifique et une entreprise spécifique. Ils sont donnés ici à titre d'exemple, de sorte que les détails soient clairs:
- Si la requête SQL ne dépend pas de l'utilisateur actuel (par exemple, une carte de demande pour les conducteurs indiquant les taux de trajets minimum et les coefficients pour les polygones), alors cette requête doit être effectuée par cron avec une certaine fréquence (dans notre cas, une fois par minute suffit). Écrivez le résultat dans le cache (Memcached ou Redis), qui est déjà utilisé dans le code de production.
- Si la requête SQL fonctionne avec des données dont le backlog n'est pas critique pour l'entreprise, son résultat doit être mis en cache avec du TTL (par exemple, 30 secondes). Et puis dans les requêtes suivantes lues dans le cache.
- Si dans le cadre du traitement d'une requête sur le web (dans notre cas, dans le cadre de l'implémentation d'une méthode serveur spécifique en PHP) vous souhaitez effectuer une requête SQL, vous devez vous assurer que ces données ne sont pas «arrivées» avec une autre requête SQL (et s'ils viendront plus loin par code). La même chose s'applique à l'accès au cache: il peut également être inondé de demandes si vous le souhaitez, donc si les données sont déjà "arrivées" du cache, alors vous n'avez pas besoin d'aller dans le cache comme chez vous et d'en prendre, qui est déjà emporté.
- Si, dans le contexte du traitement des requêtes sur le Web, vous souhaitez appeler n'importe quelle fonction, vous devez vous assurer qu'aucune requête SQL supplémentaire ou accès au cache ne sera effectuée dans ses abats. Si l'appel d'une telle fonction est inévitable, vous devez vous assurer qu'elle ne peut pas être modifiée ou sa logique rompue afin de ne pas faire de requêtes inutiles aux bases de données / caches.
- Si vous devez toujours entrer dans SQL, vous devez vous assurer que vous ne pouvez pas ajouter les champs nécessaires plus haut ou plus bas dans le code aux requêtes qui existent déjà dans le code.
3. Intervention manuelle infructueuse dans le système
Exemples de tels accidents: un ALTER infructueux (qui a surchargé la base de données ou provoqué un décalage de réplique) ou DROP infructueux (a rencontré un bogue dans MySQL, a bloqué la base de données lorsqu'une nouvelle table a été supprimée); lourde demande de maître faite par erreur à la main; Nous avons effectué un travail sur le serveur sous charge, bien que nous pensions qu'il était sans travail.
Pour minimiser les chutes pour ces raisons, il est malheureusement nécessaire de comprendre à chaque fois la nature de l'accident. Nous n'avons pas encore trouvé la règle générale. Encore une fois, essayez les exemples. Disons qu'à un moment donné, les coefficients de surtension ont cessé de fonctionner (ils multiplient le prix du voyage au lieu et au moment de l'augmentation de la demande). La raison en était que sur la réplique de la base de données, d'où provenaient les données de calcul des coefficients, le script Python fonctionnait, qui consommait toute la mémoire, et la réplique était en panne. Le script fonctionne depuis longtemps, il a fonctionné sur une réplique juste pour plus de commodité. Le problème a été résolu en redémarrant le script. Les conclusions étaient les suivantes: ne pas exécuter de scripts tiers sur une machine avec une base de données (enregistrés dans les choses à faire et à ne pas faire, sinon c'est un plan vierge!), Surveiller la fin de la mémoire sur une machine avec une réplique et alerter par SMS si la mémoire est bientôt épuisée.
Il est très important de toujours tirer des conclusions et de ne pas se glisser dans une situation confortable "ils ont vu un problème, l'ont corrigé et l'ont oublié". Un service de qualité ne peut être construit que si des conclusions sont tirées. De plus, les alertes SMS sont très importantes - elles placent la qualité de service à un niveau supérieur à ce qu'elle a fait, l'empêchent de chuter et améliorent encore la fiabilité. En tant que grimpeur de chaque état stable, il se redresse et est fixé dans un autre état stable, mais à une altitude plus élevée.
La surveillance et l'alerte avec des crochets en fer invisibles mais rigides taillent dans la roche de l'incertitude et ne nous laissent jamais tomber en dessous du niveau de stabilité que nous avons fixé, que nous élevons constamment.
4. Oeuf de Pâques
Ce que nous appelons "l'œuf de Pâques" est une bombe à retardement qui existe depuis longtemps, mais que nous n'avons pas rencontrée. En dehors de cet article, ce terme fait référence à une fonctionnalité non documentée faite exprès. Dans notre cas, ce n'est pas du tout une fonctionnalité, mais plutôt un bug, mais qui fonctionne comme une bombe à retardement et qui est un effet secondaire de bonnes intentions.
Par exemple: débordement 32 bits
auto_increment
; non-optimalité dans le code / configuration, "tir" en raison de la charge; réplique en retard (généralement soit en raison d'une demande sous-optimale pour une réplique déclenchée par un nouveau modèle d'utilisation, soit d'une charge plus élevée, soit en raison d'une mise à jour sous-optimale sur le maître qui a été appelée par un nouveau modèle de charge et a chargé la réplique).
Un autre type populaire d'oeuf de Pâques est le code non optimal, et plus précisément, la requête SQL non optimale. Auparavant, la table était plus petite et la charge moins importante - la requête fonctionnait bien. Et avec l'augmentation du tableau, linéaire dans le temps et la croissance de la charge, linéaire dans le temps, la consommation de ressources du SGBD a augmenté de façon quadratique. Habituellement, cela conduit à un effet négatif marqué: tout était «ok» et bang.
Des scénarios plus rares sont une combinaison d'oeufs de bogue et de Pâques. Une version avec un bogue a entraîné une augmentation de la taille de la table ou une augmentation du nombre d'enregistrements dans une table d'un certain type, et un œuf de Pâques déjà existant a provoqué une charge excessive sur la base de données en raison de requêtes plus lentes sur cette table envahie.
Cependant, nous avons également eu des œufs de Pâques, sans rapport avec la charge. Par exemple,
auto increment
32 bits: après deux et quelques milliards d'enregistrements dans le tableau, les insertions cessent d'être effectuées. Le domaine de
auto increment
dans le monde moderne doit donc être rendu 64 bits. Nous avons bien appris cette leçon.
Comment gérer les œufs de Pâques? La réponse est simple: a) recherchez les anciens "œufs", et b) empêchez les nouveaux d'apparaître. Nous essayons de remplir les deux points. La recherche d'anciens «œufs» dans notre pays est associée à une optimisation constante du code. Nous avons identifié deux des développeurs les plus expérimentés pour l'optimisation presque à plein temps. Ils trouvent dans les requêtes slow.log qui consomment le plus de ressources de base de données, optimisent ces requêtes et le code qui les entoure. Nous réduisons la probabilité de nouveaux œufs en vérifiant le code d'optimalité de chaque commit par le sensei rezrabotchiki susmentionné. Leur tâche consiste à signaler les erreurs qui affectent les performances; vous dire comment faire mieux et transférer les connaissances à d'autres développeurs.
À un certain moment après le prochain œuf de Pâques que nous avons trouvé, nous avons réalisé que la recherche de requêtes lentes est bonne, mais il serait utile de rechercher en outre des requêtes qui ressemblent à lentes mais fonctionnent rapidement. Ce ne sont que les prochains candidats pour tout mettre en cas de croissance explosive de la prochaine table.
5. Causes externes
Ce sont des raisons que nous pensons être mal contrôlées par nous. Par exemple:
- Trot par Google Maps. Vous pouvez le contourner en surveillant l'utilisation de ce service, en observant un certain niveau de charge sur celui-ci, en planifiant à l'avance la croissance de la charge et en achetant l'extension du service.
- La chute du réseau dans le centre de données. Vous pouvez vous déplacer en plaçant une copie du service dans le centre de données de sauvegarde.
- Accident de service de paiement. Vous pouvez contourner la réservation des services de paiement.
- Blocage du trafic erroné par le service de protection DDoS. Vous pouvez vous déplacer en désactivant le service de protection DDoS par défaut et en l'activant uniquement en cas d'attaque DDoS.
Étant donné que l'élimination d'une cause externe est une entreprise longue et coûteuse (par définition), nous venons de commencer à collecter des statistiques sur les accidents dus à des causes externes et à attendre l'accumulation de masse critique. Il n'y a pas de recette pour déterminer la masse critique. Cela fonctionne simplement par intuition. Par exemple, si nous étions 5 fois en temps d'arrêt complet en raison de problèmes, par exemple, du service de contrôle DDoS, à chaque nouvelle baisse, cela deviendra de plus en plus aigu pour poser la question d'une alternative.
D'un autre côté, si vous pouvez en quelque sorte le faire fonctionner avec un service externe inaccessible, nous le faisons définitivement. Et cela nous aide à effectuer une analyse post mortem de chaque chute. Il doit toujours y avoir une conclusion. Cela signifie que vous voulez toujours ne pas vouloir, mais vous pouvez trouver une solution de contournement.
6. Mauvaise version, fonctionnalité cassée
Il s'agit du type d'accident le plus désagréable. Le seul type d'accident qui n'est visible pour aucun symptôme autre que les plaintes des utilisateurs / entreprises. Par conséquent, un tel accident, surtout s'il n'est pas important, peut durer longtemps sans être remarqué.
Tous les autres types d'accidents s'apparentent plus ou moins à «mauvaise libération, 500e erreur». C'est juste que le déclencheur n'est pas une version, mais une charge, une opération manuelle ou un problème du côté d'un service externe.
Pour décrire la méthode de traitement de ce type d'accident, rappelez-vous simplement la blague barbu:
Les mathématiques et la physique se sont vu proposer la même tâche: faire bouillir une bouilloire. Des outils auxiliaires sont fournis: cuisinière, bouilloire, robinet d'eau avec de l'eau, allumettes. Les deux versent de l'eau en alternance dans la bouilloire, ouvrent le gaz, l'allument et mettent le feu à la bouilloire. Ensuite, la tâche a été simplifiée: une bouilloire remplie d'eau et un poêle à gaz ont été proposés. Le but est le même - faire bouillir de l'eau. Le physicien met le feu à la bouilloire. Le mathématicien verse de l'eau de la bouilloire, coupe le gaz et dit: "La tâche a été réduite à la précédente." anekdotov.net
Ce type d'accident doit être réduit par tous les moyens à une «mauvaise libération, 500e erreur». Idéalement, si des bogues dans le code étaient enregistrés dans le journal comme une erreur. Eh bien, ou du moins laissé des traces dans la base de données. À partir de ces traces, vous pouvez comprendre qu'un bogue s'est produit et alerter immédiatement. Comment y contribuer? Nous avons commencé à analyser chaque bug majeur et à proposer des solutions, quel type de surveillance / alerte SMS peut être fait pour que ce bug se manifeste immédiatement de la même manière que la 500ème erreur.
6.1. Exemple
Les plaintes ont été massives: les commandes payées via Apple Pay ne se ferment pas. Ils ont commencé à comprendre, le problème s'est répété. Nous avons trouvé la raison: nous avons amélioré le format de
expire date
des cartes bancaires lors de l'interaction avec l'acquisition, à la suite de quoi ils ont commencé à le transférer spécifiquement pour les paiements via Apple Pay dans le format attendu du service de traitement des paiements (en fait, l'un est traitable, mutiler quelque chose d'autre), donc tous les paiements via Apple Pay ont commencé à décliner. Rapidement corrigé, déployé, le problème a disparu. Mais ils "ont vécu" le problème pendant 45 minutes.
Suite aux traces de ce problème, nous avons surveillé le nombre de paiements échoués via Apple Pay, et également effectué une alerte SMS / IVR avec un seuil non nul (car les paiements échoués sont la norme du point de vue du service, par exemple, le client n'a pas d'argent sur la carte ou la carte est bloquée) . A partir de ce moment, lorsque le seuil est dépassé, nous apprenons instantanément le problème. Si la nouvelle version introduit un problème dans le traitement d'Apple Pay, ce qui entraînera une inopérabilité du service, même partielle, nous en apprendrons immédiatement la surveillance et annulons la version dans les trois minutes (il est décrit ci-dessus comment fonctionne le processus de roulement manuel). C'était 45 minutes de temps d'arrêt partiel, c'est devenu 3 minutes. Bénéfice
6.2. Autres exemples
Nous avons déployé l'optimisation de la liste des commandes proposées aux conducteurs. Un bug s'est glissé dans le code. En conséquence, les conducteurs dans certains cas n'ont pas vu la liste des commandes (elle était vide). Ils ont découvert le bogue par accident - l'un des employés a examiné la demande du conducteur. Recul rapidement. En conclusion de l'accident, nous avons fait un graphique du nombre moyen de commandes dans la liste des pilotes selon la base de données, regardé le graphique rétroactivement pendant un mois, y avons vu un échec et fait une alerte SMS pour la requête SQL, qui forme ce graphique lorsque le nombre moyen de commandes dans la liste sous le seuil sélectionné en fonction du minimum historique pour le mois.
Modification de la logique de remboursement aux utilisateurs pour les voyages. Y compris distribué au mauvais groupe d'utilisateurs. Nous avons résolu le problème, établi un calendrier des remises en argent distribuées, y avons vu une forte augmentation, avons également constaté qu'il n'y avait jamais eu une telle croissance, avons lancé une alerte SMS.
Avec la publication, la fonctionnalité de clôture des commandes a été rompue (la commande a été fermée pour toujours, le paiement par carte n'a pas fonctionné, les chauffeurs ont exigé le paiement en espèces des clients). Le problème était de 1,5 heure (total des étapes passives et actives). Nous avons appris le problème du centre de contact des plaintes. Ils ont fait une correction, fait un suivi et des alertes sur l'heure de clôture des commandes avec des seuils trouvés à partir de l'étude des graphiques historiques.
Comme vous pouvez le constater, l'approche de ce type d'accident est toujours la même:
- Déployez la version.
- Découvrez le problème.
- Réparez-le.
- Nous déterminons par quelles traces (dans la base de données, les journaux, Kiban) vous pouvez trouver les signes du problème.
- Nous traçons ces signes.
- Nous le rembobinons dans le passé et regardons les explosions / chutes.
- Nous sélectionnons le seuil correct pour l'alerte.
- Lorsqu'un problème survient à nouveau, nous en prenons immédiatement connaissance grâce à une alerte.
Ce qui est bien avec cette méthode: une énorme classe de problèmes est fermée immédiatement avec un graphique et une alerte (exemples de classes de problèmes: non-clôture des commandes, bonus supplémentaires, non-paiement via Apple Pay, etc.).
Au fil du temps, nous avons intégré les alertes de construction et la surveillance de chaque bug majeur dans la culture de développement. Pour éviter que cette culture ne se perde, nous l'avons un peu formalisée. Pour chaque accident, ils ont commencé à exiger d'eux un rapport. Un rapport est un formulaire rempli avec des réponses aux questions suivantes: cause profonde, méthode d'élimination, impact sur l'entreprise, conclusions. Tous les articles sont obligatoires. Par conséquent, si vous le souhaitez ou non, vous écrirez les conclusions. Ce changement de processus, bien sûr, a été écrit par les choses à faire et à ne pas faire.
7. Kotan
, , -, . - ( , ) «». «». :-)
«» :
. — , . , ( ), ( ) , . ( , ).
. , . , : — , — . , « 500- 1 %» — . « 500- 1 %, - , - , - » — . , . ( ). , : , «», , , , . — . ( , ). .
. . , ( , , ), , : , , , .
. , , ( , ).
8. ?
— . . : , . , , . , , , .. — , — ! , . , , ? , , .. , , .
. . ( , ), , : , , , . , , . . . -, , . , , , — : .
9.
, .
? | ? |
---|
.
| .
|
( ) post-mortem.
| .
|
do's & dont's.
| , , .
|
, 5 .
| .
|
, .
| .
|
.
| .
|
| .
|
.
| .
|
.
| .
|
SMS/IVR- .
| .
|
( ) .
| .
|
.
| - .
|
( — slow.log).
| - « ».
|
.
| .
|
.
| .
|
.
| , , .
|
«» — .
| , .
|
.
| .
|
, ! , , , , !