Entre las excepciones y la flojera de los desarrolladores

Una de las recomendaciones importantes en el desarrollo de código es la “no utilización de excepciones para evitar realizar validaciones.”


¿A qué me refiero?


A usar try/catch para no tener que escribir código que valide algo. Total, si se cae, en el catch retorno que es falso, si no se cayó, entonces retorno verdadero. Así no es necesario codificar rutinas especiales.


Vamos al caso


Como ya es costumbre en mi trabajo, y de las buenas costumbres, las pruebas de carga muestran la peor parte de las aplicaciones, y esta no fue la excepción, aunque el culpable era código de un componente de terceros.


Pruebas de carga


Treinta minutos de ejecución de un script grabado con [ACT], 50 usuarios concurrentes, 30 minutos. No vamos a dar todos los detalles, pero el uso de la CPU estaba “fluctuante”, y tenía unas subidas a 100% por varios segundos.


El siguiente es el grafico de performance monitor. Como hay más de 30 minutos, los segundos que la aplicación pasaba al 100% solo se ven como picos, pero en algunos casos llegaba a casi 40 segundos.



Te podrá haber llamado la atención que el uso de la CPU está muy extraño, como que no es fluido, considerando que una prueba de carga debiera mantenerla ocupada constantemente. No tengo la explicación ahora, pero al final veremos algo diferente.


Mientras revisaba contadores varios, me llamó la atención la cantidad de excepciones que se producían a veces, por lo tanto, agregué el contador de excepciones y voila!!. Problema encontrado.



Asombrosa similitud, ¿no?


A tomar dumps en las excepciones para ver que está ocurriendo. Mmm, mejor que no. Con 184 excepciones por segundo, reventamos el servidor. Mejor aún, utilizo un archivo de configuración para AdPlus, que viene en [windbg], el cual configuro para obtener información de threads, threadpool, stacks manejados y no manejados y excepciones cuando yo le solicite, que será cuando la CPU esté en 100%.


¿Resultado?


Esta es parte del stack no manejado, de un thread manejado. Como este, habían 10 más, es decir, 11 threads lanzando excepciones, a una asombrosa tasa de 184 por segundo.






 22 Id: 3924.46b8 Suspend: 1 Teb: 7ffa5000 Unfrozen
ChildEBP RetAddr Args to Child
06ece448 7c827ad6 7c8063cb ffffffff 7921ace1 ntdll!KiFastSystemCallRet
06ece44c 7c8063cb ffffffff 7921ace1 00000000 ntdll!NtQueryVirtualMemory+0xc
06ece4ac 7c8123b4 7921ace1 00000000 793e8730 ntdll!RtlIsValidHandler+0x82
06ece520 7c834d44 06ecc000 06ece530 00010007 ntdll!RtlDispatchException+0x78
06ece800 77e52db4 06ece810 050d5008 e0434f4d ntdll!RtlRaiseException+0x3d
06ece860 7921af79 e0434f4d 00000001 00000000 kernel32!RaiseException+0x53
06ece8b8 7921aefc 1194c7f0 00000000 06eceb14 mscorwks!RaiseTheException+0xa0
06ece8e0 7921aeb0 1194c7f0 00000000 06eceb24 mscorwks!RealCOMPlusThrow+0x48
06ece8f0 79218cc2 1194c7f0 00000000 0219d0b4 mscorwks!RealCOMPlusThrow+0xd
06eceb24 792b1893 00000018 00000001 00000000 mscorwks!CreateMethodExceptionObject+0x67b
06eceb58 79274773 00000018 79274778 06ecec08 mscorwks!RealCOMPlusThrow+0x35
06eceb68 79338b01 0216137a 000000e7 06eceb90 mscorwks!StringToNumber+0x7e
06ecec08 04266804 06ecec14 0f1604f0 000000e7 mscorwks!COMNumber::ParseDouble+0x32


Esta es parte del stack manejado, del mismo thread.





0x06ece908 0x7c834cf4 [FRAME: GCFrame]
0x06ecec38 0x7c834cf4 [FRAME: ECallMethodFrame] [DEFAULT] R8
System.Number.ParseDouble(String,ValueClass System.Globalization.NumberStyles,Class System.Globalization.NumberFormatInfo)
0x06ecec48 0x79a14e0f [DEFAULT] R8
System.Double.Parse(String,ValueClass System.Globalization.NumberStyles,Class System.IFormatProvider)
0x06ecec84 0x79a0e3cc [DEFAULT] R8
System.Convert.ToDouble(String)
0x06ecec88 0x06a30741 [DEFAULT] [hasThis] ValueClass System.Drawing.StringAlignment
C1.Util.Styles.StyleContext.1M(String)
0x06ececc4 0x06a306cd [DEFAULT] [hasThis] ValueClass System.Drawing.StringAlignment C1.Util.Styles.StyleContext.GetGeneralAlignment(Class C1.Util.Styles.Style,String)
0x06ececd8 0x06a30246 [DEFAULT] [hasThis] Void C1.Util.Styles.StyleContext.UpdateStringFormat(Class C1.Util.Styles.Style,String)
0x06ececf8 0x0580f464 [DEFAULT] [hasThis] ValueClass System.Drawing.SizeF C1.Util.Styles.StyleContext.GetContentSize(Class C1.Util.Styles.Style,Class System.Drawing.Graphics,ValueClass System.Drawing.SizeF,String,Class System.Drawing.Image)
0x06eced40 0x06a31182 [DEFAULT] [hasThis] I4 C1.Util.Styles.StyleContext.GetContentWidth(Class C1.Util.Styles.Style,Class System.Drawing.Graphics,I4,String,Class System.Drawing.Image)

