Ir al contenido



Foto

¿Cómo evitar el deadlock o conflicto al actualizar o eliminar un registro en Firebird?


  • Por favor identifícate para responder
23 respuestas en este tema

#1 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.254 mensajes
  • LocationRepública Dominicana

Escrito 24 marzo 2017 - 10:38

Pues eso, mi sistema trabaja en red, y en varios equipos trabajan haciendo despacho de insumos al mismo tiempo, y me ha ocurrido el típico error lock on no wait transaction update conflicts..., y es debido que actualizan datos hacia la misma tabla, y ya sabeis que se queda esa transacción en el limbo y eso de estar usando GFix cada vez que ocurra está cabrón, utilizo Firebird 2.5 con FireDAC, ahora bien, que medidas debo hacer para evitar ese tipo de conflictos de acuerdo a su experiencia.

 

Saludos.


  • 0

#2 Fenareth

Fenareth

    Advanced Member

  • Administrador
  • 3.486 mensajes
  • LocationMexico City

Escrito 24 marzo 2017 - 11:02

Ese error es frecuente cuando en en los componentes de acceso, en este caso en los FireDAC, en los campos persistentes, no se ha especificado cuáles campos funcionan como campos llave.

 

Si estás utilizando TFDQuerys, es importante que especifiques los campos que funcionan como llave para evitar este problema ya que en realidad la excepción se da cuando se intenta modificar el mismo registro al mismo tiempo en una tabla, no la tabla como tal.

 

Saludox ! :)


  • 1

#3 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.254 mensajes
  • LocationRepública Dominicana

Escrito 24 marzo 2017 - 11:17

En realidad es TFDStoredProc, que actualiza las cantidades de los insumos, y éste es el SP:


sql
  1. CREATE OR ALTER PROCEDURE ACTUALIZA_STOCK (
  2. SALIDA INTEGER,
  3. TIPO VARCHAR(3))
  4. AS
  5. DECLARE variable ID_INSUMO INTEGER;
  6. DECLARE variable CANTIDAD FLOAT;
  7. BEGIN
  8. FOR SELECT is_id, ins_cantidad FROM salidas_almacen_detalles
  9. WHERE sal_id = :salida
  10. INTO :id_insumo, :cantidad do
  11. BEGIN
  12. IF (:tipo = 'in') THEN
  13. UPDATE insumos SET ins_existencia = ins_existencia + :cantidad WHERE ins_id = :id_insumo;
  14. IF (:tipo = 'out') THEN
  15. UPDATE insumos SET ins_existencia = ins_existencia - :cantidad WHERE ins_id = :id_insumo;
  16. /*suspend;*/
  17. END
  18. END

Lo que sí no tiene llaves es en la tabla detalles de los despachos, donde se obtiene la cantidad a descontar de cada insumo, sólo una clave foránea.


  • 0

#4 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.084 mensajes
  • LocationRepública Dominicana

Escrito 24 marzo 2017 - 12:00

Saludos.

 

Tal vez esto te ayude.


  • 0

#5 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.254 mensajes
  • LocationRepública Dominicana

Escrito 24 marzo 2017 - 12:20

Saludos.

 

Tal vez esto te ayude.

 

Entonces según la nota debería usar el FOR UPDATE WITH LOCK?, porque ocurre lo siguiente, la tabla donde obtengo los datos no es el que se actualiza, sino otro, o no estoy entendiendo pues.


  • 0

#6 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.084 mensajes
  • LocationRepública Dominicana

Escrito 27 marzo 2017 - 06:24

Entonces según la nota debería usar el FOR UPDATE WITH LOCK?, porque ocurre lo siguiente, la tabla donde obtengo los datos no es el que se actualiza, sino otro, o no estoy entendiendo pues.

 

Saludos.

 

Correcto mi estimado, esa es la línea.


  • 0

#7 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.254 mensajes
  • LocationRepública Dominicana

Escrito 27 marzo 2017 - 11:14

Bien, estuve leyendo más a fondo ese mecanismo, y he visto que eso aplica siempre y cuando sea la misma tabla a bloquear, digo, no he encontrado casos donde intervienen tablas diferentes como en mi caso, ahora mismo se me acaba de ocurrir algo, en mi procedimiento recorre los registros de selección y va actualizando, ¿hay manera de abrir la transacción e ir haciendo commit en cada paso en el mismo SP?.


  • 0

#8 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.084 mensajes
  • LocationRepública Dominicana

