PDA

Ver la Versión Completa : Dos aplicaciones modificando el mismo registro ...


seoane
26-09-2007, 17:31:07
La situación es la siguiente, tengo dos programas, uno oculto que trabaja como un servicio y que va haciendo cambios en la base de datos, y otro visible en el que el usuario revisa los datos y puede hacer modificaciones.

El problema surge cuando el usuario esta viendo un registro y decide cambiarlo, sin saber que el otro programa, el oculto, ha borrado ya ese registro. Entonces se produce el "famoso" error de deadlock. También se produce un error cuando en vez de modificar ese registro, se modifica otro que depende de el, se produce un error indicando que se ha incumplido una de los "constrains".

Se que este es un error muy común, por eso recurro a vuestra experiencia para preguntaros cual es la mejor forma de solucionar esto. A mi se me ocurren varias formas: un botón para refrescar los datos, cerrar y volver a abrir la transacción antes de hacer los cambios, etc ... puede que sean "ideas de bombero" :o así que os pido vuestra opinión.

Mas datos:

Delphi 7
Firebird 2.0
WindowsXP
Utilizo los componentes de la paleta Interbase, Data Controls y Data Access


Ayudar a este novato diciéndole como resolvéis vosotros este problema, antes de que se ponga a reinventar la rueda :o

Gracias

dec
26-09-2007, 17:44:56
Hola,

Una pregunta Domingo, ¿cada cuánto tiempo el servicio puede modificar registros? Mi experiencia en estos temas es más bien poca, pero, ¿no bastaría en principio con que el programa del usuario mostrase (justamente, al ser mostrado) los registros, la información, recién actualizada? Claro que si el Servicio modifica registros cada N segundos... No sé, no sé... :)

juanelo
26-09-2007, 17:59:59
Hola Seaone,
Una solución seria tener tus querys de actualizacion con esquemas de oldValue=ValorActualEnTablas, es decir, que guardes los valores originales y cuando vayas a realizar algun cambio pues en tu where pongas la condicion de que el valor original deba de ser igual al valor en tablas para que la modifcacion pueda ser llevada a cabo. Bien otra opcion (para mi la mejor) es utilizando los ClientDataSet y DataSetProvider, con esta pareja ya no es necesario hacer a "mano" las querys de actualizacion, insercion y borrado, sino que en automatico te las genera el provider, aunque si se dan "errores" de actualizacion (como el que te da) te da la oportunidad de trartarlos y decidir que hacer.
Espero haberte ayudado.
Saludos

seoane
26-09-2007, 18:00:47
¿cada cuánto tiempo el servicio puede modificar registros? ... ¿no bastaría en principio con que el programa del usuario mostrase (justamente, al ser mostrado) los registros, la información, recién actualizada?
El problema es que el servicio puede modificar los datos en cualquier momento, y el programa del usuario puede estar abierto durante bastante tiempo, así que se tendrían que refrescar los datos, pero ¿cada cuanto tiempo?. Y además, aunque refrescáramos los datos periódicamente, nadie nos asegura que estén actualizados en el momento en que los vayamos a utilizar :(

dec
26-09-2007, 18:04:07
Hola,

Sí... ya me suponía yo que mi idea era también de bombero... ¡torero! Pero aquí habrá quien le sepa tañer Domingo, ya verás, ya. :D :)

seoane
26-09-2007, 18:09:42
Gracias por contestar juanelo, ahora mismo uso los componentes TIBTable y TIBQuery. No hay problema para capturar el error, pero lo que todavía no he decidido es que hacer cuando se produce el error ¿refrescar los datos y avisar al usuario de que vuelva a intentarlo? :confused:

