Soudain, un système de collecte des ordures à lui seul ne suffit pas

Voici une courte histoire sur les mystérieuses pannes de serveur que j'ai dû déboguer il y a un an (article daté du 05 décembre 2018, environ). Les serveurs ont bien fonctionné pendant un certain temps, puis à un moment donné, ils ont commencé à planter. Après cela, les tentatives d'exécution de presque tous les programmes qui se trouvaient sur les serveurs ont échoué avec les erreurs «Il n'y a pas d'espace sur le périphérique», bien que le système de fichiers n'ait signalé que quelques gigaoctets occupés sur ~ 20 Go de disques.

Il s'est avéré que le problème était dû au système de journalisation. Il s'agissait d'une application Ruby qui prend les fichiers journaux, envoie des données à un serveur distant et supprime les anciens fichiers. L'erreur était que les fichiers journaux ouverts n'étaient pas explicitement fermés. Au lieu de cela, l'application a permis au garbage collector automatique de Ruby de nettoyer les objets File. Le problème est que les objets Fichier n'utilisent pas beaucoup de mémoire, donc le système de journalisation pourrait théoriquement garder des millions de journaux ouverts avant que la collecte des ordures ne soit requise.

* Les systèmes de fichiers Nix séparent les noms de fichiers et les données dans les fichiers. Les données sur un disque peuvent avoir plusieurs noms de fichiers pointant vers elles (c'est-à-dire des liens durs), et les données sont supprimées uniquement lorsque le dernier lien est supprimé. Un descripteur de fichier ouvert est considéré comme un lien, donc si le fichier est supprimé pendant la lecture du programme, le nom du fichier disparaît du répertoire, mais les données du fichier restent actives jusqu'à ce que le programme le ferme. C'est ce qui est arrivé à l'enregistreur. La commande du («utilisation du disque») recherche les fichiers à l'aide d'une liste de répertoires, elle n'a donc pas vu les gigaoctets de données de fichier pour les milliers de fichiers journaux qui étaient encore ouverts. Ces fichiers n'ont été découverts qu'après avoir exécuté lsof ("list open files").

Bien sûr, une erreur similaire se produit dans d'autres cas similaires. Il y a quelques mois, j'ai dû rencontrer une application Java qui est tombée en panne après quelques jours en raison d'une fuite dans les connexions réseau.

J'écrivais la plupart de mon code en C, puis en C ++. À cette époque, je pensais que la gestion manuelle des ressources était suffisante. C'était compliqué? Chaque malloc () a besoin de la fonction free (), et chaque open () a besoin de close (). Simplement. Sauf que tous les programmes ne sont pas simples, la gestion manuelle des ressources au fil du temps est devenue une camisole de force. Puis un jour, j'ai découvert le comptage de liens et la collecte des ordures. J'ai pensé que cela résout tous mes problèmes et j'ai complètement cessé de me soucier de la gestion des ressources. Encore une fois, pour les programmes simples, c'était normal, mais tous les programmes ne sont pas simples.

Vous ne pouvez pas compter sur la récupération de place, car elle ne résout que le problème de la gestion de la mémoire, et les programmes complexes doivent traiter bien plus que de la mémoire. Il y a un mème populaire qui répond à cela avec le fait que la mémoire est à 95% des problèmes de ressources . Vous pourriez même dire que toutes les ressources représentent 0% de vos problèmes - jusqu'à ce que vous en manquiez. Ensuite, cette ressource devient 100% de vos problèmes.

Mais une telle pensée perçoit toujours les ressources comme un cas particulier. Un problème plus profond est que, à mesure que les programmes deviennent plus complexes, tout tend à devenir une ressource. Par exemple, prenez un programme de calendrier. Un programme de calendrier sophistiqué permet à plusieurs utilisateurs de gérer plusieurs calendriers partagés et avec des événements qui peuvent être partagés sur plusieurs calendriers. Toute partie des données affectera finalement plusieurs parties du programme et devrait être pertinente et correcte. Par conséquent, pour toutes les données dynamiques, vous avez besoin d'un propriétaire, et pas seulement pour la gestion de la mémoire. À mesure que de nouvelles fonctionnalités sont ajoutées, de plus en plus de parties du programme devront être mises à jour. Si vous êtes sain d'esprit, vous ne pourrez autoriser la mise à jour des données que d'une partie du programme à la fois,de sorte que le droit et la responsabilité de mettre à jour les données deviennent en soi une ressource limitée. La modélisation de données mutées à l'aide de structures immuables n'entraîne pas la disparition de ces problèmes, mais les traduit seulement dans un autre paradigme.

La planification de la propriété et de la durée de vie des ressources est une partie inévitable de la conception de logiciels complexes. C'est plus facile si vous utilisez des modèles courants. L'un des modèles est les ressources interchangeables. Un exemple est la chaîne immuable "foo", qui est sémantiquement la même que tout autre "foo" immuable. Ce type de ressource n'a pas besoin d'une durée de vie ou d'une possession prédéterminée. En fait, afin de rendre le système aussi simple que possible, il est préférable de ne pas avoir une durée de vie ou une propriété prédéterminée (hi Rust, environ par personne). Un autre modèle est celui des ressources qui ne sont pas interchangeables, mais qui ont une durée de vie déterminée. Cela inclut les connexions réseau, ainsi que des concepts plus abstraits, tels que le droit de contrôler une partie des données.La chose la plus raisonnable est de garantir explicitement la durée de vie de ces choses lors de l'encodage.

Notez que le garbage collection automatique est vraiment bon pour implémenter le premier modèle, mais pas le second, tandis que les techniques de gestion manuelle des ressources (telles que RAII) sont excellentes pour implémenter le deuxième modèle, mais terribles pour le premier. Ces deux approches deviennent complémentaires dans des programmes complexes.

Source: https://habr.com/ru/post/undefined/


All Articles