Escrito 27 marzo 2017 - 12:25

Bien, estuve leyendo más a fondo ese mecanismo, y he visto que eso aplica siempre y cuando sea la misma tabla a bloquear, digo, no he encontrado casos donde intervienen tablas diferentes como en mi caso, ahora mismo se me acaba de ocurrir algo, en mi procedimiento recorre los registros de selección y va actualizando, ¿hay manera de abrir la transacción e ir haciendo commit en cada paso en el mismo SP?.

 

Saludos.

 

Estimado enecumene, no creo que sea una buena idea ir realizando commit por cada linea de actualización.

 

Me parece que has captado a media la idea del Lock, la tablas que recorres no debes ponerla en el modo "Lock" mas bien la otra tabla insumos.

 

No he probado el código y siguiendo tu idea (aunque tal vez pueda mejorarse) quedaría algo así:


sql
  1. CREATE OR ALTER PROCEDURE ACTUALIZA_STOCK (
  2. SALIDA INTEGER,
  3. TIPO VARCHAR(3))
  4. AS
  5. DECLARE variable ID_INSUMO INTEGER;
  6. DECLARE variable CANTIDAD FLOAT;
  7. BEGIN
  8. FOR SELECT is_id, ins_cantidad FROM salidas_almacen_detalles
  9. WHERE sal_id = :salida
  10. INTO :id_insumo, :cantidad do
  11. BEGIN
  12. SELECT ins_existencia, id_insumo FROM insumos WHERE ins_id = :id_insumo FOR UPDATE;
  13. IF (:tipo = 'in') THEN
  14. UPDATE insumos SET ins_existencia = ins_existencia + :cantidad WHERE ins_id = :id_insumo;
  15. IF (:tipo = 'out') THEN
  16. UPDATE insumos SET ins_existencia = ins_existencia - :cantidad WHERE ins_id = :id_insumo;
  17. /*suspend;*/
  18. END
  19. END


  • 0

#9 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.084 mensajes
  • LocationRepública Dominicana

Escrito 27 marzo 2017 - 12:27

Saludos.

 

Cabe señalar que la configuración de tus transacciones deben tener el parámetro de espera, el "Wait". Conjuntamente con el parámetro de tiempo relacionado.

 

Así tu aplicación se quedará esperando por un pequeño tiempo mientras otra transacción esta en proceso.


  • 0

#10 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.254 mensajes
  • LocationRepública Dominicana

Escrito 30 marzo 2017 - 05:55

Saludos.

 

Cabe señalar que la configuración de tus transacciones deben tener el parámetro de espera, el "Wait". Conjuntamente con el parámetro de tiempo relacionado.

 

Así tu aplicación se quedará esperando por un pequeño tiempo mientras otra transacción esta en proceso.

 

Entonces el asunto quedarí así:


sql
  1. CREATE OR ALTER PROCEDURE ACTUALIZA_STOCK (
  2. SALIDA INTEGER,
  3. TIPO VARCHAR(3))
  4. AS
  5. DECLARE variable ID_INSUMO INTEGER;
  6. DECLARE variable CANTIDAD FLOAT;
  7. BEGIN
  8. FOR SELECT is_id, ins_cantidad FROM salidas_almacen_detalles
  9. WHERE sal_id = :salida
  10. INTO :id_insumo, :cantidad do
  11. BEGIN
  12. SET TRANSACTION READ WRITE WAIT READ COMMITED NO RECORD_VERSION LOCK TIMEOUT 5
  13. SELECT ins_existencia, id_insumo FROM insumos WHERE ins_id = :id_insumo WITH LOCK;
  14. IF (:tipo = 'in') THEN
  15. UPDATE insumos SET ins_existencia = ins_existencia + :cantidad WHERE ins_id = :id_insumo;
  16. IF (:tipo = 'out') THEN
  17. UPDATE insumos SET ins_existencia = ins_existencia - :cantidad WHERE ins_id = :id_insumo;
  18. /*suspend;*/
  19. END
  20. END

Corrígueme si me equivoco.

 

Saludos.


  • 0

#11 asapltda

asapltda

    Newbie

  • Miembros
  • Pip
  • 7 mensajes

Escrito 07 abril 2017 - 08:13

Rolphy Reyes, el 27 Mar 2017 - 1:27 PM, dijo:


sql
  1. ASí tu aplicación se quedará esperando por un pequeño tiempo mientras otra transacción esta en proceso.

