Problema curioso

Saludos comunidad!

Tengo un problema muy curioso, resulta que tengo una aplicación web, creada con JSP y servlets, utiliza una base datos de Oracle y esta montada en Websphere 6, la aplicacion procesa a la semana unos 500 folios, hay momentos en los que hay hasta 100 personas capturando folios al mismo tiempo durante la semana, hasta aqui todo marcha bien, pero cuando termina la semana me he dado cuenta que de los 500 folios en promedio que se capturaron siempre hay como 5 o 7 folios en los que la informacion de un folio se solapa con otro folio, no entiendo porque lo hace, el codigo al parecer esta bien, yo siento que puede ser problema del websphere o de la configuración de la JVM, no se, o quizá que el metodo que guarda deberia ser Synchronized, no se, alguna idea?

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de ezamudio

varios problemas

Pues debería haber alguna regla en la base de datos que force la integridad de datos, en este caso la unicidad de folios, de modo que no permita a la aplicación guardar folios duplicados, marcando error si intentan hacer eso, y en la aplicación lo manejas reasignando un nuevo folio.

Imagen de Jvan

Si existe esa regla, de

Si existe esa regla, de hecho, el error ocurre nada mas al actualizar el folio, por ejemplo, si tenemos que actualizar el folio 678, se modifican los datos en el JSP y cuando se da actualizar, actualiza el folio 678 pero no con la informacion que capturamos en el JSP, si no que lo actualiza con otro folio x, y en la bitacora guarda ese folio con los datos del JSP y milesimas de segundo despues vuelve a guardar en la bitacora el mismo folio pero ahora con los datos del otro folio x. Y repito de 500 folios a la semana solo ocurre 5 o 7 veces por lo que descarto que sea error en el codigo.

Imagen de ezamudio

código

Pues lo más probable es que sí sea un error en la aplicación. Que ocurra poco es indicativo de que es algo que solamente pasa cuando dos usuarios salvan al mismo tiempo o casi al mismo tiempo, trabajando ambos en el mismo folio o tal vez con distinto folio pero lo más probable es que haya algun objeto que están compartiendo las sesiones y que debería cada sesión (cada usuario) tener su propia copia.

Imagen de Nopalin

concurrencia

Tambien opino lo mismo que @ezamudio. Yo agregaria a la bitacora el usuario que esta haciendo el cambio y tal vez la ip. De esa forma sabrias quien hace que, y no importa lo que te diga el usuario (ya que el usuario siempre miente) si en tu bitacora te dice que juan actualizo un folio 200 milisegundos antes que panchito, entonces el problema no es de tu sistema, si no de los mismos trabajadores que no se sincronizan bien.

Ami una vez me paso algo similar, teniamos un sistema de ordenes de trabajo en el area de mantenimiento para una empresa, cada que se crea una OT (orden de trabajo), se le crea un objeto de tipo Actividad relacionada a este que indica que se ha creado, pero la actividad ahi mismo se finaliza, despues un operador ve la ot y le captura la informacion, creando otra actividad y esta queda abierta. Bueno pues en la lógica no deben existir dos actividades abiertas al mismo tiempo por cada OT, mas si embargo una vez paso que habian dos abiertas, fui y lo corregi, varios meses despues paso lo mismo y les pregunte que si los operadores habian trabajado en la misma OT al mismo tiempo en maquinas diferentes y me dijeron que si, pero al mismo tiempo no, que uno lo habia hecho 10 mins despues, pero revisando el log me di cuenta que la creacion las actividades tenian una diferencia de milisegundos, lo que me llevo a la conclusión de que me mintieron...

En fin, suerte con tu problema.

Sobres!

Edicion... aunque pensandolo bien el problema si es del sistema, tienes que checar por concurrencia en tu sistema. Como utilizo hibernate, lo solucione agregando el famoso "versioning", de esa forma si dos procesos separados se llaman al mismo tiempo, gana quien despues de cambiar los datos y tratar de guardarlos, la version no han cambiado desde que fue leida. (la version se checa justo cuando traes el objeto y se incrementa cuando lo actualizas).

