NO RECORD_VERSION es el defecto en el aislamiento READ COMMITTED

5 comentarios

Esto es importantísimo para recordar y tener en cuenta.

En ambas versiones del libro “The Firebird Book” de Helen Borrie, que es la “biblia” del Firebird dice que si una transacción tiene el aislamiento READ COMMITTED su versionado por defecto es RECORD_VERSION.

Eso no es así (se equivocó la “biblia”) porque en realidad el versionado por defecto es NO RECORD_VERSION, tal y como se establece en el documento “Interbase 6.0 Embedded SQL Guide”, página 67 (está en inglés) y en el documento “Firebird 2.5 Language Reference”, página 348 (está en ruso).

Como en “The Firebird Book” decía que el versionado por defecto era RECORD_VERSION el autor de este blog así lo creyó y lo escribió en un artículo previo (que ya está corregido también).

Entonces y para asegurarnos … verifiquemos cual es el versionado por defecto.

Para ello abriremos dos instancias de ISQL y veremos lo que sucede cuando se actualiza una fila de una tabla.

RECORD_VERSION

Captura 1. Si haces clic en la imagen la verás más grande

Como se puede ver en la Captura 1., cuando no se especifica el versionado el Firebird asume por defecto que se trata de NO RECORD_VERSION. Eso lo podemos comprobar haciendo el UPDATE de una fila en la instancia ISQL 1 y luego queriendo consultar esa misma fila en la instancia ISQL 2. El mensaje “Lock conflict on no wait transaction….” nos está diciendo que ocurrió un conflicto con otra transacción. ¿Por qué? Porque hay otra transacción que hizo un UPDATE o un DELETE a la fila cuyos datos quisimos consultar y el aislamiento de nuestra transacción es READ COMMITTED y su versionado evidentemente es NO RECORD_VERSION, por eso obtuvimos el mensaje de error.

Establezcamos ahora que la transacción sea READ COMMITTED y su versionado RECORD_VERSION y veamos lo que ocurre.

¿Qué ocurrió? Pues que nuestro SELECT que involucra a la fila que la instancia ISQL 1 está actualizando, esta vez sí tuvo éxito. Como podemos ver en la última fila de la Captura 1., obtuvimos lo que se encuentra en la última versión confirmada (es decir, que finalizó con un COMMIT) de esa fila.

Conclusión:

Es muy importante recordar que por defecto el Firebird abre a las transacciones READ COMMITTED con el versionado de NO RECORD_VERSION y que ese es el versionado que provoca más conflictos.

Como podemos ver en la Captura 1. el versionado NO RECORD_VERSION provocó un conflicto, en cambio el versionado RECORD_VERSION pasó sin problema.

De todo esto podemos sacar dos enseñanzas:

  1. No confiar ciegamente en todo lo que dicen los libros (o los blogs, je), sus autores pueden equivocarse.
  2. Si abrimos una transacción READ COMMITTED y no especificamos el versionado, éste será NO RECORD_VERSION y ese versionado provoca muchos conflictos.

Artículos relacionados:

Entendiendo a las transacciones

El índice del blog Firebird21

El foro del blog Firebird

Anuncios

Entendiendo ACID

4 comentarios

Desde los inicios de la Informática se guardaron datos para ser recuperados y procesados más tarde pero muchísimas veces esos datos eran inconsistentes y causaban problemas muy graves.

Veamos un ejemplo:

Se tiene que grabar una venta y para ello se tienen dos tablas:

  • Cabecera de ventas
  • Detalles de ventas

Se grabó la cabecera y antes de que se grabaran todos los detalles ocurrió un corte en la energía eléctrica o se dañó la red o algún otro problema que impidió grabar todos los detalles de dicha venta.

Eso implica que algo está muy mal: la cabecera no corresponde exactamente con los detalles. Y eso es inaceptable.

A veces, ese tipo de error puede ser detectado y corregido enseguida, pero a veces pueden pasar días, meses o inclusive años antes de ser detectado.

Este tipo de problemas y muchos más causaban serios perjuicios a las empresas, motivo por el cual muchas personas se dedicaron a investigar como solucionarlos. A finales de los años 1970 Jim Gray definió las propiedades que debía tener una transacción confiable y en 1983 Andreas Reuter y Theo Härder inventaron la palabra ACID para describir a ese tipo de transacción.

ACID es el acrónimo de Atomicity, Consistency, Isolation, Durability (en castellano: Atomicidad, Consistencia, Aislamiento, Durabilidad)

Atomicidad significa que las modificaciones a la Base de Datos (agregar, borrar o modificar) deben seguir la regla de todo o nada. Si cualquier parte de la transacción falló, por más pequeña que sea dicha parte, toda la transacción falló.