Gracias por la informacion, asumamos que el sistema tiene una espera de 5 segundos, que pasa si al pasar 5 segundos el registro que se requiere leer sigue bloqueado? el sistema envia un error, o sigue esperando en forma indefinida, podrias comentar tu experiencia , gracias

 


  • 0

#12 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.254 mensajes
  • LocationRepública Dominicana

Escrito 10 abril 2017 - 01:10

Pues me marca error en la línea 12 del codigo de Rolphy del tipo parsing error, no he encontrado un ejemplo funcional.


  • 0

#13 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.254 mensajes
  • LocationRepública Dominicana

Escrito 10 abril 2017 - 02:08

Y esto me tiene medio alterado, pensé que Firebird manejaba muy bien esa parte, pero en esta ocasión me tiene el c..o colorado. 8o|


  • 0

#14 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.084 mensajes
  • LocationRepública Dominicana

Escrito 11 abril 2017 - 07:02

Saludos.
 
Amigo enecumene, disculpa la tardanza en responder.
 
Te había mencionado que no es efectivo el abrir/cerrar una transacción por cada registro para realizar la actualización. El componente de transacción (Delphi) es quien debe tener parametrizado el tiempo de espera, no el Stored Procedure.
 
De acuerdo con la documentación existente, el código quedaría algo así (modificar hasta tanto compile/funcione)

sql
  1. CREATE OR ALTER PROCEDURE ACTUALIZA_STOCK (
  2. SALIDA INTEGER,
  3. TIPO VARCHAR(3))
  4. AS
  5. DECLARE variable ID_INSUMO INTEGER;
  6. DECLARE variable CANTIDAD FLOAT;
  7. BEGIN
  8. FOR SELECT sad.ins_cantidad, ins.ins_id FROM insumos ins
  9. JOIN salidas_almacen_detalles sad ON ins.ins_id = sad.is_id
  10. WHERE sad.sal_id = :salida FOR UPDATE INTO :cantidad, :id_insumo do
  11. BEGIN
  12. IF (:tipo = 'in') THEN
  13. UPDATE insumos SET ins_existencia = ins_existencia + :cantidad WHERE ins_id = :id_insumo;
  14. IF (:tipo = 'out') THEN
  15. UPDATE insumos SET ins_existencia = ins_existencia - :cantidad WHERE ins_id = :id_insumo;
  16. /*suspend;*/
  17. END
  18. END


  • 0

#15 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.084 mensajes
  • LocationRepública Dominicana

Escrito 11 abril 2017 - 07:03

Saludos.

 

Cabe resaltar que el motor de Firebird, hará el bloqueo del registro solo en la tabla principal en este caso la tabla del From.

 

En caso de que el problema persista, agregale la cláusula WITH LOCK después de FOR UPDATE y realiza las pruebas pertinentes.


  • 0

#16 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.254 mensajes
  • LocationRepública Dominicana

Escrito 11 abril 2017 - 07:20

Gracias Rolphy, haré las pruebas y en el transcurso del día ó tan pronto tenga inconvenientes te comento.


  • 0

#17 asapltda

asapltda

    Newbie

  • Miembros
  • Pip
  • 7 mensajes

Escrito 12 abril 2017 - 08:54

HOLA:

En caso que TIPO sea diferente de in, out podrias dejar el registro bloqueado.

Lo otro que podria ser es que la unica diferencia en el update es que la cantidad suma o resta, podria invertir el signo si la transaccion es de salida

 

Sobre el manejo del tiempo en el componente no lo sea los que uso no manejan ese concepto, seria muy bueno conocer tu experiencia gracias


  • 0

#18 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.254 mensajes
  • LocationRepública Dominicana

Escrito 13 abril 2017 - 06:53

Hasta el momento no se ha presentado problemas, pero esperaré hasta la semana próxima para emitir un juicio al respecto.

Saludos
  • 0

#19 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.254 mensajes
  • LocationRepública Dominicana

Escrito 25 abril 2017 - 05:04

En casi dos semanas de intenso uso puedo decir que ha funcionado perfectamente, gracias Rolphy!.


  • 0

#20 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.084 mensajes
  • LocationRepública Dominicana

Escrito 27 abril 2017 - 07:06

En casi dos semanas de intenso uso puedo decir que ha funcionado perfectamente, gracias Rolphy!.

Saludos.

 

Gracias enecumene por el feedback. Solo por curiosidad para que otros puedan aprovecharse de esta situación, ¿Cómo quedó el código del SP?


  • 0