Concurrencia en aplicaciones GeneXus.

Desde hace unos días, estoy trabajando en optimizar un programa que tiene algunos problemas de concurrencia.
Los problemas que suceden, son de bloqueos que se producen en determinados lugar de la aplicación y algunos deadlocks.

Los problemas de concurrencia y deadlocks, muchas veces son dificiles de diagnositicar y de reproducir, pues se necesitan varios procesos para que los errores se produzcan.

En el proceso que me tocaba arreglar, hay 10 procesos simultáneos que ejecutan y trabajan con tablas que tienen millones de registros, por lo que es necesario hacer los cambios con cierto cuidado. La base de datos es SQL Server 2008.

Algunas cosas que hemos encontrado en estos dias fueron:

1) A SQL Server 2008, ya no le gusta el hint (FASTFIRSTROW) , pues fue sustituido por FAST n.
El tener el hint (FASTFIRSTROW) hace que algunas consultas (en particular joins entre tablas grandes, cabezal y renglones) en vez de filtrar usando un indice de la tabla de renglones, decide recorrer toda la tabla por clave primaria. Modificando la propiedad del objeto GeneXus "Fast first row" en un valor NO, soluciono el problema.

2) Algunos bloqueos que se producian eran del tipo:

for each
if condicion_que_no_puede_evaluarse_en_el_DBMS
-- ATT = &valor
endif
endfor

Esto produce que se defina un cursor que bloquea mas de la cuenta, pues no se bloquean unicamente los registros que se modifican, sino que se bloquean todos.

Por ejemplo, si tengo una tabla

Numero*
Descripcion

y el código

for each
if EsPrimo(Numero)
...Descripcion='EsPrimo'
endif
endfor

A pesar que solamente se quieren modificar los números primos de la tabla, se van a bloquear todos, pues el for each queda definido para actualizar todos los registros.

Una solucion para esto, es sustituir:

Descripcion='Es Primo'

por un procedimiento que haga dicha modificación.

Esto es algo bastante engorroso, que seria bueno evitar, pues se crea un objeto adicional (el procedimiento que actualiza) y el código no queda tan claro.

Estaria bueno poder definir dentro del for each, cuando quiero el comportamiento actual, o uno que solamente bloquee los registros que se van a modificar.

**
No quise complicar la cosa con los dirty reads de los cursores con (nolock) y los "isolation levels", pues complica aun mas el panorama



