\0_ ASP.NET : les contrôles en session, c'est non.
mardi 03 juin 2008
Une mésaventure m'est récemment arrivée à cause d'un contrôle mis en session. Au moment de coder l'ajout du contrôle en session, j'avais bien senti une perturbation dans la Force, mais je n'arrivais à mettre le doigt sur ce qui me dérangeait... en même temps il était 4h30 du matin et c'était teeeeeeeellement pratique de procéder comme ça. J'ai donc vite oublié ce malaise, jusqu'à ce que le site se mette à crasher pour un oui pour un non. Le problème, c'est que c'était loin d'être le seul changement apporté au site, et on ne savait donc pas d'où venait le problème. On a donc dû faire appel aux shamans de chez Microsoft pour qu'ils déchiffrent les inscriptions runiques contenus dans le dump mémoire du serveur, ce qui a fini par nous amener à la conclusion suivante : il y a un chacal qui met n'importe quoi en session.Zut, c'est moi :-/
Syndrome : votre site ASP.NET crashe un peu n'importe quand en levant des OutOfMemoryException
Cause possible : vous avez mis des contrôles en session et/ou en cache
Explication : imaginons qu'à chaque chargement de vos pages, vous mettiez en session une référence à un contrôle afin de pouvoir changer son contenu tranquillou par la suite (par exemple parce que vous voulez le faire en même temps que d'autres trucs dans un appel Ajax, mais que vous n'avez pas accès à toute l'arborescence des contrôles, et donc que vous pouvez toujours vous brosser pour faire un FindControl (parce que vous faites de beaux appels Ajax, et pas un truc de bourrin qui produit un postback complet de votre page)).
Tout seul sur votre serveur de développement, vous ne constaterez probablement pas de problème. En revanche, à plusieurs, ça devient problématique. En effet, lorsque vous mettez votre contrôle dans l'objet Session ou Cache, vous l'empêchez purement et simplement d'être désalloué par le garbage collector. "Aucun problème", vous dites-vous, "le contrôle sera détruit en même temps que la page qui le contient, c'est-à-dire dès la fin du rendu de la page". "Pas du tout" vous répondrais-je, "puisque l'objet Session/Cache contient toujours une référence vers votre contrôle, il sera considéré comme utilisé et ne sera donc pas collecté". Si ce n'était que pour un contrôle, ça ne serait pas grave, d'autant plus que, la référence étant écrasée à chaque chargement de page, il finira bien par être collecté. Seulement voilà, comme le contrôle est toujours "vivant", tous les objets qu'il référence le sont également, et leur mémoire ne peut donc pas être récupérée. Or, dans les contrôles ASP.NET, on trouve entre autres les propriétés Parent, qui donne une référence vers le contrôle parent, et Page, qui donne une référence vers l'objet Page contenant le contrôle. Autrement dit, lorsque vous mettez un contrôle en Session/Cache, vous empêchez le garbage collector de récupérer la mémoire pour toute la page contenant ce contrôle. Et quand je dis "toute la page", ça inclut tout ce que la page elle-même référence : contrôle, HttpContext, HttpRequest, HttpResponse, et caetera.
Bref, ça fait des sessions utilisateurs très volumineuses, et donc à plusieurs utilisateurs sur le même serveur, on atteint assez vite le point où il n'y a plus de mémoire, et patatras.
Je le répète une dernière fois : De contrôle en session/cache tu ne mettras point, parce que ça craint.
Si vous êtes plus sensibles aux haïkus :
Contrôles en session
Toute la page reste en mémoire
Mon serveur se meurt
Coïncidence amusante, quelques jours après l'incident, Tess Hernandez postait un article sur le sujet ^_^