jhonny
26-09-2007, 18:13:45
El problema es que el servicio puede modificar los datos en cualquier momento, y el programa del usuario puede estar abierto durante bastante tiempo, así que se tendrían que refrescar los datos, pero ¿cada cuanto tiempo?. Y además, aunque refrescáramos los datos periódicamente, nadie nos asegura que estén actualizados en el momento en que los vayamos a utilizar :(

Podrias usar el Post_event de FireBird, de esa manera cada que el servicio cambie o borre dicho registro en la BD, podras enviar una "Señal" a tu aplicación y refrescar los datos "inmediatamente".

juanelo
26-09-2007, 18:18:57
Mira,
Para el programa que tiene "interfaz con el usuario", pues lo mas recomendable es que le informes de la situacion (depende en realidad de la logica de negocios de tu app) pero ya tomada una desicion, es decir, imaginate que estas en programa de ventas y que en la pantalla de ventas te aparecen 10 unidades del articulo A, pero en el momento de llevar a cabo la venta (grabar) resulta que solo queda 5 y el cliente te esta solicitando 6, pues lo que yo haria es mandar un mensaje en donde le indico al usuario que no hay suficientes existencias para su orden (pero la desicion ya esta tomada, NO SE GRABO NADA DE LA FACTURA) y por supuesto le sugiero la nueva cantidad (refresco los datos). Ahora bien lo interesante viene en tu servicio, ya que este tiene que tomar las desiciones el solo, sin ayuda del usuario, lo mismo, esto depende de la logica de negocios que tengas.
PD: Puede usar ClientDataSet y DataSetProvider perfectamente con TIBQuery.
Saludos-

seoane
26-09-2007, 18:21:38
Podrias usar el Post_event de FireBird, de esa manera cada que el servicio cambie o borre dicho registro en la BD, podras enviar una "Señal" a tu aplicación y refrescar los datos "inmediatamente".

Me gusta esa idea, voy a probar. Si tengo problemas volveré :cool:

duilioisola
26-09-2007, 18:24:25
En el OnBeforePost del componente que utilizas (TIBTable) podrías verificar antes de grabar si los datos han cambiado.

- Select según la PK verificando que los demás datos sean iguales que los campos que tienes en la tabla.
- Si devuelve EOF, se ha borrado el registro

Si ya no existe --> Mensaje de error y refrescar la tabla o insertarlo nuevamente.
Si ha cambiado algo --> Quizás mensaje diciendo que alguien ya lo ha tocado antes
--> o No importa, se hace el post y el commit.

gluglu
26-09-2007, 18:35:57
Personalmente, me parece mucho más facil que todo lo que estais comentando. :p

En la llamada al procedimiento que modifique el registro, lo que haces es un Refresh de ese registro en concreto, con lo que en el momento de editar tendrás la última actualización que se haya realizado.

Justo después del refresh puedes comprobar a su vez si fue borrado o no el registro, o por supuesto al hacer el refresh, entiendo que todos los 'dependientes' también se actualizarán.

Ya otro tema sería si te interesan actualizaciones 'periódicas' de los datos que en ese momento muestres en pantalla. Pero entiendo que eso es un asunto diferente.

Yo al menos me apaño muy bien con este método. ;)

fjcg02
26-09-2007, 18:38:25
Seoane,
considero que enviar una señal al programa abierto no es una solución 100% efectiva, ya que pueden coincidir el borrado del servicio/refresco/borrado de la aplicación en el tiempo.
Me gusta más la solución que te plantean anteriormente. Al borrar el registro, antes de realizar el borrado, comprobar que sigue existiendo. Si es así, se borra. Si no es así, sacar un mensaje de error para que lo vea el usuario diciendo "No se puede borrar porque ya no existe, los datos han caducado". o similar.
Si además te planteas que puede ocurrir muy a menudo, puedes bloquear el registro, para que quien borre el registro no lo haga si está bloquedo por otro usuario y/o en otra transacción. Ya de esto te podrán aportar más datos los expertos en firebird, ya que no tengo experiencia al respecto. Porque nadie te asegura quién será el que borre el registros, el servicio o la aplicación.

Espero haberte aportado algo.

Suerte y un abrazo - sin dead, de abrazo mortal 'dead lock' ;-)

PD: Vaya , gluglu se me adelantó