Consistencia significa que solamente datos válidos pueden ser escritos en la Base de Datos. Dentro de una transacción se pueden tener temporalmente datos inválidos pero cuando la transacción termina, sea de la forma que sea, la Base de Datos debe tener solamente datos válidos.

Aislamiento significa que si varias transacciones se están ejecutando al mismo tiempo, ninguna de ellas interfiere con las otras. Eso implica que la transacción A no conoce ni puede cambiar lo que la transacción B está haciendo. Cada transacción está aislada de todas las demás transacciones, o sea que cada transacción es totalmente independiente de todas las demás transacciones.

Durabilidad significa que cuando los cambios a los datos que realizó la transacción son grabados, dichos cambios permanecerán aún cuando se corte la energía eléctrica, se interrumpa la conexión a la red, o cualquier otro problema físico.

Si una Base de Datos no cumple con alguno de esos 4 criterios entonces no puede ser considerada confiable.

Para quienes usamos Firebird hay una muy buena noticia: Firebird cumple con ACID al 100%

¿Cómo se termina una transacción?

Solamente hay dos formas “normales” de terminar una transacción:

    • con un COMMIT
    • con un ROLLBACK

Si una transacción termina con un COMMIT exitoso entonces todos los cambios que ocurrieron dentro de esa transacción (es decir, todos los INSERT, DELETE, UPDATE) son grabados permanentemente en la Base de Datos

Si una transacción terminó con un ROLLBACK entonces ninguno de los cambios que ocurrieron dentro de esa transacción (es decir, ningún INSERT, DELETE, UPDATE) será grabado y la Base de Datos regresará al estado que tenía cuando comenzó la transacción

¿Se puede escribir alguna operación en Firebird afuera de una transacción?

No, Firebird cumple con ACID al 100% y eso implica que todas las operaciones que escribas (INSERT, DELETE, UPDATE, SELECT, EXECUTE PROCEDURE, etc.) siempre estarán, sí o sí, dentro de una transacción.

Sin embargo, yo muchas veces he escrito operaciones sin previamente iniciar una transacción

Es cierto, tú no iniciaste la transacción pero el Cliente del Firebird la inició por tí. Pero no debes olvidarte de lo siguiente: el Cliente del Firebird inició esa transacción con los parámetros por defecto, que son los siguientes:

READ WRITE: lo cual significa que en dicha transacción podrás insertar, borrar o modificar datos

SNAPSHOT: lo cual significa que esa transacción solamente podrá “ver” y conocer los datos que fueron “commiteados” antes de que la transacción empezara

WAIT: lo cual significa que si hay un conflicto con otra transacción porque ambas están queriendo actualizar o borrar la misma fila, esta transacción esperará hasta que la otra transacción termine

Si lo que necesitas es solamente un SELECT entonces sería mejor que tu transacción fuera READ ONLY porque de esa manera obtendrás los resultados más rápidamente. Aquí puedes ver la importancia de iniciar tu mismo las transacciones y no depender del Cliente del Firebird.

¿Por que se necesita que un SELECT esté dentro de una transacción?

Esta es una pregunta muy común de los principiantes, después de todo, si lo único que se hará será obtener datos: ¿cuál es la necesidad de abrir una transacción para ello?

La respuesta es la siguiente: si se pudiera tener a un SELECT afuera de la transacción entonces ese SELECT no estaría aislado y las operaciones de inserción, borrado, modificación de datos que realizaran otras transacciones estarían interfiriendo con él.

Por ejemplo se escribe:

SELECT
   PRD_CODIGO,
   PRD_NOMBRE,
   PRD_PREVTA
FROM
   PRODUCTOS

porque se necesita imprimir un informe con el Código, el Nombre, y el Precio de Venta de los productos para entregarlo a los vendedores. La tabla de PRODUCTOS tiene 2000 filas y antes de imprimir la fila 800 alguien le cambió el Precio de Venta. Después de imprimir la fila 200 alguien le cambió el Precio de Venta.

Esto implica que si inmediatamente después de ejecutarse el primer SELECT se escribió un SELECT exactamente igual, los datos obtenidos serán distintos. Y eso es inaceptable, porque todos los vendedores deben tener el mismo informe.

En cambio, si usamos transacciones y el modo de aislamiento es SNAPSHOT podremos ejecutar ese SELECT miles de veces y todas esas veces obtendremos exactamente el mismo resultado.

Artículos relacionados:

Modos de bloqueo de las transacciones

Bloqueos mortales

Terminar las transacciones de los SELECTs

COMMIT y ROLLBACK en stored procedures y triggers

Detectando aplicaciones y usuarios que mantienen las transacciones abiertas durante mucho tiempo

El índice del blog Firebird21