La información de la excepción se muestra en la siguiente caja, y es equivalente a la presentada en el stack manejado.






Exception 0f69dff0 in MT 79bacb74: System.FormatException
_message: Input string was not in a correct format.
_stackTrace:
00000000
00000000
79bbb998
79a14eb0 [DEFAULT] R8
System.Double.Parse(String,ValueClass System.Globalization.NumberStyles,Class System.IFormatProvider)
06e8eca0
79bac7b0
79a0e3cb [DEFAULT] R8
System.Convert.ToDouble(String)
06e8ed20
79bbf6b0
06a30740 [DEFAULT] [hasThis] ValueClass System.Drawing.StringAlignment
C1.Util.Styles.StyleContext.1M(String)
06e8ed24
0573bf28


¿Qué nos dice [reflector] del código en el método 1M?







No voy a entrar a analizar que hace o no el código.


Lo único que puedo vez rápidamente es que el desarrollador que codificó estas líneas tenia ganas de irse rápido a la casa, como muchas veces me pasó a mí [:)].


¿Por qué poner un try/catch en vez de hacer la validación que correspondería hacer?


¿Es tan difícil usar un expresión regular para validar decimales o doubles, algo así como “[-+]?[0-9]*\.?[0-9]+“?


Muchas de los try/catch que son codificados podrían ser reemplazados por validaciones con ifs y elses, pero consume mucho más tiempo aprender cosas nuevas, y seguramente las pruebas en el computador del desarrollador no detectaron este problema [:)].


Como no fue posible corregir el código defectuoso, ya que estaba en un componente de terceros, procedimos a hacer unos ajustes y remover parte de la funcionalidad que invocaba ese método.


Nueva prueba de carga


El resultado obtenido habla por si solo. Consecuentemente, el grafico del uso de CPU ahora muestra un comportamiento más esperado para una prueba de carga. Y la gran cantidad de excepciones se fueron.



Conclusión


Tarde o temprano, los problemas aparecen. Es mejor invertir un par de horas en aprender algo que te podrá servir muchas veces a futuro, y dejar la chapucería para otro tipo de actividades.


Estas conclusiones están medias pobres, pero se debe a que no hay mucho más que decir. Los gráficos hablan por sí solos.


Adicionales


Si te interesa aprender de expresiones regulares, existe un muy buen libro y muchos sitios en internet. Personalmente uso el libro Mastering Regular Expressions.


Otros sitios web interesantes:



Saludos,
Patrick.

8 Replies to “Entre las excepciones y la flojera de los desarrolladores”

  1. bueno, es que cualquier codigo que este en algun tipo de loop (directo o indirecto) se debe analizar mas de cerca

    y las excepciones son para casos… ejem… excepcionales

    pero esto del tema de las excepciones, me he dado cuenta que es uno de los temas mas dificiles de aprender para los desarrolladores, yo he visto gente con muchos años programando que no saben usar excepciones, asi que que se puede esperar

    salu2

  2. Buenísimo Patrick

    Coincido contigo y con Eber, no sólo no hay q usar inadecuadamente las excepciones sino también q aún cuando hay que usarlas… es complicado!!

    Alguna vez metí un post al respecto

    http://diegumzone.spaces.live.com/cns!1pHxrrKG6RzuZjEIZgyJyg0A!150.entry

    Quiero recomendar a cualquiera un seminario de Krzysztof Cwalina, el papá de la biblioteca de clases básicas de .NET

    http://www.researchchannel.org/…/displayevent.aspx

    Ojo, el seminario dura como 4 horas pero si lo abren y van directo a la hora 2:23 (2 hs 23 mins), Krzysztof cuenta lo bueno, regular y malo de las excepciones, y cómo usarlas inteligentemente

    Patrick, un kilo tu blog

  3. Siempre un placer contar con su presencia en mi blog, y más aún, las felicitaciones….

    Con respecto a Krzysztof Cwalina, no es joda, pero mi proximo libro es este:

    Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries
    http://www.amazon.com/exec/obidos/tg/detail/-/0321246756/ref=ord_cart_shr/002-5670830-6047244?%5Fencoding=UTF8&m=ATVPDKIKX0DER&v=glance

    Voy a chequear tu post al respecto y conversamos…….

    quedamos pendientes en la conversa de arquitectura el otro día

    Saludos,

  4. Totolo,

    concuerdo plenamente con tu comentario y el post sobre el tema. Creo que un escenario como este, trycast es más adecuado que las expresiones regulares.

    Sin embargo, creo en el uso de expresiones regulares y que hay casos donde son la solución a los problemas.

    Lamentablemente, éstas no son estudiadas por lo desarrolladores, probablemente por lo mismo que dices tu, porque son difíciles de entender y aplicar en ciertos escenarios. Aún así, la dificultad no es excusa.

    Saludos,

    Patrick

  5. Me parece genial esta entrada del blog, además las excepciones no son tan complicadas como para ponerse a llorar, y reafirmo lo que dijeron acerca de que es para ocaciones especiales.

    El problema de muchos programadores es que se “Casan” con estilos de programación errados, y se vuelven viciosos y chapuseros. Mejor aprender de vez en cuando, o al menos preguntar a un par para que nos ayude y así hacer trabajos de calidad.

  6. yo creo que las excepciones mal aplicadas vienen heredadas de quienes las usabamos como “la puerta falsa” en vb6, en alguna ocasión me pasó, era tan “cómodo” en el mejor de los casos usar On Local Error GoTo: o bien algo mucho más chapucero,… On Local Error Next
    Indiscutiblemente coincido con ustedes, pero más que nada la recomendación es clara, por decreto…. que las Excepciones se usen solo … Excepcionalmente…
    En horabuena.

Leave a Reply

Your email address will not be published. Required fields are marked *