Mejorar la performance de una aplicación GeneXus / Sqlserver

La semana pasada un cliente, al cual le habíamos brindado apoyo de consultoría hace unos años, nos pidió ayuda pues tenía una aplicación con problemas de performance.
El proyecto terminó hace mas de de dos años, por lo que si bien conocíamos la aplicación, a la misma le habían realizado varios cambios.

El problema

Una aplicacion generada en GeneXus 8.0, con .NET y SQL Server 2000, con componentes win/2 capas, webservices y páginas web. La cantidad de usuarios, es de unos 400 para la aplicación win y algunos miles para la aplicacion web.
El problema concreto que tenían era una aplicación que recibe mensajes xml en una cola (en realidad utiliza el File System para esto), los procesa y los responde. Este proceso estaba demorando muchísimo cuando recibía mensajes xml de mas de 4Mb, que llegaba a demorar mas de una hora en procesarlos, produciendo atrasos importantísimos en el procesamiento de los mensajes posteriores.

El proceso

Cuento un poco el proceso que seguimos para solucionarlo, pues puede servirle a alguien mas en la comunidad. Antes que me lo reprochen, fue un trabajo en equipo, donde con Ruben, Gabriel, Gustavo y yo.

Tratamos de encarar el problema desde varios puntos en forma simultánea y fue fundamental hacerse algunas preguntas básicas que ayudan a resolverlo.

Las que nos hicimos fueron:

1) Demora solo en xml grandes?
2) Se necesitan xml grandes?
3) Se puede ejecutar en paralelo mas de un proceso?
4) El proceso demora en algun punto en particular?

1) Demora solo en xml grandes?

La respuesta a esta pregunta fue SI.

2) Se necesitan xml grandes?

Rápidamente y conversando con los usuarios funcionales de la aplicación, se vió que no era necesario la utilizacion del nivel de detalle que se estaba utilizando, por lo que se podía reducir muchísimo el tiempo de procesamiento, las necesidades de almacenamiento y el uso de ancho de banda, con una modificación en la forma que se solicitaban los mensajes xml. Este cambio, que no implica cambiar nada en la programacion, es el que puede brindar las mayores mejoras en el mediano plazo.

3) Se puede ejecutar mas de un proceso en paralelo?

Se pusieron a funcionar mas de un proceso procesando los mensajes en paralelo con lo cual, sin hacerle ningun cambio a los programa, se logró bajar el tiempo de respuesta a los mensajes, de una forma muy importante. Lo bueno de este punto, es que agregando mas procesos/hardware, se puede escalar muy bien en la solución.

4) El proceso demora en algun punto en particular?.

Recien en esta etapa, llegamos a ver que es lo necesario cambiar en la programación para lograr mejorar la performance.
Como regla general, cada vez que una aplicación Genexus demora, conviene revisar cuales son las sentencias que esta ejecutando en la base de datos, para ver donde se pueden tener problemas.

4.1) Registrar Sentencias ejecutadas

Para este caso, utilizamos el SQL Server Profiler, que permite capturar las sentencias que se ejecutan en la base de datos. Para esto, tenemos un template definido para java y otro para .NET, donde se capturan datos como
  • Sentencia ejecutada
  • Cantidad de lecturas
  • Cantidad de escrituras
  • Duracion en ms
  • Cantidad de CPU utilizada

Luego corremos el proceso que se quiere optimizar, con un usuario especifico (y diferente a todos los demas) y nos quedamos unicamente con las sentencias ejecutadas por ese usuario.
Se salva la informacion capturada, en tabla en otra base de datos.

4.2) Detectar sentencias ofensivas

Luego agrupando por la sentencia ejecutada, hacemos agrupaciones y sumas consultando:

  • Las diez sentencias ejecutadas mas veces
  • Las diez sentencias cuya suma de duracion es mayor
  • Las diez sentencias cuya suma de CPU es mayor
  • Las diez sentencias cuya suma de lecturas es mayor
  • Las diez sentencias cuya suma de escrituras es mayor

Con este proceso (que ya tenemos bastante automatizado) logramos obtener un conjunto mucho mas manejable de puntos donde pueden estar los problemas de performance.
De un conjunto de unas 520.000 sentencias que se ejecutaron, logramos un subconjuto de unas 30 "sentencias ofensivas", que son mucho mas manejable.

