Este artículo es para expertos solamente.

Como seguramente ya sabes, en Firebird lo preferible y lo recomendable es que las transacciones sean optimistas. Sin embargo hay algunos casos en que se necesita que una transacción sea pesimista.

Estos casos son rarísimos y si los necesitas con frecuencia entonces evidentemente aún no has entendido como funciona Firebird.

Hay una cláusula del comando SELECT que te permite tener transacciones pesimistas pero si no entiendes muy pero muy bien como funcionan las transacciones en Firebird, no deberías usar esa cláusula jamás.

Este artículo es para expertos solamente.

Sintaxis

SELECT
   ...
FROM
   MiTabla
[WHERE
   MiCondición]
[FOR UPDATE [OF...]]
[WITH LOCK]

WITH LOCK realiza un bloqueo pesimista a la fila (o filas) del conjunto resultado del SELECT. Lo recomendable es que:

  1. La cantidad de filas bloqueadas sea muy pequeña (lo ideal es que sea una sola fila)
  2. La aplicación controle muy bien el bloqueo

Si usas WITH LOCK a veces conseguirás que las filas sean bloqueadas y a veces no. Si otra transacción ha previamente bloqueado a alguna de esas filas entonces WITH LOCK fallará.

Si WITH LOCK tuvo éxito entonces ninguna otra transacción podrá hacerle un UPDATE ni un DELETE a las filas involucradas, hasta que la transacción que hizo el WITH LOCK finalice. Por eso el bloqueo es pesimista.

Cuando se incluye la cláusula FOR UPDATE el bloqueo es aplicado a cada fila, una por una, a medida que son extraídas por el Servidor. Es por lo tanto posible (aunque muy improbable, pero posible) que un bloqueo que pareció tener éxito cuando se hizo el SELECT falle más adelante cuando se quiere hacer el UPDATE. ¿Por qué? Porque otra transacción concurrente bloqueó a esa fila e hizo un COMMIT antes de que el SELECT … WITH LOCK finalizara.

Esa es otra razón más para que el SELECT … WITH LOCK involucre a muy pocas filas (idealmente, a una sola fila). De esta manera se minimizará el riesgo.

Un SELECT … WITH LOCK te garantiza que:

  1. Todas las filas del conjunto resultado que obtuviste están bloqueadas
  2. Todas las demás filas no están bloqueadas

Un SELECT … WITH LOCK no te puede garantizar que:

  1. Todas las filas que cumplen con la condición del filtro del WHERE estén bloqueadas

Entendamos mejor este último punto.

Supongamos que si escribes tu SELECT … WHERE obtienes 100 filas, pero al escribir SELECT … WHERE … WITH LOCK obtienes 98 filas. Eso podría ocurrir si otra transacción concurrente ha bloqueado a las 2 filas faltantes entre el inicio y el fin del SELECT … WITH LOCK. Entonces el SELECT … WITH LOCK te garantiza que hay 98 filas bloqueadas pero no te garantiza que no hay filas que cumplen la condición de filtro del WHERE pero que no fueron bloqueadas (en este ejemplo, hay 2 filas que cumplen la condición del WHERE pero no están bloqueadas).

¿Recuerdas que lo ideal es que el SELECT … WITH LOCK devuelva una sola fila? Ahora se entiende mejor el motivo.

Bloqueo de filas

El SELECT … WITH LOCK realiza (si es que puede) un bloqueo pesimista pero siempre cumpliendo con los parámetros de las transacciones.

Eso significa que si el modo de bloqueo de la transacción donde se encuentra el SELECT … WITH LOCK es WAIT y otra transacción previamente ha bloqueado a una fila de su conjunto resultado, esperará hasta que la otra transacción finalice. Si su modo de bloqueo es NO WAIT entonces inmediatamente finalizará con error, y así con los demás parámetros.

¿Dónde no se puede usar SELECT … WITH LOCK?

  • En una subconsulta
  • En un JOIN
  • Con el operador DISTINCT
  • Con la cláusula GROUP BY
  • Con las funciones agregadas: COUNT(), SUM(), AVG(), MAX(), MIN(), LIST()
  • En una vista
  • En la salida de un stored procedure seleccionable
  • En una tabla externa

Conclusión:

En Firebird muy raramente se necesita que una transacción sea pesimista pero si ese es el caso hay varias maneras de conseguirlo, una de ellas es mediante el comando SELECT … WITH LOCK.

Si se usa este comando hay que tratar de que el conjunto resultado tenga muy pocas filas, idealmente una sola fila.

Si la condición de filtro de la cláusula WHERE cumple más de una fila, no se puede garantizar que el SELECT … WITH LOCK haya bloqueado a todas esas filas porque otra transacción concurrente podría haber realizado algún bloqueo.

Artículos relacionados:

Entendiendo a las transacciones

Evitando confilctos en las transacciones

Evitando actualizaciones concurrentes del mismo registro

Transacciones optimistas y transacciones pesimistas

Algo más sobre transacciones optimistas y transacciones pesimistas

¿Por qué en Firebird es preferible que las transacciones sean optimistas?

El índice del blog Firebird21

El foro del blog Firebird21

Anuncios