Surviving the Night

El blog de Pablo Doval sobre .NET, SQL, WinDbg...

Looks that Kill: Componentes de DevExpress y ThreadAbortException

Aviso a Navegantes: El siguiente post no va a ser políticamente correcto. Es más, creo que este año los chicos de Developer Express Inc. no me van a enviar un jamoncito ni una botella de vino por Navidad, precisamente. Y sin embargo, me siento en la obligación de compartir esto con vosotros… así que ¡allá vamos!

Últimamente he pasado bastante tiempo en tierras Navarras: alegrándome la vista con el verdecito que ya voy echando tanto de menos, disfrutando como un enano de los increibles pintxos de Pamplona y, de cuando en cuando, tirando de WinDbg para resolver algunos problemas de rendimiento que tiene uno de nuestros clientes aquí :)

Y es que en estas semanas he visto muchos escenarios interesantes: las tradicionales fugas de memoria por manejadores de eventos en páginas ASP.NET, problemas con el tamaño de sesión y el histórico de ViewStates, un curioso caso de excepciones indeseadas en cada invocación a un Servicio Web con transacciones que espero poder postear en breve… vamos, ¡que no me he aburrido!

Uno de estos problemas, y uno de los más llamativos, tenía que ver con la increible cantidad de excepciones lanzadas en el frontal web de la aplicación; estamos hablando de varias decenas de miles a la hora! A estas alturas no hace falta que os diga que el mero hecho de lanzar una excepción (aunque esta sea capturada por nuestro código) supone un impacto negativo en el rendimiento, y debemos tratar de evitar estas situaciones.

Si bien el contador de rendimiento # of Excepts Thrown en .NET CLR Exceptions era suficiente para detectar el problema, necesitabamos algo más para averiguar el tipo de excepiones lanzadas y poder investigar mejor el problema real. Como no podía ser de otra manera, ese algo más fue WinDbg :)

Después de adjuntarme al proceso, le pedí a WinDbg que se detuviera cada vez que saltara una excepcion .NET (sxe clr) y que en cada una de ellas, me mostrara la información de la excepción (!pe), la pila de llamadas administrada (!clrstack) y que prosiguiera la ejecución sin manejar la excepción (gn):

sxe –c “!pe; !clrstack; gn” clr

La salida demostró que había dos tipos que conformaban la práctica totalidad de las excepciones:

  • MissingManifestResourceException:

Esta excepción no me preocupaba demasiado, porque en número era muy inferior a la que veremos a continuación. Sin embargo, es curioso el hecho de que siempre sucedía en unas DLLs de DevExpress.

A continuación os pongo una ocurrencia del problema (omitiendo la pila de llamadas, que en este caso no es relevante):

(d18.11b4): CLR exception - code e0434f4d (first chance)
Missing image name, possible paged-out or corrupt data.
Exception object: 0000000240991ed8
Exception type: System.Resources.MissingManifestResourceException
Message: Could not find any resources appropriate for the specified culture or the neutral culture.  Make sure "Resources.DevExpress_XtraScheduler_v9_3_Core.resources" was correctly embedded or linked into assembly "App_GlobalResources.2gule58b" at compile time, or that all the satellite assemblies required are loadable and fully signed.
InnerException: <none>
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 80131532

Después de revisar la solución, comprobamos que todas las referencias a todas las DLLs de DevExpress estaban aparentemente bien, por lo que decidimos involucrar al soporte de Developer Express en este caso.

  • ThreadAbortException:

Esta excepción ya suena peor ¿verdad? Hacía no mucho, en este mismo cliente, nos habíamos encontrado con el conocido bug de la plataforma en el método Response.End(), pero…. ¡no adelantemos acontecimientos!

Primero veamos un ejemplo de una de las excepciones que capturamos y su volcado de pila:

(d18.11b4): CLR exception - code e0434f4d (first chance)
Exception object: 0000000200f83940
Exception type: System.Threading.ThreadAbortException
Message: Thread was being aborted.
InnerException: <none>
StackTrace (generated):
SP               IP               Function
000000000911E8E0 0000000000000001 System.Threading.Thread.AbortInternal()
000000000911E8E0 000006427834300A System.Threading.Thread.Abort(System.Object)
000000000911E930 00000642BC9131D6 System.Web.HttpResponse.End()
000000000911E980 0000064281AC2EA6 DevExpress.Web.ASPxClasses.Internal.HttpUtils.EndRespons