ArdiIIa
26-09-2007, 18:42:21
Incredible
El Sr. Seoane es humano, y también pregunta...:D:D

No había visto este hilo. La opción referida a Post_event, sería buena en el caso de que el servicio no hiciera muchos cambios, de otro modo, el programa del usuario/s estarían refrescando datos constantemente y podría interferir en el proceso normal de programa.

A mi juicio, lo mas simple yeficaz habida cuenta de lo dicho, sería comprobar que el registro a modificar existe antes de proceder a esa modificación, ni mas ni menos, Tal vez con un query alternativo o temporal.

ArdiIIa
26-09-2007, 18:44:48
O escribo lento, o este hilo está que arde... según escribí entraron dos mensajes...

jhonny
26-09-2007, 18:45:04
Bueno, todo depende para lo que necesites, si necesitas que el usuario vea en tiempo "real" la manera como cambia dicha información el Post_event es una buena solución, pero si solo necesitas que el programa no deje tocar un registro que no existe porque se borro con el servicio que mencionaste, definitivamente las otras opciones son las mejores ;).

gluglu
26-09-2007, 18:48:08
... para una vez que pregunta un maestro y uno puede ayudar ... :p :p

fjcg02
26-09-2007, 18:50:10
gluglu tiene razón,
cuando uno puede ayudar a un maestro, duerme esa noche a pierna suelta, como más contento.

Un abrazo

eduarcol
26-09-2007, 18:50:37
a mi me parecen valederas la dos soluciones cada una tiene pro y contras, la de jhonny con sus eventos se volveria ineficiente cuando hayan grandes cantidades de usuarios modificando la base de datos, estarias recibiendo mensajes cade segundo

la de gluglu y fjcg02 siempre y cuando los indices y la consulta este bien estructurada ya que si no tardarias un tiempo si la base de datos llega a crecer mucho

seoane
26-09-2007, 18:51:10
Incredible
El Sr. Seoane es humano, y también pregunta...:D:D