Singletons

Este problemas es comun cuando tus objetos son singletons como los objetos de negocio y dao y haces algo como:

 

Si negocio es un singleton y como dices hay usuarios al mismo tiempo en la applicacion en la misma pantalla (aunq con diferentes datos) pues es muy probable q los datos se planchen si dan guarda/cambia/actualiza al mismo o caso al mismo tiempo.

En caso de usar singletos has algo asi.
 

Recuerda si son singletons no hay que cambiar el estado del objeto entre operaciones o pasan estas cosas.

Imagen de Jvan

Problema resuelto

Por fin pude resolver el problema, y todo se debia a una falta conocimiento en la tecnología Servlet, resulta que lo que hace cualquier servidor de aplicaciones es cargar un servlet y proporcionar un hilo de ese servlet para cada petición, es decir, el servlet solo se carga al inicio y despues por cada invocación solo se llama al método "doGet" o "doPost" de cada hilo, lo que provocaba que si 2 personas modifican folios al mismo tiempo, el servlet era el mismo solo que se ejecutaban 2 hilos distintos para ese servlet, yo pensaba que cada hilo tenia sus correspondientes valores de variables pero no era asi, cada hilo utilizaba los mismos valores de las variables y es por eso que se planchaban los datos de un folio con otro, agregué   al método del update pero me di cuenta que no lo solucionaba, y esto es por que antes de que el proceso llegara a ese metodo se llamaban a otros métodos donde se inicializaban variables, se configuraban sentencias SQL dependiendo del folio, etc, y era ahí donde ocurria el planchado, entonces agregué   al "doGet" y "doPost" y con eso se solucionó el problema. Recuerdo que cuando me enseñaron Servlets en la universidad me dijo mi maestro que habia que tener cuidado con los Servlets porque eran Multihilo pero nunca imagine que las variables internas del servlet tuvieran esos comportamientos. En fin, solo para comentarles cual era el problema y si alguna vez les pasa pues igual puede tratarse de lo mismo.

Saludos.

Imagen de ezamudio

mala solución

Tu solución resuelve la concurrencia pero ahora TODAS las peticiones al servidor van a serializarse. Espera pronto quejas de que el sistema está lento.

Los servlets deben ser "stateless", o sea no guardar ningún tipo de variables con valores asociados a la petición que están procesando, precisamente porque cuando hay dos invocaciones simultáneas te dan problemas. La solución de fondo es rediseñar la manera en que funciona ese servlet.

En todo caso, deberías identificar la sección crítica de código (o secciones, puede haber más de una) y sincronizar solamente esos bloques, en vez de todo el método.

Imagen de Jvan

Sí, se que no es la mejor

Sí, se que no es la mejor solución, al menos no de la manera en como se hizo pero es un Servlet bastante grande y no está muy bien diseñado ya que no está muy bien modulado, es de lo primero que se hizo en el proyecto y la persona que lo hizo ya no está asi que la opcion que decidieron fue hacer eso, esperemos que no afecte tanto.

Imagen de luxspes

No es que no sea la mejor, es que es la peor

Tu solución resuelve la concurrencia pero ahora TODAS las peticiones al servidor van a serializarse. Espera pronto quejas de que el sistema está lento.

Sí, se que no es la mejor solución ... esperemos que no afecte tanto.

No es que no sea la mejor, es que es la peor, si varios usuarios usaran el sistema, tu solucion garantiza el peor rendimiento posible.

Imagen de Jvan

Orale, y que soluciones

Orale, y que soluciones propones luxspes aparte de las que ya comentaron los demas compañeros. Me gustaría tener más opciones, espero me comentes algunas otras.

Imagen de luxspes

Perdon, tal ves sono gacho...

@Jvan: Perdon, tal ves sono gacho, mi intencion no era hacerte una critica desctructiva, es solo que... simplemente, tu solucion es, en terminos de rendimento, la peor. Todo el chiste de los servlets es correr en hilos independientes para poder escalar y atender a diferentes usuarios, si les quitas eso, pues sencillamente estas anulando el proposito para el que se inventaron, y estas producionendo una solucion cuyo rendimiento simplemente no podria ser peor... :'(