StackTraceString: <none>
HResult: 80131530
OS Thread Id: 0x11b4 (23)
Child-SP         RetAddr          Call Site
000000000911e980 00000642803d1965 DevExpress.Web.ASPxClasses.Internal.HttpUtils.EndResponse()
000000000911e9c0 00000642803d049d DevExpress.Web.ASPxClasses.Internal.ResourceManager.ProcessRequest()
000000000911ea80 00000642bc8f2eb0 DevExpress.Web.ASPxClasses.ASPxHttpHandlerModule.BeginRequestHandler(System.Object, System.EventArgs)
000000000911eab0 00000642bc8e449b System.Web.HttpApplication+SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
000000000911eb10 00000642bc8f2215 System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)
000000000911ebb0 00000642bc8e3553 System.Web.HttpApplication+ApplicationStepManager.ResumeSteps(System.Exception)
000000000911ec60 00000642bc8e7874 System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext, System.AsyncCallback, System.Object)
000000000911ecc0 00000642bc8e745c System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest)
000000000911ed50 00000642bc8e608c System.Web.HttpRuntime.ProcessRequestNoDemand(System.Web.HttpWorkerRequest)
000000000911ed90 000006427f602012 System.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr, Int32)

Como se puede ver, uno de los métodos de las librerias de DevExpress, DevExpress.Web.ASPxClasses.Internal.HttpUtils.EndResponse está invocando a System.Web.HttpResponse.End(), el cual a su vez lanza una excepción de tipo ThreadAbortException.

Mmm… antes hablabamos de un caso conocido de excepción ThreadAbortException en las llamadas a HttpResponse.End(), así que una rápida busqueda nos lleva al KB312629, que si no conoceis os recomiendo que leais y comprobéis si vuestras aplicaciones se ven afectadas por él.

Una comprobación del código fuente del método de DevExpress (con la siempre inestimable ayuda de .NET Reflector), nos revela que los desarrolladores del producto son conscientes del problema, implementando un catch para el ThreadAbortException:

public static void EndResponse()
{
    HttpContext.Current.ApplicationInstance.CompleteRequest();
    try
    {
        GetResponse().End();
    }
    catch (ThreadAbortException)
    {
    }
}

Sin embargo, esta solución no evita el lanzamiento descontrolado de las excepciones que, como ya se ha comentado, a pesar de ser controladas, siguen teniendo un impacto sobre el rendimiento de la aplicación, ¡por no hablar del ruido que meten a la hora del monitorizar el sistema!

Con esta información en la mano, elaboré un documento detallado (mucho más que este post) para que mi cliente pudiera abrir un caso de soporte con la gente de DevExpress, esperando que resolvieran el problema que nos estaba impactando severamente en producción, mediante la implementación condicional de una llamada a HttpContext.Current.ApplicationInstance.CompleteRequest() en lugar de invocar al método HttpResponse.End().

La respuesta de la gente de DevExpress no se hizo esperar mucho (punto para ellos!), y se resume en lo siguiente:

  1. No van a cambiar el código, ni a dejar la opcion al desarrollador de asumir el cambio con el CompleteRequest.
  2. Del problema de las MissingManifestResourceException simplemente nos comentaron que esas excepciones son esperadas y que no nos preocupemos.
  3. Con las dos respuestas (a todas luces insuficientes y debatibles) llegó la notificación del cierre del caso. Sin derecho a réplica.

En esta ocasion, lo que más rabia me da no es la parte técnica; puedo entender los problemas que tengan para realizar el cambio (aunque no es de recibo que su código levante tantas excepciones), hasta casi puedo entender que su código levanta excepciones buscando recursos localizados inexistentes… pero por lo que no paso es por la dejadez y el mal trato al usuario en el soporte técnico.

Asi que… ¡Avisados estais! :) Vamos a por el mini-resumen del día…

Resumiendo:

  1. ¿Habéis monitorizado su vuestra aplicacion tira excepciones? Si no es así, revisad los contadores de rendimiento y tirad de WinDbg… ¡a lo mejor os llevais una sorpresa!
  2. Comprobad si teneis ThreadAbortException, y si es así, evaluad el workaround descrito en el KB.
  3. ¿Estais planteandoos adquirir una licencia para controles de usuario para un nuevo proyecto? ¡Aseguraos de tener un buen soporte, no sabeis lo útil que puede llegar a ser!

Keep Rockin’!

Rock Tip:

No podía ser de otra manera, este post solo lo podían presentar los chicos de Mötley Crüe con su “Looks that Kill”. Apropiado, porque en este caso la vistosa interfaz de usuario de la aplicación es la que contiene un pequeño veneno, como hemos visto ;)

Posted: 1/6/2010 1:12 por Pablo Alvarez | con 7 comment(s) |
Archivado en:
Comparte este post:

Comentarios

Juan Irigoyen ha opinado:

Muy bueno el post, que envidia tengo de no saber lo que sucede, la mayoría de las veces nos tenemos que conformar con reiniciar la aplicación y rezar un par de oraciones...

Trabajamos con controles de devexpress desde hace tiempo y aunque en el fondo resuelven todas nuestras necesidades hemos tenido que luchar innumerables veces con el soporte técnico, me pregunto incluso si realizan testeo unitario, pues algunas actualizaciones nos han ocasionado multitud de problemas.

Un saludo.

# June 1, 2010 10:54 AM

Miguel ha opinado:

Pablo, ya se que no te sirve de consuelo pero nosotros hemos tenido muchisimos problemas con controles de otra compañia (Telerik para ser exactos) y la respuesta es siempre la misma para la próxima release estará arreglado. En fin resignación...

Un saludo

# June 1, 2010 4:42 PM

Pablo Alvarez ha opinado:

Gracias por vuestros comentarios a los dos!

@Juan: Yo me intento decir a mi mismo que si alguna vez tengo que reiniciar la aplicacion y rezar un par de oraciones, dejo la informática y me vuelvo al mundo de la farándula! ;) Pero te entiendo, a veces no nos queda otra.

Aún así, precisamente por eso me gusta tanto WinDbg, Reflector y todo lo que me permita ver un poco las tripillas a los productos... generalmente no hace falta meterse demasiado para, al menos, tener un poco de idea de que es lo que esta haciendo reventar a nuestra pobre aplicacion ;) Y por eso es por lo que intento animar en mi blog y mis charlitas a que usemos estas herramientas para ir 'un pasito mas alla' :)

@Miguel: Gracias por compartir tu experiencia con Telerik.. si esta claro que nadie se libra ;) De todos modos, ¡no me sirve de consuelo! Yo deseo que a vosotros no os dé jamás ningun problema Telerik, ni a mi cliente DevExpress :)

Por cierto, la respuesta de Telerik y DevExpress es muy diferente. Ya quisiera yo oir un "En la proxima release..". Lo que nos dijeron a nosotros es "No hay ningun plan para modificar el código ni implementar el workaround que nos sugerís."... argh!! :)

un abrazo a los dos!

# June 1, 2010 4:57 PM

El Bruno ha opinado:

Eso por no usar Infragistics .. que van muy bien !!! ^^ jeje

gran post Pablo !! :D

SaLu2

# June 1, 2010 8:36 PM

Pablo Alvarez ha opinado:

Nah.. eso nos pasa por no ser 'desarrolladores de verdad' y andar comprando todo por ahi... si nos hicieramos nuestros propios controles no pasaría esto! XDD

Gracias por el comentario!!! un abrazote!!!

# June 1, 2010 9:40 PM

Juan Irigoyen ha opinado:

Quiero apuntaros tambien, que teneis una forma de solucionarlo, en la versión de Devexpress bienen tambien los fuentes de los controles, con lo que podrias recompilar la parte del código que necesitais cambiar y resolver el problema, al menos temporalmente.

Por cierto, yo ya estoy en el mundo de la farándula desde hace algunos años :), bastante loco me vuelven los de Microsoft, como para aprender lo del WinDbg, me dan mareos solo de pensarlo, me recuerda al desensamblador de las antiguas PC-TOOLS, además, la felicididad es directamente proporcional a la ignorancia, pero lo tengo claro, si alguna vez detecto un problema de estos te llamare de inmediato, con los años he aprendido a delegar y no veas lo bien que se me da.

Un saludo.

# June 2, 2010 12:43 AM

¿Sabes si este problema no lo han corregido aun? ha opinado:

Que tal Pablo,

Muy buen post, excelentemente documentado.

He estado revisado el sitio de devexpress y he estado muy emocionado con todo lo que hacen. Pero comencé a buscar si había problemas conocidos. He visto tu post y parece que le pasa a otras personas. Lo cual me preocupa y decepciona bastante pues pensaba que era la mejor herramienta para ahorrar tiempo al hacer aplicaciones robustas.

¿Sabes si este problema no lo han corregido aun?

Saludos cordiales

# July 23, 2010 7:06 PM