4.3) Detectar en que objetos GeneXus se ejecutan dichas sentencias.

Despues esta la tarea (nada facil) de ver en que objetos se ejecutan estas sentencias.
Para esto, lo mejor es utilizar algun buscador de texto (yo uso el Search&Replace) , para buscar en los fuentes generados por Genexus.

En este caso por ejemplo tenemos que encontrar que objeto ejecuta esta sentencia:

"SELECT [SINUME_SERIE], [SICLASE], [PICODI_ADUAN], [PIANO_PRESE], [PINUME_CORRE], [PINRO_SECUE] FROM [SERINC] (NOLOCK) WHERE [SINUME_SERIE] = @P1 ORDER BY [PICODI_ADUAN], [PIANO_PRESE], [PINUME_CORRE], [SINUME_SERIE], [PINRO_SECUE]"

Hay que tener cuidado , pues los parametros en este caso @P1 no estan en el codigo de esta forma, por lo que hay que hacer algunas modificaciones en lo que hay que buscar.

Como regla sencilla, se puede buscar lo que hay hasta el primer parametro

"SELECT [SINUME_SERIE], [SICLASE], [PICODI_ADUAN], [PIANO_PRESE], [PINUME_CORRE], [PINRO_SECUE] FROM [SERINC] (NOLOCK) WHERE [SINUME_SERIE] ="

en todos los fuentes *.CS del directorio del modelo, y no incluir el directorio web (en este caso no estabamos buscando objetos web).

En caso de que devuelva mas de un objeto, hay que estudiar bastante mas los fuentes generados y la logica en GeneXu para entender cual (o cuales ) son los que se estan ejecutando.

4.4) Corregir los Objetos.

Una vez identificado dichos objetos, hay que cambiar la programación para lograr que genere sentencias mas eficientes.

Los problemas que aparecen mas a menudo son:

4.4.1) Sentencias que no le llegan condiciones al Where

Algunas veces pasa que aunque en programa GeneXus diga que tiene un condición, la misma no se pasa al where de la sentencia, sino que se resuelve con un if. En este caso estaba pasando esto, por lo que cuando llegaba un xml con muchos elementos, los mismos se guardaban en la base de datos y luego se recorrian varias veces y una sentencia que debia traer 1 registro, estaba devolviendolos todos (llegue a ver con 8000 registros) y lo filtraba en el programa. Reformulando un poco la condicion del for each, logramos mejorar la performance de esa parte.

4.4.2) Sentencias que estan ordenando en forma innecesaria

Algunas sentencias estaban devolviendo el resultado en forma ordenada, y no era necesrio.

Se les agrego un "order none" y esto mejoró un poquito la performance.

4.4.3) Sentencias que realizan joins complejos

En este caso no se nos presento este problema, pero muchas veces se logran simplifcar joins.

4.4.4) Sentencias que demoran aunque esten bien formadas

En esots casos, paso las sentencias a los DBAs, que hacen sus magias habituales que incluyen

  • Estudio de estadisticas de la tablas
  • Estudio sobre los histogramas de distribución
  • Defragmentación de indices
  • Tamaño de tablas
  • Definición de indices
  • Parametros de la sentencias (tipos de datos)
  • Borrar datos innecesarios

4.4.5) Sentencias que se ejecutan muchas veces

Muchas veces estas sentencias son ejecutadas leyendo tablas de parametros o metadatos que varian poco. Habilitando el cache de sentencias, se logran mejoras importantes, se carga menos el servidor de base de datos y ademas el habilitarlo da muy poco trabajo y las mejoras son instantaneas.

4.4.6) Locks y Deadlocks

La explicación de como lidiar con estos problemas, lo dejo para otro post, pues este ya quedó demasiado largo.

Alguien quiere seguirla?.

Esta metodologia ya le hemos usado en varias aplicaciones, y ha dado muy buenos resultado, tanto en aplicaciones java y .NET y con Oracle y SQL Server.

Muchas de estas tareas, pueden servir para muchas plataformas y creo que si alguien se anima se podria armar con algo parecido a un Collaborative Project, para automatizar toda esta metodología y hacer una herramienta similar al Profiler, para todas las plataformas soportadas por GeneXus (procesando el log generado por GeneXus?) y también identificar que objeto GeneXus ejecuta las sentencias.

