Un article sur la façon de résoudre le problème de l'optimisation du processus de suppression de fichiers d'un système fragmenté. Il s'agit d'un projet de partage et de travail avec des fichiers. Le système était une startup il y a environ 8 ans, puis il a tourné avec succès et a été vendu plusieurs fois. Le projet a 4 développeurs qui sont avec le projet dès le début, ce qui est très précieux. Traditionnellement, la documentation n'a pas eu le temps d'écrire ou elle n'est pas très pertinente.
Pourquoi voudriez-vous lire ceci et pourquoi ai-je écrit tout cela? Je voudrais parler d'un râteau qui se trouve soigneusement à l'intérieur du système et qui bat pour que les étoiles débordent des yeux.
Je tiens à dire un grand merci à
Hanna_Hlushakova pour avoir travaillé ensemble,
mené le projet à son terme et aidé à préparer l'article. Fondamentalement, vous trouverez des descriptions du problème et de l'algorithme pour le résoudre, que nous avons utilisés, il n'y a pas d'exemples de code, de structures de données ou d'autres choses nécessaires. Je ne sais pas si mon expérience vous aidera à éviter un râteau à la maison, mais j'espère que vous obtiendrez quelque chose d'utile. Cet article sera peut-être une perte absolument irrévocable de temps précieux.

À propos du projet
Le projet est l'un des leaders de la place Gartner, a des entreprises clientes avec plus de 300 000 employés aux États-Unis et en Europe, et plusieurs milliards de fichiers pour la maintenance.
Les technologies suivantes sont utilisées dans le projet: Microsoft, serveurs C # .net, base de données MS SQL, 14 serveurs actifs + 14 en mode miroir de données.
Le volume des bases de données peut atteindre 4 To, la charge constante en heures ouvrables est d'environ 400 000 requêtes par minute.
Il y a beaucoup de logique métier dans la base de données:
450 tables
1000 procédures stockées
80 000 lignes de code SQL
Traditionnellement, soit ils n'avaient pas le temps de rédiger la documentation, soit elle n'est pas pertinente.
À propos de la tâche
La tâche consiste à refaire la suppression des fichiers du stockage qui ont déjà été supprimés par les clients, et la période de stockage des fichiers supprimés a expiré, au cas où ils souhaiteraient être restaurés. Dans la version actuelle, selon les calculs de l'entreprise elle-même, les fichiers supprimés il y a 1 an étaient stockés sur les serveurs, bien que selon les conditions générales de vente, ils n'auraient dû être stockés que 1 mois. Étant donné que certains fichiers sont stockés sur S3, la société a payé les données supplémentaires et les clients qui ont utilisé le stockage sur site se demandaient pourquoi les fichiers prenaient plus d'espace qu'ils ne le devraient.
Bases de données shardovany pour le client de l'entreprise.
Comment le retrait a-t-il fonctionné plus tôt?