Comentarios

  1. Muy bueno el post Enrique.

    En los proyectos con el CES nos ha pasado de encontrar problemas de estos. El tema es que como tester si bien siempre intentamos dar la máxima info que podemos no nos ponemos a hacer debugging. La próxima vez que vea un lock les mando el link al blog por las dudas :P

    La pregunta que te quería hacer es, como hacías para reproducir el error? o sea tocabas algo para ver si se corregía y que corrías? tenían algo automatizado?

    Abrazo

    ResponderEliminar
  2. Matias:
    Para tratar de reproducir el problema, teniamos 88 mensajes que eran bastante parecidos y lanzabamos 4 procesos simultaneos a procesarlos, con lo que (algunas veces) lograbamos reproducirlos.

    El gran problema tambien es que cuando pones algo para medir o registrar como por ejemplo, correr con el log activado, muchas veces el error no se presenta, pues el timing de las aplicaciones es diferente.

    Tal como decis, lograr un ambiente de prueba similar a produccion para reproducir estos problemas, es bastante complicado y da mucho trabajo.

    Gracias por el aporte,
    Enrique

    ResponderEliminar
  3. Muy buenos datos.

    Otro que sucede también es el de la ausencia de commit's.

    Dependiendo del tipo de aplicación y la concurrencia, así como si se desea rollback o no, usar commit en algunos puntos estratégicos puede hacer que bloqueos de registros/páginas/tablas sea mucho menor.

    Recuerdo un caso que detectamos hace poco, teníamos una pantalla Win/Java, que usaba un programa que autonumeraba, pero se les pasó poner un commit para cerrar el ciclo, la pantalla quedaba a la espera de la acción del usuario reteniendo y bloqueando a todo aquél que quisiera desde otra parte de la aplicación actualizar ese numerador.

    Algunas herramientas que hoy en día se necesitan son herramientas para detectar qué programa está realizando la modificación (para detectar el bloqueo), y luego tener alguna herramienta para saber cual es el árbol de llamada (en nuestro caso era decenas de programas que usaban ese pgm de autonumerado).

    Teníamos que descubrir cual del call stack era para descubrir cual no hacía commit).

    Aclaro, el problema se complica cuando el uso de los programas es por call dinámico, realmente no puedes conocer y reconstruir desde el IDE de GeneXus con CrossRef quienes realmente lo usan, tiene que existir alguna herramienta a nivel de runtime que nos permita capturar quién por lo menos llamó a cada instancia de un programa.

    Neptuno.net una herramienta desarrollada inicialmente por Concepto nos ayuda para cuando tenemos el crossref en GeneXus, haría falta algo similar en base a info "crossref/commit/rollback" a nivel de runtime.
    http://blog.marcoscrispino.com/2009/10/neptunonet-open-source.html

    ¿Conocen si en GX X+ lo del FastFirstRow si el DBMS es SQL 2008 usa el Fast n? Si ya lo tiene excelente :-).

    ResponderEliminar
  4. David:

    El tema del numerador, generalmente lo hemos resuelto con la propiedad

    "Execute in a new LUW" que permite ejecutar en una nueva unidad logica y hacer commit, de forma de poder tomar un numero y liberar el registro, sin cortar la transaccion que estamos manejando.

    El commit, es todo un problema, por la forma en que Genexus lo maneja.
    Muchas veces al agregar un objeto nuevo, los programadores mas novatos dejan la propiedad de "Commit on Exit" en *YES (que es el default y no se puede cambiar) y arma bastante lio.

    Con respecto a la GeneXus X, por lo que vi, sigue manejando el FASTFIRSTROW. La sugerencia de cambio quedo en el SAC # 28041. Y esperemos que se pueda implementar en el futuro.

    La herramienta para ver las utl en la Kb genexus, es algo que es necesario.

    No seria dificil hacer una herramienta que procese un log generado por genexus y grafique las UTL que se generan en runtime.

    Gracias por el aporte.

    ResponderEliminar
  5. Pregunta: El problema del FASTFIRSTROWS no pasa por sustituirlo por FAST n sino por no generar ninguno de esos hints. Estoy en lo correcto?

    Sugerencia: Me parece que alcanza con mover la actualización a una subrutina en lugar de definir un procedimiento separado. Creo que es menos engorroso y más claro/visible del por qué.

    ResponderEliminar
  6. Gustavo:
    En el caso de esta consulta, alcanzaba con cambiar el fastfirstrow por fast 20 (era un webpanel que mostraba 20 registros) y se solucionaba.
    Si le poniamos FAST 1, hacia el mismo comportamiento irracional, de recorrer todo la tabla.

    En cuanto a lo del procedimiento o subrutina, es cierto lo que dices, pues con una subrutina queda mas claro y no hay que crear un nuevo objeto.

    Muchas veces en nuestra aplicación, el mismo update se hace de varios objetos diferentes, y en ese caso, nos conviene hacer un nuevo procedimiento para actualizar.

    ResponderEliminar
  7. hola desarrolladores:
    soy nuevo en desarrollo sobre geneXus ev1 y estoy con un problemita:
    tengo las siguientes vbles &Monto, &cantidad y PrecioUnitario; al presionar la tecla Tab despues de cargar la cantidad, sale del foco de Cantidad sin hacer el calculo del Monto, cosa que si realiza cuando presiono la tecla Enter debido al evento Enter.
    ¿como puedo solucionar para que calcule el monto al presionar la tecla tab?.
    ya he probado con javaScript sin resultados positivos. Ayudenme Porfavor.
    Perdonen mi interrupcion y haber cambiado el tema de la conversacion, pero es que estoy bastante desesperado con esto...
    Gracias...

    ResponderEliminar
  8. Elias, con la informacion que das de como esta programado, es dificil darte una respuesta.

    Podes ver el evento IsValid() http://wiki.gxtechnical.com/commwiki/servlet/hwiki?IsValid+Event, de la variable cantidad, y ahi realizar el calculo de Monto, pero como te dije arriba, depende mucho de como este programado.

    ResponderEliminar

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

El Sordo

StackOverflow Documentation

Codigo simple