Pourquoi une exception entraînerait-elle des fuites de ressources dans Node.js?

Si vous regardez le début de la documentation Node.js pour les domaines, il indique:

Par la nature même de la façon dont le lancer fonctionne en JavaScript, il n'y a presque jamais moyen de "ramasser en toute sécurité où vous l'avez laissé", sans renverser de références ou créer un autre type d'état fragile indéfini.

Encore une fois dans l'exemple de code qu'il donne dans cette première section, il dit:

Bien que nous ayons empêché le redémarrage abrupte des processus, nous fuyons des ressources comme des fous

J'aimerais comprendre pourquoi c'est le cas? Quelles sont les ressources qui fuient? Ils recommandent que vous utilisiez uniquement des domaines pour détecter des erreurs et arrêter en toute sécurité un processus. Est-ce un problème avec toutes les exceptions, pas seulement lorsque vous travaillez avec des domaines? Est-ce une mauvaise pratique de jeter et d'attraper des exceptions en Javascript? Je sais que c'est un modèle commun dans Python.

MODIFIER

Je peux comprendre pourquoi il pourrait y avoir des fuites de ressources dans une langue non collectée si vous lancez une exception parce que tout code que vous pourriez exécuter pour nettoyer des objets ne fonctionnerait pas si une exception est lancée.

La seule raison pour laquelle je peux imaginer avec Javascript est si lancer une exception stocke les références aux variables dans le champ d'application de l'exception (et peut-être les choses dans la pile d'appels), en gardant ainsi les références, puis l'objet d'exception est conservé Se nettoie. À moins que les ressources fuyantes mentionnées ne soient des ressources internes au moteur.

METTRE À JOUR

J'ai écrit un blog expliquant la réponse à cela un peu mieux maintenant. Vérifiez-le

Des exceptions inattendues sont celles dont vous devez vous inquiéter. Si vous ne connaissez pas suffisamment l'état de l'application pour ajouter une gestion pour une exception particulière et gérer tout nettoyage d'état nécessaire, par définition, l'état de votre application est indéfini et inconnaissable, et il est tout à fait possible qu'il y ait des choses Il ne faudrait pas que ça se fasse autour de ça. Ce ne sont pas seulement des fuites de mémoire dont vous devez vous soucier. L'état de l'application inconnue peut provoquer un comportement d'application imprévisible et indésirable (comme la livraison de la sortie juste erronée – un modèle partiellement rendu, ou un résultat de calcul incomplet, ou pire, une condition où chaque sortie ultérieure est incorrecte). C'est pourquoi il est important de quitter le processus lorsqu'une exception non gérée se produit. Il donne à votre application la possibilité de se réparer.

Des exceptions se produisent, et c'est bien. Embrasse le. Arrêtez le processus et utilisez quelque chose comme Forever pour le détecter et régler les choses sur la bonne voie. Les clusters et les domaines sont géniaux aussi. Le texte que vous lisez n'est pas une mise en garde contre le lancement d'exceptions ou la poursuite de la procédure lorsque vous avez géré une exception que vous prévoyez – c'est une mise en garde contre le maintien de la procédure en cas d'exception inattendue.

Je pense que lorsqu'ils ont déclaré que " nous gouttons des ressources ", ils voulaient vraiment dire " nous pourrions avoir des ressources qui fuient ". Si http.createServer gère les exceptions de façon appropriée, les threads et les socket ne doivent pas être divulgués. Cependant, ils pourraient certainement être si cela ne gère pas correctement les choses. Dans le cas général, vous ne savez jamais vraiment si quelque chose gère correctement les erreurs.

Je pense qu'ils ont tort / très trompeuse quand ils ont dit: " Par la nature de la façon dont le lancer fonctionne en JavaScript, il n'y a presque jamais moyen de sécurité … ". Il ne devrait pas y avoir de quoi fonctionne en Javascript (par rapport à d'autres langues) et le rend dangereux. Il n'y a pas non plus de savoir comment le lancer / capture fonctionne en général, ce qui le rend dangereux – sauf si vous les utilisez mal.

Ce qu'ils auraient dû dire, c'est que des cas exceptionnels (indépendamment de l'utilisation ou non des exceptions) doivent être traités de manière appropriée. Il existe quelques catégories différentes à reconnaître:

Un état

  1. Les exceptions qui se produisent lorsque l'état externe (écriture de base de données, sortie de fichier, etc.) est dans un état transitoire
  2. Les exceptions qui se produisent alors que la mémoire partagée est dans un état transitoire
  3. Exceptions où seules les variables locales peuvent être dans un état transitoire

B. Réversibilité

  1. Etat réversible / révélable (par exemple, renversement de base de données)
  2. État irréversible (données perdues, inconnu de renversement ou prohibitif de renverser)

C. Critibilité des données

  1. Les données peuvent être mises au rebut
  2. Les données doivent être utilisées (même si elles sont corrompues)

Quel que soit le type d'état avec lequel vous vous embarquez, si vous pouvez l'inverser, vous devriez le faire et vous êtes configuré. Le problème est un état irréversible. Si vous pouvez détruire les données corrompues (ou la mettre en quarantaine pour une inspection distincte), c'est le meilleur moyen pour un état irréversible. Cela se fait automatiquement pour les variables locales lorsqu'une exception est lancée, c'est pourquoi les exceptions excèdent le traitement des erreurs dans le code purement fonctionnel (c.-à-d. Les fonctions sans effets secondaires possibles). De même, tout état partagé ou externe doit être supprimé si cela est acceptable. Dans le cas de l'état partagé, soit lancer des exceptions jusqu'à ce que l'état partagé devienne l'état local et est nettoyé par déroulement de la pile (soit statiquement ou via le GC), soit redémarrez le programme (j'ai lu des personnes suggérant l'utilisation de quelque chose Comme nodejitsu pour toujours). Pour l'état externe, cela est probablement plus compliqué.

Le dernier cas est lorsque les données sont essentielles. Eh bien, vous devrez vivre avec les bugs que vous avez créés. Tout le monde doit faire face aux bugs, mais c'est le pire lorsque vos bogues impliquent des données corrompues. Cela nécessitera habituellement une intervention manuelle (reconstitution des données perdues / endommagées, élagage sélectif, etc.) – la gestion des exceptions ne vous permettra pas d'aller dans le dernier cas.

J'ai écrit une réponse similaire à la façon de gérer l'échec de la mi-opération dans divers cas dans le contexte de plusieurs mises à jour de stockage de données: https://stackoverflow.com/a/28355495/122422

Prenant l'échantillon de la documentation node.js:

 var d = require('domain').create(); d.on('error', function(er) { // The error won't crash the process, but what it does is worse! // Though we've prevented abrupt process restarting, we are leaking // resources like crazy if this ever happens. // This is no better than process.on('uncaughtException')! console.log('error, but oh well', er.message); }); d.run(function() { require('http').createServer(function(req, res) { handleRequest(req, res); }).listen(PORT); }); 

Dans ce cas, vous garez les connexions lorsqu'une exception se produit dans handleRequest avant de fermer le socket.

"Fuite" dans le sens où vous avez terminé le traitement de la demande sans nettoyer par la suite. Finalement, la connexion émettra le temps et fermera la prise, mais si votre serveur est sous charge élevée, il se peut qu'il ne manque plus de prises avant que cela ne se produise.

En fonction de ce que vous faites dans handleRequest vous risquez également de handleRequest des poignées de fichiers, des connexions à la base de données, des auditeurs d'événements, etc.

Idéalement, vous devriez gérer vos exceptions afin que vous puissiez nettoyer après eux.