Si, soy humano :( ( ya te pillare preguntando sobre la API :p)

¿Que os parece esta idea?

Cuando hago una modificación capturo el error que se produce, aviso al usuario y refresco los datos. Además coloco un botón que permita actualizar los datos, para que el usuario pueda obtener información actualizada cuando lo desee.

eduarcol
26-09-2007, 18:51:31
O escribo lento, o este hilo está que arde... según escribí entraron dos mensajes...

Mira que me ha pasado lo mismo :D:D

ArdiIIa
26-09-2007, 18:55:15
Si, soy humano :( ( ya te pillare preguntando sobre la API :p)

¿Que os parece esta idea?

Cuando hago una modificación capturo el error que se produce, aviso al usuario y refresco los datos. Además coloco un botón que permita actualizar los datos, para que el usuario pueda obtener información actualizada cuando lo desee.

Supongo que te refieres a que cuando el usuario graba (post), capturas el error y haces lo demás...

y si es un registro grande, que el usuario se ha tomado media hora para modificarlo, luego le informas que todo su trabajo se ha ido al garete...:D:D

seoane
26-09-2007, 19:00:18
Supongo que te refieres a que cuando el usuario graba (post), capturas el error y haces lo demás...

y si es un registro grande, que el usuario se ha tomado media hora para modificarlo, luego le informas que todo su trabajo se ha ido al garete...:D:D
Son registros pequeños, con unos pocos campos ¿te parece mal la idea? :confused:

ArdiIIa
26-09-2007, 19:04:58
Son registros pequeños, con unos pocos campos ¿te parece mal la idea? :confused:
Que me va a parecer mal....

Pero al márgen del refresco de la tabla y bajo esas circunstancias, no te cuesta ningún trabajo meter un evento antes de modificar y que ese evento verifique que efectivamente ese registro existe en verdad.
Desde ese evento haces un pequeño Query preguntando Select registro Where ID = al que el usuario quiere modificar
Si existe, procedes
si no existe le avisas y refrescas...

seoane
26-09-2007, 19:07:56
Entonces Ardilla, si te entiendo bien, sugieres que ademas de capturar el error realice una comprobacion antes de empezar para que el usuario no trabaje de mas ¿correcto? :confused:

ArdiIIa
26-09-2007, 19:08:01
Por cierto, ya sería mucha casualidad que el servicio borrara el registro que el usuario está modificando, no ?

ArdiIIa
26-09-2007, 19:08:58
Entonces Ardilla, si te entiendo bien, sugieres que ademas de capturar el error realice una comprobacion antes de empezar para que el usuario no trabaje de mas ¿correcto? :confused:

Exacto, eso quiero decir.
Que te asegures que el registro a modificar, existe en la BD

Casimiro Notevi
26-09-2007, 19:22:46
Yo también me inclino por comprobar antes si se ha modificado/eliminado el registro.
El post_event, en según qué condiciones, puede ser contraproducente.



Edito: Tendrás que "ver" qué hay en el registro antes de editarlo por el usuario, por si se ha modificado,
En caso de haberlo borrado el "otro" programa, no hay problema, dará error al intentar borrar algo que no existe, cuestión de controlarlo en un típico try except y presentarle el mensaje oportuno.

seoane
26-09-2007, 19:47:15
Bueno, si nadie tiene objeciones lo voy a montar así:

- El usuario indica que quiere modificar un registro.
- Compruebo los cambios en el registro, y si ha cambiado aviso al usuario.
- Actualizo el registro.
- Capturo los posibles errores, y si se producen, actualizo y le indico al usuario que lo vuelva a intentar.

Muchas gracias a todos

Caral
26-09-2007, 20:02:46
Hola
Pregunto IB, no permite transacciones?

DataModule1.AC1.BeginTrans;
// lo que se va ha hacer
DataModule1.AC1.CommitTrans;
//y si hay algun problema se crea una Exception
on E:Exception do DataModule1.AC1.RollbackTrans;

En ese orden.
Solo pregunto, porque cuando se hacen este tipo de cosas, se pueden perder datos, me parece.
Saludos

paldave
26-09-2007, 20:13:57
Se me ocurre que podrían abrirse las tablas con la propiedad Exclusive = True, mantenerlas cerradas y abrirlas solo cuando se vayan a modificar. Entonces el programa que no tiene prioridad para modificar las tablas (no sé cual de los 2 es) primero comprueba si ya están usándose, y si es así intenta más tarde, cuando el que tiene prioridad haya terminado de hacer los cambios y haya cerrado las tablas.

Neftali [Germán.Estévez]
27-09-2007, 11:12:49
- El usuario indica que quiere modificar un registro.
- Compruebo los cambios en el registro, y si ha cambiado aviso al usuario.
- Actualizo el registro.
- Capturo los posibles errores, y si se producen, actualizo y le indico al usuario que lo vuelva a intentar.

Bueno, llego tarde...:(:(

En nuestro caso, utilizamos un par de campos extra en la tabla para comprobar los cambios.
TimeStamp y UserUpdate, que permiten saber si se ha modificado desde la última lectura y quien lo ha hecho.
Realizamos la consulta antes de actualizar para comprbar si los valores de memoria son iguales a los de la tabla.
Lectura y actualización en la misma transacción.
Creo que algo habíamos hablado aquí (http://www.clubdelphi.com/foros/showpost.php?p=167678&postcount=2), aquí (http://www.clubdelphi.com/foros/showthread.php?t=37789&highlight=timestamp) y aquí (http://www.clubdelphi.com/foros/showthread.php?t=45035&highlight=timestamp).

seoane
27-09-2007, 12:32:28
Bueno, llego tarde...:(
De ningún modo, como ya dije, en estas labores soy un novato y cuantas mas opiniones tenga de gente con experiencia mejor.

El problema mas grave era que el servicio borraba registros, y el usuario no lo sabia e intentaba modificarlos. Así que ahora antes de modificar nada, compruebo que el registro sigue ahí (de hecho actualizo los datos), y parece que con eso es suficiente por ahora.

Muchas gracias