Imagen de luxspes

Critical section: syncronized al minimo, como sacas los folios?

El que synchronized haya solucionado tu problema, implica que aquello que protegiste con synchronized es un dato compartido entre tus hilos, y en eso como ya comento zamudio (lo más probable es que haya algun objeto que están compartiendo las sesiones y que debería cada sesión (cada usuario) tener su propia copia) radica tu error. Yo he hecho decenas de sistemas con manejo de folio y no he tenido nunca que usar synchronized para evitar el translape de los folios. supongo que el codigo con el que lo haces tiene un error fundamental, que tu parchaste con el synchronized.

Lo mas grave es que tu pusiste el synchronized en el doGet y doPost, cubriendo toda la funcionalidad del servlet, el synchronized solo se debe usar en un área lo mas pequeña posible, afectando solo al código especifico que altera a la región de memoria compartida (critical section) (objeto o variable compartido) y no sobre el procesamiento de todo el servlet. De nuevo, tu solución funciona, pero (y no es con afan de ofender) es la peor posible en cuanto a rendimiento

No se que estés haciendo mal en tu código (tendria que verlo, si lo subes aqui, lo puedo checar), he visto este problema, por ejemplo,cuando para generar los folios, en vez de utilizar un secuencia (en bases de datos como Oracle) o una tabla foliadora (en bases de datos menos afortunadas), utilizan un "select max()", lo cual, aparte de afectar gravemente el rendimiento de la base de datos por ser tremendamente ineficiente, es a menudo parchado con un synchronized , lo cual lo hace doblemente ineficiente. Recuerda: "select max()" es muy mala idea para generacion de folios.

Si tu sacas los folios de la base de datos,(y lo haces correctamente con secuencias o tabla foliadora) la base de datos misma debería estar resolviendo el problema, y tu no deberías tener que usar synchronized. Por otro lado, si tus folios no tienen nada que ver con la base de datos, entonces si podria tener sentido que usaras synchronized, pero solo en los métodos de un objeto "Foliador" singleton a nivel de toda la aplicación, y que este encargado de generar los folios, no cubriendo todo el ciclo petición/respuesta de tu servlet.

Si yo fuera tu, postearia aqui el codigo con el que vas obteniendo el folio, para que ayudemos a determinar lo que estas haciendo mal.

Saca la funcionalidad a otra clase

Una solucion q tal vez no sea tantediosa y quites el synchronized de los metodos es hacer una clase nueva y ponerle un metodo q reciba el request y el response y q esta clase practicamente haga lo q hace la servlet, por ejemplo:

 

y en la servlet dejas solo algo como así

 

Asi en la servlet solo llamas a la clase q hace tu proceso y no tienes el problema de las variables de instancia, basicamente la clase Proceso tendria doto lo de tu servlet y como se le pasa el request y response pues podria comportarce como la servlet.

Esta es una solución q en lo personal realizaria solo por los tiempos de entrega, porque como comentan lo mejor es rediseñar esto, aunque no lo hayamos construido nosotros.

Imagen de luxspes

Sin ver el codigo, no se sabe, si funciona, es suerte

Saca la funcionalidad a otra clase

Si el cambia todo a otra clase, con suerte y resuelve el problema al aislar accidentalmente alguna variable, pero eso no garantiza que sus variables no generacion de folio sea correcta, si, por ejemplo esta usando select max, o variables static, seguira generando mal los numeros de folio, y seguira habiendo problemas, lo que se necesita aqui es saber que esta haciendo.

Una idea similar, pero en mi opinion mas acertada, seria crear una clase Foliadora que implementara netamente la generacion de folios siguiendo una interfaz como esta:

 

Asi separaria toda la logica de generacion de folios del resto, y quiza podria compartirnos el codigo de la clase y no podriamos dar una idea mas precisa de la causa del problema