Ir al contenido


Foto

Transacciones en firebird.


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

#1 look

look

    Advanced Member

  • Miembros
  • PipPipPip
  • 418 mensajes
  • LocationLa Ceiba-Atlantida-Honduras

Escrito 06 enero 2015 - 10:34

Hola amigos , tengo el siguiente codigo:


delphi
  1.     if not  DataModule.IBTransaction1.InTransaction then DataModule.IBTransaction1.StartTransaction;
  2.     try
  3.      
  4.     //// sql insert o update
  5.      
  6.       DataModule.IBTransaction1.CommitRetaining;
  7.     except
  8.       on E: Exception do
  9.       begin
  10.             DataModule.IBTransaction1.RollbackRetaining;
  11.             Application.MessageBox( PCHAR('Ocurrio un error.'+#13+E.MESSAGE), 'Error',MB_ICONERROR );
  12.             exit;
  13.       end;
  14.     END;



este es el que siempre he utilizado para guardar datos en la base de datos,  en este caso guardar una factura en mi sistema de facturacion, he estado haciendo pruebas en dos computadoras, tratando de guardar un registro al mismo tiempo, pero me esta generando un dead lock al guardar al mismo tiempo, he dejado todo el codigo en una sola transaccion y no se porque siempre me salta el dead lock, he revisado el codigo y no doy con el error, espero puedan recomendarme algo para solucionar el problema.
  • 0

#2 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 06 enero 2015 - 11:22

Desde la perspectiva de Firebird bastaría con configurar el parámetro de conexión WaitOnLocks=True, el lío surge con la compatibilidad del IBDatabase  que fue diseñado originalmente para Interbase.

De todas formas intenta agregando a los parámetros de conexión:



delphi
  1. //Conexion.Params.Clear; // Ojo porque tendrías que agregar todos los parámetros
  2.   Conexion.Params.Add('WaitOnLocks=True');



También podrías intentar cambiando el nivel de aislamiento de las transacciones modificando el parámetro IsolationLevel.


Saludos.
  • 0

#3 Nikolas

Nikolas

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 604 mensajes
  • LocationMar del Plata / Bs As / Argentina

Escrito 06 enero 2015 - 12:47

mira quien te genera el deadlock

porque puede ser el numero de factura o los campos de la factura. si es el primer caso es un "UPDATE" y si es lo segundo es un "INSERT"

lo primero lo resolves leyendo antes de escribir y lo segundo, revisa las claves de la tabla.

^o|
  • 0

#4 look

look

    Advanced Member

  • Miembros
  • PipPipPip
  • 418 mensajes
  • LocationLa Ceiba-Atlantida-Honduras

Escrito 06 enero 2015 - 03:43

Desde la perspectiva de Firebird bastaría con configurar el parámetro de conexión WaitOnLocks=True, el lío surge con la compatibilidad del IBDatabase  que fue diseñado originalmente para Interbase.

De todas formas intenta agregando a los parámetros de conexión:



delphi
  1. //Conexion.Params.Clear; // Ojo porque tendrías que agregar todos los parámetros
  2.   Conexion.Params.Add('WaitOnLocks=True');



También podrías intentar cambiando el nivel de aislamiento de las transacciones modificando el parámetro IsolationLevel.


Saludos.


Hola amigo, el parametro WaitOnLocks=True no me la acepta, utilizo los componentes de interbase, quiza es un problema de incompativilidad...
  • 0

#5 look

look

    Advanced Member

  • Miembros
  • PipPipPip
  • 418 mensajes
  • LocationLa Ceiba-Atlantida-Honduras

Escrito 06 enero 2015 - 03:45

mira quien te genera el deadlock

porque puede ser el numero de factura o los campos de la factura. si es el primer caso es un "UPDATE" y si es lo segundo es un "INSERT"

lo primero lo resolves leyendo antes de escribir y lo segundo, revisa las claves de la tabla.

^o|


Hola, he hecho pruebas con un simple insert y me da problemas, ya en trabajado de esta manera antes, pero en MS SQL y no habia tenido problemas de este tipo.

Saludos!
  • 0

#6 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 06 enero 2015 - 05:55

El Deathblock o abrazo mortal se debe a que ambas transacciones se quedan esperando a que finalice la otra.
Fíjate que en el componente IBTransaction tiene una propiedad en la que se establece los parámetros y configuración de dicha transacción. Seguramente tienes puesto el valor Wait, quítalo.

Como ya no uso IBX no recuerdo el nombre de la propiedad y el nombre exacto del valor de opción. Era Wait algo.

Saludos,
  • 0

#7 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 06 enero 2015 - 06:46

El Deathblock o abrazo mortal se debe a que ambas transacciones se quedan esperando a que finalice la otra.
Fíjate que en el componente IBTransaction tiene una propiedad en la que se establece los parámetros y configuración de dicha transacción. Seguramente tienes puesto el valor Wait, quítalo.

Como ya no uso IBX no recuerdo el nombre de la propiedad y el nombre exacto del valor de opción. Era Wait algo.

Saludos,


Haciendo dobleClick sobre el componente IBTransaction aparecen varias opciones, justo la que menciona Delphius (NoWait) se muestra escogiendo la opción Read Committed o Precommitted. Hay varias opciones para probar.

Saludos.
  • 0

#8 look

look

    Advanced Member

  • Miembros
  • PipPipPip
  • 418 mensajes
  • LocationLa Ceiba-Atlantida-Honduras

Escrito 07 enero 2015 - 08:29

Esta son los parametros que utiliza la transaccion.


delphi
  1. read_committed
  2. rec_version
  3. nowait


veo que dice NOWAIT , ¿deberia de ser wait?

Saludos!


  • 0

#9 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 07 enero 2015 - 08:40

Aunque nunca he utilizado estos estos componentes, se me ocurre que hay que quitarlo, de todas formas prueba haber que pasa.

Saludos.
  • 0

#10 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 07 enero 2015 - 08:43

Si mal no recuerdo en La Cara Oculta de Delphi 6 hay una referencia a estos componentes, dales una mirada haber si aclara dudas.

Saludos.
  • 0

#11 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 07 enero 2015 - 09:27

El wait ese no te sirve, solo hace que el error tarde mas o menos en saltar (http://www.firebirdfaq.org/faq151/)

El problema es si das de alta dos facturas en tu sistema "a la vez", es decir, que en el tiempo entre que la transaccion empieza hasta que se hace el commit, otra transaccion ha tocado algo relacionado con tus cambios y le ha hecho un post.

Si las dos facturas usan como indice primario digamos el ejercicio+numero de factura, y ese numero se decide al entrar a dar el alta (si la ultima es la 5, te propone un 6), entonces ya la tienes liada!

Los dos entran a dar un alta y obtienen los dos un codigo 6, uno guarda primero, el segundo no puede, porque la factura 6 ya existe en otra transaccion... es decir, los dos necesitan que el otro termine su proceso para liberar sus dudas y poder hacer commit en su transaccion.

Revisa esto, cual es el indice unico de tus tablas, y que pasa si dos entran a dar un alta, si estos indices se generan como unicos o se cruzan.

Nosotros siempre usamos un "Id" interno y no editable, que se asigna solo en el trigger before insert, de forma que si esta vacio, se toma de un contador que incrementa solo.

La gracia de este truco es que los contadores se actualizan de forma a transaccional,  o con una transaccion de primoridad maixma, como querais verlo, pero esto hace que nunca dos altas tengan un choque por el indice unico.

De todas formas, la raiz podria estar en como "ven" esas transacciones a los datos que ya se han enviado, existen varias formas, lo normal es read_commited. Prueba con estas parametros:

read
read_committed
rec_version
nowait

Son los que usamos por aqui...


  • 0

#12 look

look

    Advanced Member

  • Miembros
  • PipPipPip
  • 418 mensajes
  • LocationLa Ceiba-Atlantida-Honduras

Escrito 07 enero 2015 - 11:11

El wait ese no te sirve, solo hace que el error tarde mas o menos en saltar (http://www.firebirdfaq.org/faq151/)

El problema es si das de alta dos facturas en tu sistema "a la vez", es decir, que en el tiempo entre que la transaccion empieza hasta que se hace el commit, otra transaccion ha tocado algo relacionado con tus cambios y le ha hecho un post.

Si las dos facturas usan como indice primario digamos el ejercicio+numero de factura, y ese numero se decide al entrar a dar el alta (si la ultima es la 5, te propone un 6), entonces ya la tienes liada!

Los dos entran a dar un alta y obtienen los dos un codigo 6, uno guarda primero, el segundo no puede, porque la factura 6 ya existe en otra transaccion... es decir, los dos necesitan que el otro termine su proceso para liberar sus dudas y poder hacer commit en su transaccion.

Revisa esto, cual es el indice unico de tus tablas, y que pasa si dos entran a dar un alta, si estos indices se generan como unicos o se cruzan.

Nosotros siempre usamos un "Id" interno y no editable, que se asigna solo en el trigger before insert, de forma que si esta vacio, se toma de un contador que incrementa solo.

La gracia de este truco es que los contadores se actualizan de forma a transaccional,  o con una transaccion de primoridad maixma, como querais verlo, pero esto hace que nunca dos altas tengan un choque por el indice unico.

De todas formas, la raiz podria estar en como "ven" esas transacciones a los datos que ya se han enviado, existen varias formas, lo normal es read_commited. Prueba con estas parametros:

read
read_committed
rec_version
nowait

Son los que usamos por aqui...


Hola amigo, gracias por tu respuesta... veras tengo una duda, partiendo de lo que entiendo, yo obtengo el siguiente numero de factura de la siguiente manera, con el codigo anterior, hago un select max(documento) a este le sumo 1, esto dentro de la transaccion, es casi imposible que empiecen al mismo tiempo, asumiento que ambas empiecen a guardar casi al mismo tiempo, la primera que entre en transaccion asignara el numero de documento siguiente y como esta en IN TRANSACTION, esta deberia de esperar a que la otra guarde, almenos eso es lo que entiendo con el IN TRANSACTION. :s,  pero pareciera que de alguna manera el IN TRANSACTION no esta funcionando , como que no espera..

Saludos!
  • 0

#13 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 07 enero 2015 - 02:15

En la zona de descargas tenemos el excelente libro de Kinobi que trata sobre las transacciones en Interbase y Firebird. Recomiendo su lectura.

El parámetro NOWAIT por regla general puede evitar los conflictos de deathblock. Está pensado para emitir un error ni bien se presenta el problema. Por su parte el WAIT queda constantemente esperando.
Yo me refería a éste último pensando que quizá sea lo que estuviera ocasionando el abrazo mortal. En una ocasión tuve un problema similar y descubrí que de forma accidental tenía puesto un WAIT, con borrarlo pude manejar mejor las cosas.

Ahora bien, como dije. El NOWAIT funciona en general. Pero también hay que considerar la forma en como se está generando los valores únicos y el tipo de sentencia que nos ha llevado al abrazo mortal. Por ejemplo: Si uno emplea generadores/secuencias para obtener los valores únicos que harán de clave primaria en una operación INSERT mediante TRIGGERS (BEFORE INSERT) la probabilidad de haya un abrazo es poca ya que justamente éstos no están alcanzados por la transacción. Recuerden que los generadores/secuencias escapan al concepto de transacción.

Ahora la mayoría, sino todos, los casos en donde se detectan abrazo mortales son cuando se procede en un UPDATE o en un DELETE en combinación de un segundo UPDATE/DELETE de otra transacción o bien con un INSERT.

Si uno define su propio proceso de como obtener un "ID", como el típico select(max), que está afectado y alcanzado por la transacción y naturalmente por su configuración de aislamiento. Existe la posibilidad de 2 o más transacciones caigan en el pecado de obtener estos abrazos. Máxime si se espera casi al final de todo para asignarle el valor de la clave primaria.
Debido a esto es que muchos optan por un esquema en donde se asigne el valor previamente, o bien, lleve un log o histórico de los valores que se van asignando.

Saludos,
  • 0

#14 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 08 enero 2015 - 06:25

Al grabar una factura, el problema suele estar en que se toma numero al entrar a editar, y hasta que no se escribe todo y se guarda, la transaccion ha estado abierta y nnigun otro cliente puede iniciar un alta de factura en ese tiempo o se crea el conflicto.

La solucion es dejar el numero vacio, no editable, y al grabar, en el trigger before insert, si te llega cero o null, lo cambias por el ultimo mas uno. Esto aun tiene pegas, porque aun puedes chocar, pero muy muy pocas veces, y siempre puedes hacer un try y si falla, nilear de nuevo el numero de factura y repetir el commit.

Es de esas cosas que crees que no pasan nunca, pero si dos clientes lanzan un proceso de facturacion automatica, pueden chocar un 5% de las veces o un 0% en funcion de como plantees este punto concreto.

Ah! Y el parametro "read_committed" es el mas importante! Hace que la segunda factura vea a la primera tras commitearse, aunque esto ocurra a mitad de su transaccion. Activalo!
  • 0




IP.Board spam blocked by CleanTalk.