2012-12-10 3 views
3

Nous avons récemment développé une nouvelle application Web ASP.NET MVC 4 (C#/Visual Studio). Après les tests et le débogage locaux, nous l'avons déployé en production, puis nous avons commencé à recevoir de plus en plus de messages de suivi de l'état de santé. Ceux-ci avaient différents messages d'exception:Stack Problème vide sur l'utilisation simultanée de l'application MVC

  • Pile vide.
  • La collection a été modifiée; l'opération d'énumération peut ne pas s'exécuter.
  • L'article a déjà été ajouté. Clé dans le dictionnaire: 'ALL_HTTP' Clé ajoutée: 'ALL_HTTP' (autres clés également mentionnées).
  • La valeur ne correspond pas à la plage attendue.

E.g. toute une série de types d'erreurs que nous ne pouvions pas simplement résoudre ou reproduire. Le 'Stack Empty' est celui qui se produit le plus, plusieurs centaines par jour (par exemple pour 1-10% des utilisateurs), donc nous nous concentrons sur celui-ci, car les autres erreurs semblent liées. Voici une trace de la pile partielle:

Exception information: 
Exception type: System.InvalidOperationException 
Exception message: Stack empty. 
... 
Stack trace: at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource) 
at System.Collections.Generic.Stack`1.Pop() 
at System.Web.WebPages.TemplateStack.Pop(HttpContextBase httpContext) 

Comme le montre la trace de pile sont le plus souvent situé complètement dans le framwork MVC (System.Web). Le seul endroit dans notre propre code qui se produisait régulièrement dans certaines traces de pile était dans les vues (fichiers .cshtml) de l'URL demandée puis dans un appel @ Html.RenderAction(). À ce jour, nous en avons refacturé beaucoup à des appels RenderPartial(). Cela a conduit à l'absence de plus de vues dans la trace de la pile, bien que certains RenderPartial maintenant donné également certains

La recherche de cette erreur indiquée concomitance/exécution parallèle est la cause. Cela correspond au fait que nous n'avons initialement pas pu reproduire l'erreur localement, mais cela s'est produit en production. Nous n'avons effectué aucun test de charge, mais nous avons maintenant réussi à reproduire l'erreur sur un système de développement local en démarrant un grand nombre d'applications/demandes simultanément. Cependant, dans notre code, rien n'est fait avec des instructions parallèles explicites.

Cela semble être lié au fait que la vue MVC n'est PAS sûre pour les threads. Cependant, il est difficile d'imaginer que personne d'autre n'aurait rencontré cela. Nous avons quelques milliers de visiteurs par jour, environ 30 utilisateurs actifs à tout moment. Malheureusement, ce nombre est en baisse en raison de la baisse du classement de Google (liée à ce problème).

Est-ce que quelqu'un connaît une solution/approche de ce problème?

+1

Est-ce que vous transmettez en tant que modèle un objet potentiellement accessible par plusieurs threads? Comme un objet partagé? – vtortola

+0

vtorola Pas vraiment! Wel tous les modèles s'étendent à partir de BaseModel qui a un objet de contexte db statique, Cependant, cela est stocké dans le HTTPContext (en utilisant une clé). Ceci est fait de sorte qu'il est utilisé sur une base «par demande» (il est paresseux chargé sur le premier accès). Les traces de pile ne pointent PAS sur ce code, elles sont seulement dans la logique de vue. De plus, j'ai déjà ajouté une instruction 'lock' sur le code qui initialise le DB Context, juste au cas où. Cela n'a pas semblé changer quoi que ce soit. Nous utilisons Entity Framework. – Bart

+0

Woa qui semble effrayant: D Vous devez utiliser votre DbContext dans le cadre de votre contrôleur, générer les objets POCO souhaités à partir de la base de données, puis créer vos modèles de vue. Une vue devrait être thread safe, je n'ai jamais vu une erreur comme ça, donc je suggère de commencer à regarder ce DbContext partagée effrayant profondément. Peut-être que la trace de la pile ne pointe pas vers cet objet BaseModel, mais qu'une partie est certainement partagée entre plusieurs threads. – vtortola

Répondre

3

Je développe une application ASP.NET MVC 4 et j'ai également rencontré les erreurs que vous mentionnez. Bien qu'ils soient différents, ils semblent avoir la même source. Après avoir passé plusieurs heures à essayer de trouver la raison (et beaucoup de changements de code), j'ai commencé mon analyse à partir de zéro.

Comme j'utilise un routage personnalisé et qu'il existe un gestionnaire pour cet itinéraire qui vérifie plusieurs choses et accède également à la base de données que j'ai démarrée en commentant l'accès à la base de données. Ouverture de plusieurs onglets de navigateur très rapidement (avec IISExpress> Afficher toutes les applications ou Ctrl + Clic dans un lien) J'étais heureux de voir que toutes les pages étaient affichées correctement, au lieu de plusieurs messages d'erreur aléatoires. J'ai essayé cela quelques fois pour être sûr et a conclu que quelque chose n'allait pas lors de l'accès à la base de données.

public class MyNewRouteHandler : IRouteHandler { 

    IHttpHandler MvcHandler; 

    public IHttpHandler GetHttpHandler(RequestContext requestContext) { 
     MvcHandler = new MvcHandler(requestContext); 

     // some checkings and 
     // some database access code 
     // that was commented 

     return MvcHandler; 
    } 
} 

Un collègue a suggéré que j'ai ajouté un petit sommeil de fil à l'intérieur de cette méthode: GetHttpHandler. Cette ligne a fait apparaître à nouveau les erreurs, suggérant que le problème n'était pas lié à DB ... Quand j'ai fait cela, j'ai vu que l'objet MvcHandler était défini comme une propriété de classe et cela pourrait être une source de ce qui semblait être un problème de simultanéité (seulement quand plusieurs accès presque consécutifs ont été exécutés, le problème a été montré).Déplacement de l'objet MvcHandler vers un objet local à l'intérieur de la méthode.

public class MyNewRouteHandler : IRouteHandler { 

    public IHttpHandler GetHttpHandler(RequestContext requestContext) { 
     IHttpHandler MvcHandler = new MvcHandler(requestContext); 

     // some checkings and 
     // some database access code 
     // that was commented 

     return MvcHandler; 
    } 
} 

Et après le test, plus d'erreurs. Décommenter tout mon code qui a accédé à la base de données (et fait d'autres vérifications) et toujours plus d'erreurs trouvées. Presque 3 jours se sont écoulés et tout fonctionne encore correctement.

+0

Super nrod! En effet, je le regarde, mais semble également utiliser un MVC HttpHandler personnalisé. Et ces gars ne semblent pas être sûrs pour les threads ... Difficile à trouver, car les traces de la pile ne pointaient pas du tout vers le routage. – Bart

0

Cette façon de faire un Custom Route Handler a résolu mon le plus de mes erreurs mais il m'en reste encore quelques unes et avec de nouveaux messages. L'un d'entre eux montrait une ligne de code dans mon Custom Route Handler et tous avaient en commun le fait qu'un dictionnaire était géré par le framework MVC, alors ... ai-je encore un problème de concurrence?

Je l'ai supposé ainsi et toutes mes propriétés de méthode ont été déplacées à l'intérieur de la méthode public IHttpHandler GetHttpHandler(RequestContext requestContext), pas seulement celle mentionnée précédemment. L'un d'entre eux était la collection RouteData ... Enfin et après 2 jours, il semble qu'il n'y ait plus d'erreurs.

Questions connexes