Sur un serveur global contenant des informations sur tous les fichiers du système, des plages de 15 000 identifiants de fichiers ont été formées.
Ensuite, selon le calendrier, une enquête sur le serveur a été lancée pour une gamme d'identifiants de fichiers.
Les limites de l'aire de répartition ont été transmises à chaque fragment.
Shard en réponse a envoyé des fichiers trouvés de la plage.
Le serveur principal a ajouté les fichiers manquants à la table de file d'attente pour suppression dans sa base de données.
Ensuite, à partir de la table de file d'attente, le service de suppression de fichiers physiques du stockage a reçu un tas d'identifiants à supprimer, après quoi il a envoyé une confirmation qu'il allait supprimer les fichiers et une vérification a été lancée pour tous les fragments si ces fichiers y étaient utilisés.
Avec une augmentation du nombre de fichiers, cette approche a commencé à fonctionner très lentement, car il y avait plusieurs milliards de fichiers et le nombre de plages a considérablement augmenté. Les fichiers supprimés sont toujours restés inférieurs à 5% par rapport au nombre total, respectivement, il est très inefficace de trier des milliards de fichiers pour trouver plusieurs millions de fichiers supprimés.
Par exemple, généralement après qu'un utilisateur a supprimé un fichier, il doit être stocké pendant 1 mois, au cas où il devrait être restauré. Après cette période, le fichier doit être supprimé par le programme du référentiel. Avec le nombre actuel de fichiers, le nombre de plages et la vitesse de contournement des plages, il faudrait 1 an au serveur pour contourner complètement toutes les plages.
Il est clair que l'endroit n'a pas été libéré, ce qui a provoqué l'insatisfaction des utilisateurs, car leurs serveurs stockaient beaucoup plus de fichiers que ce qui aurait dû être signalé. La société de services a elle-même payé une place supplémentaire sur S3, ce qui a été une perte directe pour elle.
Seulement sur S3 au moment où le travail a commencé, 2 pétaoctets de fichiers supprimés ont été stockés, et ce n'est que sur le cloud. De plus, il y avait des clients dont les fichiers étaient stockés sur leurs serveurs dédiés, qui avaient le même problème: l'espace du serveur était occupé par des fichiers supprimés par les utilisateurs mais pas supprimés du serveur.
Qu'avez-vous décidé de faire?
Nous avons décidé de suivre les événements de suppression:
- le client a supprimé le fichier, puis a expiré.
- l'utilisateur a supprimé le fichier immédiatement sans possibilité de récupération.
Lors de la suppression d'un fichier d'une base de données fragmentée, nous avons décidé d'utiliser une approche optimiste et de supprimer l'un des contrôles à utiliser. Nous savions que 99% des fichiers ne sont utilisés que dans un seul fragment. Nous avons décidé d'ajouter immédiatement le fichier à la file d'attente de suppression et de ne pas vérifier le reste des fragments pour l'utilisation de ce fichier, car la vérification sera à nouveau effectuée lorsque la suppression du magasin confirmera le service.
De plus, ils ont laissé le JOB actuel qui vérifiait les fichiers supprimés par plages afin d'ajouter des fichiers qui ont été supprimés avant la sortie de la nouvelle version.
Tout ce qui a été supprimé sur le fragment est collecté dans une table puis transféré sur un seul serveur, avec des informations sur tous les fichiers.
Sur ce serveur, il est envoyé à la table de suppression de file d'attente.
Dans le tableau de suppression avant suppression, il est vérifié que le fichier n'est pas utilisé sur tous les fragments. Cette partie du chèque était là avant le changement de code, et ils ont décidé de ne pas y toucher.
De quoi aviez-vous besoin de changer dans le code?
Sur chacun des fragments, une table a été ajoutée dans laquelle l'identifiant du fichier supprimé doit être écrit.
Nous avons trouvé toutes les procédures pour supprimer des fichiers de la base de données, il n'y en avait que 2. Après que l'utilisateur a supprimé le fichier, le fichier se trouve toujours dans la base de données pendant un certain temps.
Dans la procédure de suppression d'un fichier de la base de données, nous avons ajouté une entrée à cette table locale avec les fichiers supprimés.
Sur le serveur global avec des fichiers, ils ont fait un JOB qui télécharge une liste de fichiers à partir de bases de données de fragments. Il suffit d'appeler une procédure à partir d'une base de données partageable, cela fait un DELETE à l'intérieur et affiche une liste de fichiers dans OUTPUT. Dans MS SQL Server pull - tirer à partir d'un serveur distant est beaucoup plus rapide que l'insérer sur un serveur distant. Tout cela se fait en blocs.
Ces fichiers sont ensuite ajoutés à la table de suppression de files d'attente sur le serveur global.
Une table avec un identificateur de fragment a été ajoutée à la table de file d'attente pour savoir d'où venait l'événement de suppression.
Comment tout le monde l'a-t-il testé?
Il existe 3 environnements:
Le développement est un environnement de développement. Le code est tiré de la branche develop du gith. Il est possible de déployer une version différente du code sur IIS et de créer plusieurs versions d'orchestration. Il se connectera à l'environnement de développement à partir du client dans vpn. Jusqu'à récemment, l'inconvénient ne concernait que les bases de données, car toutes les modifications apportées à la base de données peuvent interrompre le travail d'autres parties du système. Ensuite, les bases ont été rendues locales. Le code déjà en cours d'exécution peut être versé sur des serveurs de développement avec des bases de données afin de ne pas ruiner le travail de tout le monde. Dans un environnement de développement, il y a 3 fragments, au lieu de 12, qui sont sur la prod, mais cela suffit généralement pour tester l'interaction.
Staging - l'environnement est le même avec le prod selon la version du code (presque le même, car c'est rare, mais il y a des changements directement sur le prod par les administrateurs). Une copie du code de la branche maître. Parfois, certaines différences avec le code du prod ont été détectées dans la base de données, mais en général, elles sont identiques. Il y a aussi 3 fragments sur Staging ainsi que sur la jeune fille. Il n'y a pas de charge sur la mise en scène ainsi que sur le développement. Ici, vous pouvez exécuter des tests d'intégration déjà complètement, car le code correspond au prod. Tous les tests doivent réussir, c'est une condition préalable avant de passer au déploiement.
Perf lab, où les tests sont effectués sous charge. La charge est créée à l'aide de jmeter, 10 fois moins que sur la prod, et il n'y a qu'un seul fragment, ce qui crée parfois des inconvénients. Les données de vente sont prises, puis anonymisées et utilisées sur perf lab. Tous les serveurs de la même configuration que sur prod.
La charge est 10 fois inférieure, car on suppose qu'il s'agit d'une charge approximative qui vient à la prod pour 1 fragment. L'inconvénient est que la base mondiale est très sous-utilisée, contrairement aux ventes. Et, si les modifications concernent principalement la base de données globale avec des fichiers, vous ne pouvez compter sur les résultats des tests qu'environ - sur le prod cela peut ne pas fonctionner comme ça. Bien que perf lab ne corresponde pas idéalement à la charge avec la prod, la possibilité de tester sous la charge aide déjà à détecter de nombreuses erreurs avant de déployer sur la prod.
Il existe également un serveur de sauvegarde où vous pouvez voir les données de la vente afin de détecter certains cas. En général, la société fonctionne sous une licence qui interdit aux développeurs de donner accès aux données de vente et à l'équipe d'administration et de support (Opérations) d'accéder au développement, vous devez donc demander l'aide des administrateurs de base de données. Les données de vente rendent les tests très faciles, car certains cas se produisent uniquement sur les données de production et il est très utile d'étudier les données dans un système réel afin de comprendre comment le système fonctionne pour l'utilisateur.
Lors des tests sur perf lab, il s'est avéré que la charge de suppression des fichiers du stockage n'était pas du tout réalisée à partir du mot. Lors de la mise en œuvre du test de charge, nous avons choisi des demandes plus populaires du logiciel client, dont certaines n'étaient pas incluses dans le nettoyage du stockage. Puisqu'il s'agit d'une base de données, il s'est avéré effectuer un test simplifié pour tous les objets modifiés avec l'appel des procédures modifiées sur différentes données manuellement. (sur les options que je connaissais).
De plus, dans les tests d'intégration et de performance, l'accent a été mis sur le type de stockage de fichiers le plus populaire.
Une caractéristique supplémentaire du laboratoire de perf, qui n'a pas été immédiatement découverte, est l'inadéquation de la quantité de données dans certains tableaux sur le prod et le perf. En ce sens que tous les emplois de vente travaillent sur la performance, qui génèrent des données, mais il n'y a pas toujours quelque chose qui traite les données générées dans la table. Et, par exemple, la file d'attente de table susmentionnée pour la suppression sur la perf est beaucoup plus grande que sur la prod - 20 millions d'enregistrements sur la perf et 200 000 enregistrements sur la prod.
Processus de déploiement
Le processus de déploiement est assez standard. Aucune modification n'a été apportée au code d'application pour cette tâche, toutes les modifications se trouvent uniquement dans la base de données. Un DBA est toujours intégré à la base de données; ce processus n'est pas automatisé. 2 versions de scripts sont préparées - pour appliquer les modifications et pour annuler les modifications, et une instruction pour DBA est écrite. 2 versions de scripts sont toujours faites, et elles sont nécessairement testées pour la restauration et la restauration des modifications. Et ces mêmes scripts sont utilisés pour appliquer des modifications à la préparation et à la perf perf avant de lancer l'intégration et les tests de charge.
Que s'est-il passé après le déploiement?
Dans les 5 premières heures après le déploiement, 1 million d'événements sont survenus lorsque le logiciel client a reçu une erreur lors de la tentative de téléchargement du fichier. L'événement «fichier corrompu». Cela signifie que le client essaie de télécharger le fichier, mais le fichier est introuvable sur le référentiel. Habituellement, ces événements ne sont pas du tout ou sont mesurés entre 1 et 2 000 par jour.
Je dois dire tout de suite qu'il a fallu au moins 1 semaine pour trouver la cause de l'échec sur une équipe de 3 et parfois 5 personnes (dont moi).
Nous avons collecté la liste complète des fichiers pour lesquels l'événement «File Corrupted» est survenu.
Malgré le fait qu'il y avait plus d'un million d'événements et qu'ils provenaient tous d'utilisateurs différents, de sociétés différentes, il n'y avait que 250 fichiers uniques dans cette liste.
DBA sur le serveur de sauvegarde a été déclenché pour enquêter sur les sauvegardes de base de données au moment où les événements sont arrivés. Il y a pas mal de tableaux dans les bases du projet avec toutes sortes de journaux qui ont aidé à l'analyse. Même lors de la suppression d'informations de la base de données, un journal est ajouté à ce qui a été supprimé et par quel événement. Sur le prod, ces enregistrements sont stockés pendant 1 semaine, puis fusionnés sur le serveur d'archives.
Et les tableaux avec journaux ont également beaucoup aidé à analyser ce qui s'est passé:
Un journal complet avec les événements client est maintenu sur chaque fragment
Sur le serveur global:
- Journal des demandes de téléchargement de tous les fichiers par les utilisateurs
- Enregistrer les fichiers téléchargés sur le système par les utilisateurs
- Journal des événements FileCorrupt
- Fichier journal pour annuler la suppression du stockage
- Journal des fichiers supprimés de la base de données
De plus, un ELK avec les journaux d'application était disponible.
Nous avons réussi à répéter l'erreur sur l'environnement de développement, ce qui a confirmé l'exactitude de l'hypothèse. Au début, personne ne prenait cette hypothèse au sérieux, car il était très difficile de croire que tant de facteurs coïncidaient en même temps et que de nombreux utilisateurs venaient à ce moment précis.
Qu'est-ce qui a mal tourné?
Il s'est avéré que le système avait environ 250 (à titre de comparaison, des milliards de fichiers dans le système) de méga fichiers super duper populaires. 250 oui!
Ces fichiers étaient encore très anciens. Au moment où ces fichiers ont été téléchargés sur le système, un autre système pour générer la clé de fichier sur le stockage a été utilisé.
Il s'est avéré que pour ce type de clé, la méthode de suppression physique du stockage physique se comportait différemment des autres fichiers.
Dans la classe avec suppression, il y a un bloc de code avec une condition spécifique pour les fichiers avec l'ancienne clé. Le système, au moment de la suppression, avant de vérifier que le fichier ne se trouve pas sur des fragments, déplace cet ancien fichier vers un autre emplacement. Eh bien, cela n'a pas fonctionné.
Et il s'est avéré qu'au moment où le fichier a été déplacé (et je rappellerai qu'il est très populaire), si l'un des utilisateurs essaie de lui donner un nouveau droit d'utilisateur, le logiciel client va dans le stockage de ce fichier, mais le fichier n'est pas au bon endroit. Comme il est déplacé, cela ne fonctionne pas. Et le logiciel client envoie un message indiquant que le fichier est cassé. Dans la base de données, il est marqué comme cassé. Et toutes les informations sont supprimées de la base de données (enfin, presque toutes).
En attendant, notre routine de vérification des fragments découvre que le fichier est utilisé. Et envoie une réponse dont vous avez besoin pour le renvoyer. Mais toutes les informations ont déjà été supprimées de la base de données et il est impossible de les renvoyer.
Drôle hein?
Autrement dit, lorsque le fichier a été supprimé, l'utilisateur était dans cette période de temps lorsque le fichier a été déplacé, les fragments vérifiés et c'est à ce moment-là que l'utilisateur a envoyé une demande de téléchargement.
Le voici: une charge élevée en action, lorsque les matchs les plus incroyables vous correspondent.

Après avoir récupéré de la surprise et avoir tout restauré, nous nous sommes assurés que les fichiers des utilisateurs sont vivants, car ils ont été restaurés à partir des disques d'autres clients.
Naturellement, tout s'est bien passé dans les tests, car pendant le test, les nouveaux fichiers ont été supprimés avec un nouveau type de clé utilisé au cours des 5 dernières années. Ces fichiers ne sont pas transférés vers un autre emplacement de stockage au moment de la suppression.
Notre optimisme a diminué et nous avons décidé de ne pas emprunter la voie la plus optimiste.
Rétrospective
Nous avons décidé que nous devions ajouter des tests pour différents types de stockages
Ajouter une charge à perf lab qui utilise les appels lors de sa suppression du stockage
Fermer les conditions de course célèbres
Ajouter une surveillance (bien que ce soit dans les plans, mais ne cadrait pas avec la portée d'origine)
À propos de la surveillance
Ils ont décidé de faire une surveillance tout de suite, mais il s'est estompé en arrière-plan, car il était nécessaire de se déployer plus rapidement.
Pour la surveillance, le projet a utilisé Zabbix, ELK, Grafana, NewRelic, SQL Sentry et une version de test d'AppDynamics.
De cela, sur pef lab était NewRelic et SQL Sentry.
Nous avons déjà mesuré toutes les métriques du système et donc, nous voulions mesurer les métriques de l'entreprise. J'avais de l'expérience dans l'organisation d'une telle surveillance via Zabbix - nous avons décidé de faire de même.
Le schéma est très simple dans la base de données pour créer un tableau dans lequel collecter les métriques nécessaires par JOB et une procédure qui téléchargera les métriques collectées vers Zabbix.
Mesures:
- Le nombre de fichiers dans la file d'attente à supprimer sur une base globale
- Nombre de fichiers mis en file d'attente par le serveur
- Le nombre de fichiers envoyés au programme de désinstallation à partir du référentiel
- Nombre de fichiers supprimés
- Nombre d'événements FileCorrupt
- Le nombre de fichiers à supprimer sur chaque fragment
La surveillance a été implémentée et déployée sur le prod séparément, avant qu'ils ne commencent à déployer une nouvelle implémentation de suppression.
Nouvelle solution
En général, nous avons décidé qu'il valait mieux manger avec excès que de ne pas dormir, et avons fait un nouveau plan.
- vérifiez le même fragment que personne n'utilise le fichier à coup sûr et transférez uniquement les fichiers inutilisés vers le serveur;
- lors du transfert vers le serveur, collectez tous les fichiers de la table et vérifiez que les fichiers ne sont pas utilisés sur les fragments avant d'être placés dans la table de file d'attente de file d'attente;
- lorsque vous utilisez un fichier et que vous le recherchez dans le système, marquez la file d'attente de retrait dans le tableau comme un fichier nécessitant une vérification;
- renvoyer uniquement les fichiers pour lesquels aucune recherche n'a été effectuée;
- les fichiers qui ont été recherchés, vérifiez à nouveau les fragments;
- en général, supprimez la coche dans la procédure qui supprime le fichier, car elle devrait fonctionner rapidement - et le fichier utilisé ne devrait pas y parvenir en principe;
- prendre en compte dans la procédure que tout supprime par le fichier battu qu'il est en cours de suppression, et ne pas supprimer les informations le concernant.
Le point 6 lors du déploiement comprenait la suppression du chèque en plusieurs étapes. Tout d'abord, ils ont quitté le chèque, puis une semaine plus tard, le contrôle des dossiers des employés de l'entreprise a été désactivé, après 2 semaines, ils ont complètement désactivé le chèque.
De quoi aviez-vous besoin de changer dans le code?
Encore une fois, toutes les modifications s'appliquent uniquement à la base de données.
L'ampleur des changements était la plus importante sur le serveur mondial:
Ajoutez une table intermédiaire dans laquelle ajouter tous les fichiers téléchargés à partir de fragments.
Faites un JOB qui vérifie les fichiers sur la table intermédiaire qu'ils ne sont pas utilisés sur les fragments.
Dans la file d'attente des fichiers supprimés, ajoutez un champ avec la date du dernier accès au fichier et ajoutez un index.
Trouvez toutes les procédures avec accès au fichier - il s'est avéré 5 procédures. Ajoutez-leur un bloc modifiant la date de la dernière utilisation dans la table de file d'attente. La date change à chaque fois, qu'elle soit remplie ou non.
Ajoutez le programme de suppression aux procédures d'émission de fichiers afin qu'il n'affiche que les fichiers dont la date d'utilisation est vide.
JOB, ( 10 , , , 2 , ) . , , . , , , .
:
Dans la procédure qui supprime des fichiers du tableau avec des fichiers supprimés, vous avez dû ajouter une vérification que le fichier n'était pas utilisé. La procédure a perdu beaucoup de sa simplicité et de sa beauté - dans DELETE avec la sortie, ils ont simplement ajouté NOT EXISTS.Nous avons ajouté un JOB, qui en arrière-plan a frappé à partir des fichiers de table utilisés sur le serveur.Les tests
Des scénarios d'utilisation de toutes les options de stockage ont été ajoutés aux tests d'intégration.Ils ont également écrit de nouveaux cas pour tester de nouvelles fonctionnalités de suppression de fichiers.Perf Lab a ajouté une charge au serveur global. De plus, nous avons ajouté une charge correspondant à la suppression de fichiers du stockage.Déployer
. DBA , - . , .
- JOB , , .
.
, , .
S3 2 . , , .
- , , - .