Comentarios

  1. Hola, me parece que está muy bueno el artículo y está bueno compartir la experiencia.
    Yo lo que puedo agregar para facilitar un poco una de las tareas que realizas, es que en lugar de ver las trazas de la base de datos utilices JMX, activando los mbeans de GX (propiedad enable_managment del client.cfg) y luego con una consola cliente como MC4J obtengas esta información del DataStoreProvider. Te loguea en un xml todos los datos ya un poco procesados, y lo bueno y ventajoso es que te dice directamente el programa que generó la consulta SQL sin necesidad de buscarla entre las clases generadas.

    Saludos
    FedeFede

    ResponderBorrar
  2. FedeFede:

    Creo que todo colabora y JMX ayuda mucho a hacer las aplicaciones mas controlables.

    En particular esta aplicación es .NET y estaba generada con Genexus 8.0 por lo que no se podia aplicar lo que tu indicas.

    De cualquier forma, me gusta esta metodologia, pues permite detectar de forma bastante general problemas de performance. Utilizándola, pudimos ver que Triggers y Stored Procedures no desarrollados con GeneXus ni por nosotros, estaban teniendo problemas de performance y solucionandolos, pudimos mejorar la performance de nuestra aplicacion.

    Saludos,
    Enrique
    con E de Entropia

    ResponderBorrar
  3. Hola Enrique, cómo va?
    Después de tanto tiempo llego haciendo alguna búsqueda a la misma página (tu blog) y veo que contestaste mi post :D Gracias!

    Había pasado por alto esos detalles. Qué tal WMI? No se qué facilidades bien da Genexus con esta tecnología, pero es lo que veo medio equivalente en la plataforma Microsoft. Yo no programo Genexus, sino que me dedico a hacer testing de Performance de aplicaciones, y hasta ahora siempre fueron Genexus (eso por estar en Uruguay), y las que vi yo, generadas en Java, por lo que siempre contábamos con esta facilidad, la cual es genial.

    Un abrazo
    FedeFede

    ResponderBorrar
  4. FedeFede:
    El soporte para Instrumentacion en .NET de la 8.0, era medio primitivo y mejoro en la 9.0.

    Creo que igual, para este caso que era una aplicacion de dos capas, el monitoreo de la base de datos, es la mejor opcion, porque como hay muchos procesos a monitorear, usar la opcion de usar WMI no es la mejor opcion.

    Enrique
    con E de Esdrújulo.

    ResponderBorrar
  5. Hola Enrique excelente artículo, Nosotros estamos haciendo el aplicativo de la entidad financiera en la que trabajo, la idea es tener una sola base de datos centralizada ya que el software actual (hecho en delphi) tiene una base de datos por cada agencia que tenemos. actualmente estamos generando en java web y BD Postgresql. llevamos 2 años con el proyecto y tenemos la intención de terminar en Junio y arrancar en enero de 2013 el software en producción. Hasta el momento hemos hecho pruebas y se han corregido errores pero con pocos datos en la BD pues todavía no hemos migrado la información porque no hemos terminado. Es la primera vez que usamos Genexus y te cuento todo esto porque a la final me preocupa bastante (muchísimo) la performance del aplicativo en general. He leído por ahí que los programas generados por genexus no tienen buen rendimiendo. ¿Qué me aconsejas?. tengo 2 años de experiencia con genexus y veo que tu tienes muchísimo conocimiento. Por otro lado FedeFede habla de JMX. Sé que este post es de 2007 y no sé con genexus X Evo1 haya mejores herramientas. Gracias

    ResponderBorrar
    Respuestas
    1. Hola. Las aplicaciones Genexus pueden tener buena performance. Hay que tener cuidado en no ejecutar sentencias pesadas en la base de datos y hacer que la aplicacion no tenga accesos innecesarios a la misma.

      Algunas veces por el nivel de abstraccion en el que Genexus trabaja, es dificil darse cuenta que un programa va a tener buena o mala performance, pero con un poco de optimizacion o puesta a punto del lado de la base de datos, se logran muy buenos niveles de performance.

      Enrique

      Borrar

Publicar un comentario

1) Lee el post
2) Poné tu opinión sobre el mismo.
Todos los comentarios serán leidos y la mayoría son publicados.

Entradas más populares de este blog

La nefasta influencia del golero de Cacho Bochinche en el fútbol uruguayo

Aplicación monolítica o distribuida?

Funcionalidades de GeneXus que vale la pena conocer: DATE Constants.