Haciendo backups con GBAK (1)

2 comentarios

El Firebird nos permite realizar copias de seguridad (generalmente llamadas backups) de nuestras bases de datos de varias formas diferentes, tal como ya habíamos visto en:

https://firebird21.wordpress.com/2013/07/03/los-metodos-para-hacer-backup/

Cada uno de esos métodos tiene sus ventajas y sus desventajas, ninguno es perfecto (evidentemente, porque si uno fuera perfecto ¿para qué tendríamos los demás?).

En el presente artículo empezaremos a ver como hacer backups usando para ello el programa GBAK. Como el tema es muy largo, lo trataremos en varios artículos.

¿Qué es el ciclo backup/restore?

Todos sabemos que debemos realizar frecuentemente copias de seguridad de nuestra Base de Datos porque si ocurre un accidente (como el disco duro dañado), la única forma rápida y confiable de recuperar su contenido en todo o en gran parte es a través de una copia de seguridad actualizada. Este proceso tiene dos partes:

  1. Copiar todo el contenido de la Base de Datos en un archivo. A este paso se le llama realizar el backup.
  2. Usar el archivo de backup para obtener una Base de Datos operativa, a ese paso se le llama restaurar la Base de Datos.

Por lo tanto, el ciclo backup/restore significa: crear un archivo de backup y luego restaurarlo.

Hay que enfatizar que el proceso no está completo si solamente se realiza el backup. Hay que realizar ambos, sí o sí: el backup y el restore. O sea: la copia de seguridad y la restauración de esa copia.

¿Qué programa usaremos para realizar el ciclo backup/restore?

El programa se llama GBAK.EXE, y con él podremos:

  • Realizar el backup
  • Restaurar la Base de Datos

¿Qué tareas realiza GBAK.EXE?

Cuando hace el backup:

  • Lee todo el contenido de una Base de Datos y copia ese contenido en otro archivo. Pero no lo copia completo. Los índices por ejemplo no son copiados. Gracias a eso el tamaño de la copia es menor de lo que sería si los hubiera copiado.
  • No copia la basura en el backup (la Base de Datos original continúa con toda la basura que tenía)

Cuando restaura la Base de Datos:

  • Pone el TID de cada fila de cada tabla en 1. (El TID es un número que identifica a la transacción que creó la fila). Por lo tanto es como si todas las millones y millones de filas de las tablas hubieran sido insertadas por una sola transacción.
  • Verifica que cada fila tenga datos válidos
  • Crea los índices
  • Calcula las estadísticas de los índices

¿Dónde se encuentra el programa GBAK.EXE?

En la sub-carpeta \BIN de la carpeta donde está instalado el Firebird, por ejemplo en:

C:\Archivos de Programa\Firebird\Firebird_2_5\Bin\

¿Es necesario que el programa GBAK.EXE se encuentre ahí para hacer un backup?

No. Puedes tener una copia del programa GBAK.EXE en cualquier carpeta de tu disco duro. Por ejemplo si tienes un programa de contabilidad en la carpeta:

D:\MisSistemas\Contabilidad\

en esa misma carpeta podrías tener una copia de GBAK.EXE y entonces tu programa ejecutable (por ejemplo: CONTA.EXE) podría buscarlo a GBAK.EXE allí mismo. O sea que si lo deseas podrías tener a GBAK.EXE en la misma carpeta donde tienes a CONTA.EXE

¿Qué versión de GBAK.EXE debo utilizar?

La misma versión que el Servidor del Firebird. Si por ejemplo tu versión del Firebird es la 2.5.4, entonces deberías siempre usar el programa GBAK.EXE que viene incluido en esa versión.

¿Y si no coincide la versión del Servidor del Firebird con la versión de GBAK.EXE?

Hmmmmmmm, eso no es recomendable. Podría funcionar bien pero … también podrían ocurrir errores, no hay garantías.

Una buena posibilidad para que no ocurran errores es tener al programa GBAK.EXE en una carpeta compartida. Y asegurándote que corresponde a la versión del Firebird que se está usando. Para no confundirte, puedes renombrar a GBAK.EXE para que te indique su versión, algo como: GBAK_2_5_4.EXE para saber que es el correspondiente a la versión 2.5.4 del Firebird.

Ejemplo:

  • En la carpeta compartida D:\EJECUTABLES\ tienes una copia de GBAK.EXE
  • Esa copia de GBAK.EXE coincide con la versión del Firebird que utilizas
  • Como tu versión del Firebird es la 2.5.4 entonces lo renombras como GBAK_2_5_4.EXE
  • Tus programas ejecutables que se encuentran distribuidos en un montón de computadoras (en nuestro ejemplo: CONTA.EXE) cuando necesitan hacer un backup llaman al programa D:\EJECUTABLES\GBAK_2_5_4.EXE

¿Cuáles son las ventajas de usar GBAK.EXE para hacer los backups?

  • Se obtiene una copia completa de la Base de Datos
  • Los usuarios pueden continuar trabajando normalmente mientras se realiza el backup
  • Al restaurar el backup se reconstruyen los índices y sus estadísticas
  • Al restaurar el backup se puede cambiar el tamaño de la página
  • En la Base de Datos restaurada se ha recolectado la basura y se ha realizado el barrido (sweep)
  • Se puede ver que tarea está realizando el programa
  • En el disco duro la Base de Datos restaurada se encuentra (generalmente) menos fragmentada que la Base de Datos original, y por lo tanto todos los accesos son más rápidos

¿Cuáles son las desventajas de usar GBAK.EXE para hacer los backups?

  • El archivo generado no puede ser utilizado directamente, debe ser restaurado con la misma versión de GBAK o con una versión posterior, para poder ser utilizado
  • Si la Base de Datos es muy grande, tanto hacer el backup como la restauración demoran mucho tiempo
  • Si la Base de Datos es muy grande, el archivo de backup también lo será
  • Si la Base de Datos es muy grande, habrá un período de muchos minutos (desde que empezó la ejecución del programa GBAK.EXE hasta que terminó) en que no se contará con un backup actualizado. De todo lo que haya ocurrido dentro de la Base de Datos en ese período no se tendrá un backup.

¿Cuáles son las consecuencias de usar GBAK para hacer backups?

  • La Base de Datos restaurada (si no ocurrieron errores durante la restauración) se encuentra en perfecto estado de salud. Eso significa: índices perfectamente balanceados, estadísticas correctas, nada de basura.
  • En la Base de Datos restaurada todas las filas de todas las tablas que se encontraban en la Base de Datos original ahora tienen un TID (identificador de la transacción) igual a 1.
  • Los identificadores de las transacciones (OAT, OIT, OST, NT) ahora tienen valores muy pequeños. NT (next transaction) no es igual a 1 porque al restaurar se realizan algunas tareas dentro de la Base de Datos y esas tareas inician transacciones. Pero de todas maneras el valor de NT siempre es pequeño, típicamente menos que 100.

¿Por qué en la Base de Datos restaurada algunas consultas son más lentas que en la Base de Datos original?

Uno supondría que en la Base de Datos restaurada, como su salud es perfecta todos los SELECT serían más rápidos que en la Base de Datos original pero … no siempre sucede así.

¿Por qué?

Bien, uno de los métodos que utiliza el Firebird para que las consultas sean muy rápidas es guardar en el caché de la computadora donde se encuentra el Servidor las filas retornadas por los últimos SELECTs.

Si el Servidor se apaga, todo el contenido del caché desaparece (porque se encuentra en memoria RAM, la cual es volátil). Pero en las organizaciones que nunca apagan el Servidor el contenido del caché sirve para agilizar de gran manera a las consultas. Si las filas que solicita un usuario ya se encontraban en el caché entonces desde allí le son entregadas, y eso es muchísimo más rápido que leerlas desde el disco duro.

Sin embargo, al restaurar una Base de Datos el caché se vacía (porque los identificadores de las transacciones cambiaron, ahora ya son todos 1) y entonces hay que reiniciar el proceso de ir cargando el caché.

Como consecuencia, algunas consultas (no todas, solamente las que tenían filas en el caché) serán más lentas en la Base de Datos restaurada que en la Base de Datos original. Claro que esto será solamente durante un corto tiempo, luego será distinto.

¿GBAK siempre realiza el backup correctamente?

No. Y esto es algo súper importante para recordar. Al hacer el backup, GBAK no verifica si los datos que está copiando tienen errores o no. Eso solamente se puede descubrir en el momento de la restauración, nunca en el momento del backup.

Por lo tanto, el backup que realizó GBAK …. puede ser totalmente inservible.

¿Terrible?

Sí, así mismo, eso es. Terrible.

Por ejemplo, una columna definida como NOT NULL tiene valores NULL. Tal cosa no debería ocurrir pero a veces la Base de Datos se corrompe y sucede. GBAK copia esas filas sin ningún aviso, ninguna advertencia, ningún mensaje de error. Sin embargo, al querer restaurar ese backup allí sí se enojará … y finalizará la restauración. El archivo que estaba generando se muere ahí mismo, se vuelve completamente inservible.

Hay métodos para solventar ese problema (que veremos en otro artículo) pero lo importante a recordar es:

JAMÁS SE DEBE CONFIAR EN EL BACKUP. JAMÁS

¿Cómo se puede saber si un backup está correcto?

Ya vimos que al hacer el backup GBAK no nos avisa si la Base de Datos tiene errores. Solamente nos avisa cuando está restaurando, así que por lo tanto….

La única manera de verificar que un backup está ok es restaurándolo.

No hay otra forma.

¿Cuál es la mejor manera de hacer y verificar un backup?

Escribiendo un comando que apenas termine el backup lo restaure, de esa manera enseguida sabremos si nuestro backup es confiable o si por el contrario es inútil (y por lo tanto debemos corregir los errores que tiene y realizar otro backup).

Ese comando es el siguiente:

GBAK -b -user SYSDBA -password masterkey MiBaseDatos.FDB stdout | GBAK -r -user SYSDBA -password masterkey stdin Restaurada.FDB

¿Qué hace este comando?

En lugar de crear un archivo de backup en el disco duro (que es lo normal), lo crea en la salida estándar (stdout). Luego realiza la restauración, tomando como archivo de entrada del segundo GBAK el archivo generado por el primer GBAK.

¿Consecuencia?

Que así hacemos un backup y también una restauración. Y si al restaurar ocurre algún error entonces lo sabremos al instante y podremos corregirlo.

Conclusión:

En el Firebird hay varios métodos para hacer backups, usar el programa GBAK.EXE es uno de esos métodos. Como todo, tiene algunas ventajas y algunas desventajas. Nunca jamás hay que confiar en que el backup está correcto y sin errores porque a veces sí tiene errores. Esos errores solamente pueden descubrirse cuando se está restaurando el backup, es el único momento en que se puede saber si el backup está correcto o no. Por lo tanto, una muy buena política administrativa es apenas terminado el backup intentar la restauración. Si el backup no se puede restaurar es inservible e inútil.

Artículos relacionados:

Los métodos para hacer backup

Backup y restauración al mismo tiempo

Backup y restore a una versión más nueva del Firebird

Entendiendo los identificadores de las transacciones

Entendiendo sweep y garbage collection

La Next Transaction después de un ciclo backup/restore

El ciclo backup/restore usando GBAK

El índice del blog Firebird21

El foro del blog Firebird21

Anuncios

Usando Firebird durante 24/7/365

5 comentarios

Algunas aplicaciones requieren acceso a las bases de datos durante 24/7/365, o sea durante las 24 horas del día, los 7 días de la semana y los 365 días del año. En otras palabras: sin interrupción, por ningún motivo.

Algunos ejemplos típicos son:

  • Sanatorios, hospitales, centros de salud
  • Farmacias que trabajan las 24 horas
  • Estaciones de servicio (expendio de combustibles)
  • Policía
  • Fuerzas Armadas
  • etc.

Una aplicación desarrollada para ser utilizada por ellos tiene la obligación de poder funcionar sin interrupción en cualquier momento del día. No le puedes decir al Director de un hospital que entre las 22:00 y las 23:00 no pueden registrarse los datos de los pacientes porque hay que hacerle un mantenimiento a la Base de Datos. Ni decirle a un General que si mañana el país es atacado por el enemigo entre las 02:00 y las 03:00 entonces no podrá usar la Base de Datos porque ya tiene mucha basura y que hiciste un proceso para que elimine esa basura pero requieres que nadie esté conectado a la Base de Datos.

¿Y cuál es el problema?

El problema radica en que a las bases de datos de Firebird debe hacérsele una tarea de mantenimiento llamada sweep de vez en cuando. El sweep (barrido, en castellano) tiene como finalidad eliminar a todas las versiones inútiles de las filas que fueron modificadas o borradas. Cada vez que una fila es modificada o borrada se crea una versión de esa fila llamada delta. La parte positiva de esto es que puede realizarse un ROLLBACK y dejar a la fila como se encontraba anteriormente si el usuario cambió de idea. La parte negativa es que se va acumulando basura y todas las operaciones se van volviendo más lentas y por lo tanto esa basura debe ser eliminada alguna vez.

 Y el sweep puede demorar mucho tiempo.

¿Cuándo se inicia el sweep?

El sweep puede iniciarse automáticamente (que es su valor por defecto) cuando la diferencia entre la OST (Oldest Snapshot Transaction) y la OAT (Oldest Active Transaction) es mayor que el intervalo predefinido del sweep. Ese intervalo es de 20.000 por defecto pero puede modificarse.

Si el intervalo del sweep es de 0, entonces el sweep nunca se iniciará automáticamente. Eso implica que hay que iniciarlo manualmente. Para ello se usa el programa GFIX con la opción –sweep. En este caso lo recomendable es que nadie esté usando la Base de Datos mientras se realiza el sweep.

 ¿Qué ocurre cuándo se inicia el sweep?

Que la transacción que hizo iniciar al sweep no empezará hasta que el sweep finalice. Supongamos que el intervalo del sweep es de 20.000, entonces el desafortunado usuario que quiso iniciar una transacción cuando OST – OAT fue de 20.001 tendrá que esperar hasta que finalice el sweep antes de que su transacción pueda empezar. A quienes iniciaron sus transacciones antes que él no les afectará, pero a él sí. Y si justo es un General de muy malas pulgas, entonces…

 ¿Se puede evitar que se inicie el sweep sin perjudicar el rendimiento de la Base de Datos?

Sí.

La buena noticia es que si las aplicaciones que usan la Base de Datos están correctamente diseñadas entonces nunca será necesario realizar el sweep.

La basura que se encuentra dentro de una Base de Datos puede deberse a los siguientes motivos:

  • Una operación de UPDATE que finalizó con un COMMIT
  • Una operación de DELETE que finalizó con un COMMIT
  • Una operación de UPDATE que finalizó con un ROLLBACK
  • Una operación de DELETE que finalizó con un ROLLBACK

La basura dejada por las transacciones que finalizaron con un COMMIT es recolectada (garbage collection) automáticamente cuando se hace un SELECT a esas filas. Por lo tanto, para eliminar toda la basura de una tabla que fue dejada por los COMMIT se puede escribir un simple comando: SELECT * FROM MiTabla y listo: esa tabla ya no tendrá basura dejada por los COMMIT.

La basura dejada por el ROLLBACK sin embargo, solamente puede ser eliminada con un sweep.

Por lo tanto la solución es muy simple y muy sencilla: No usar ROLLBACK.

¿Y cómo se puede evitar usar ROLLBACK?

Hay varias alternativas posibles, una posible solución sería esta:

  • Haces un SELECT a la fila que te interesa, por supuesto finalizando ese SELECT con un COMMIT. El resultado de ese SELECT lo guardas en una tabla temporal que no es de Firebird, por ejemplo en una tabla .DBF
  • Le muestras la fila de la tabla .DBF al usuario
  • Le permites que cambie los campos o que borre la fila
  • Si decide Guardar los cambios, entonces haces un UPDATE a la tabla de Firebird con los valores que se ven en la pantalla y luego el COMMIT correspondiente
  • Si decide Cancelar los cambios, entonces allí termina todo, no tocas la tabla de Firebird para nada

Como ves, en estos casos se trabaja un poco más pero te aseguras que todas las transacciones terminen con un COMMIT.

Recolectando la basura

Adicionalmente, en tu aplicación deberías tener un proceso que se encargue de hacer un SELECT * a todas las tablas de tu Base de Datos. Cuando el usuario ejecuta ese proceso entonces se hace un SELECT * FROM MiTabla1, SELECT * FROM MiTabla2, SELECT * FROM MiTabla3, etc.

Recordatorio:

La técnica mostrada en este artículo es necesaria solamente para las bases de datos que deben estar activas durante 24/7/365. En los demás casos se puede seguir el procedimiento normal de hacer el sweep automáticamente o manualmente.

Conclusión:

Las bases de datos de Firebird pueden perfectamente ser usadas en entornos que las requieren abiertas durante las 24 horas, los 7 días de la semana y los 365 días del año. Lo que debemos tener en cuenta en esos casos es que deberíamos evitar el sweep porque el sweep puede demorar mucho tiempo en bases de datos muy grandes. La forma de evitar el sweep es nunca terminar las transacciones con ROLLBACK porque las transacciones que terminan con ROLLBACK dejan basura que solamente puede ser eliminada con un sweep, en cambio la basura dejada por las transacciones que terminan con un COMMIT puede ser eliminada con un comando SELECT * FROM MiTabla.

 Entonces, el problema es causado por la forma en que Firebird actúa cuando se hace un UPDATE o un DELETE que finalizaron con un ROLLBACK pero puede ser solucionado si la aplicación finaliza todas las transacciones con un COMMIT.

Artículos relacionados:

La arquitectura MGA

Entendiendo a las transacciones

Entendiendo a los identificadores de las transacciones

Entendiendo sweep y garbage collection

El índice del blog Firebird21

El foro del blog Firebird21

 

Lock conflict on no wait transaction. Deadlock.

4 comentarios

Si al querer hacerle un UPDATE o un DELETE a una tabla recibes este mensaje de error ¿qué significa?

Que la transacción T1 hizo un UPDATE o un DELETE y antes de que la transacción T1 terminara (con un COMMIT o con un ROLLBACK) la transacción T2 también intentó hacerle un UPDATE o un DELETE a la misma tabla. Eso provocó un conflicto. Y como el modo de bloqueo de la transacción T2 es NO WAIT entonces cuando ocurre un conflicto inmediatamente recibe el mensaje de error correspondiente.

LOCK CONFLICT

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

¿Y cuál es la solución?

Eso depende de las circunstancias.

  • Si la operación que quiso hacer la transacción T2 (es decir, el UPDATE o el DELETE) puede esperar entonces se termina la transacción T2 (con un ROLLBACK) y más tarde se inicia una transacción T3 para que realice la operación fallida.
  • Si la operación que quiso hacer la transacción T2 no puede esperar (quizás porque la transacción T1 jamás podría finalizar normalmente con un COMMIT o con un ROLLBACK debido a que dicha transacción quedó “colgada” porque un usuario presionó las teclas CTRL+ALT+DEL o algo similar) entonces hay que eliminar a la transacción problemática. Este es el caso más complicado.

Eliminando la transacción que causa el bloqueo

En la Captura 1. podemos ver que hay un conflicto y la causa de ese conflicto es que la transacción 133608 está bloqueando los UPDATE y los DELETE a las filas de una tabla. El programa que había iniciado a la transacción 133608 fue cerrado abruptamente por el “Administrador de tareas” del Windows y es por lo tanto imposible que alguna vez la transacción 133608 finalice con un COMMIT o con un ROLLBACK, como debería ser.

¿Y entonces?

Entonces debemos eliminar a la transacción problemática (la número 133608, en este ejemplo) de forma manual. Eso lo conseguiremos con el siguiente comando:

DELETE FROM
   MON$TRANSACTIONS
WHERE
   MON$TRANSACTION_ID = 133608;

COMMIT;

La tabla MON$TRANSACTIONS es una tabla de monitoreo, interna del Firebird, o sea que siempre existe en todas las Bases de Datos. En esa tabla se guardan los datos de cada transacción. Si se elimina una fila de esa tabla entonces la transacción correspondiente deja de existir.

Por supuesto, no te olvides de terminar tu transacción eliminadora con un COMMIT.

¿Pero y si falla el DELETE?

Hay ocasiones en las que el DELETE a una fila de la tabla MON$TRANSACTIONS te fallará. ¿Qué puedes hacer entonces? Finalizar a la transacción T2 con un ROLLBACK. Si eso no es suficiente, salir de todos los programas que usan a esa Base de Datos. Y si sigue sin ser suficiente entonces detener el Servidor del Firebird y luego reiniciarlo (algo que no siempre es posible porque los demás usuarios se quejarán hasta en chino).

Pero … cuidado, porque esto no termina aquí. La transacción problemática (la 133608 en este ejemplo) dejó basura dentro de la Base de Datos y esa basura deberás eliminarla alguna vez (haciendo un sweep). No hagas el sweep cuando mucha gente está usando la Base de Datos porque el sweep siempre causa que todas las operaciones se vuelvan muy lentas.

Artículos relacionados:

Bloqueos mortales

Modos de bloqueo de las transacciones

Entendiendo sweep y garbage collection

El índice del blog Firebird21

 

Verificando periódicamente los identificadores de las transacciones

14 comentarios

Como ya seguramente sabes, puedes verificar cuales son los valores actuales de los identificadores de las transacciones con el comando GSTAT – h, algo como:

VERIFICAR1

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

Si nadie está usando la Base de Datos y si todas tus transacciones finalizan con un COMMIT o con un ROLLBACK entonces siempre deberías ver algo así: la diferencia entre la “Oldest transaction” y la “Oldest active” nunca debería ser mayor que 1 (uno) y la diferencia entre la “Oldest snapshot” y la “Next transaction”  tampoco debería ser mayor que 1 (uno). Por lo tanto en ningún caso la diferencia entre los distintos identificadores debería ser mayor que 2 (dos).

Tú has verificado exhaustivamente tus aplicaciones, estás 100% seguro de que todas las transacciones, sin excepción, finalizan con un COMMIT o con un ROLLBACK, entonces no deberías molestarte en verificar los identificadores de las transacciones porque siempre tendrán una diferencia de 1 ó 2 entre ellos, ¿verdad?

No, falso.

Sería verdad si todas las aplicaciones siempre se cierran correctamente, pero no puedes asegurar de que eso siempre ocurrirá. Si hubo un corte de la energía eléctrica que  afectó a una computadora que tenía una transacción abierta o si un usuario (muy inteligente él, claro) hizo un CTRL-ALT-DEL o apretó el botón de “Reset” de la computadora entonces la aplicación que el inteligente usuario estaba usando sin dudas que finalizó pero las transacciones que esa aplicación tenía abiertas, no. No finalizaron.

Al salir de la aplicación de manera incorrecta (por medio de un CTRL-ALT-DEL, por ejemplo) la aplicación terminó, pero todas las transacciones que esa aplicación tenía abiertas continúan abiertas.

Y pasa un día, pasan dos días, pasan quinientos días y esas transacciones continuarán abiertas.

El Firebird no tiene un time-out, o sea un tiempo máximo para que una transacción continúe abierta. Una transacción “normalmente” solamente puede finalizar con un COMMIT o con un ROLLBACK.

¿Y cuál es el problema de que una transacción que nadie usa continúe abierta?

Que el Firebird no hará una limpieza automática (llamada “garbage collection” o “recolección de basura” en castellano) de las transacciones más nuevas que la OAT (Oldest Active Transaction).

Eso implica que toda la basura que fueron dejando las transacciones más nuevas (siempre se deja basura cuando se modifica o se borra un registro) quedarán dentro de la Base de Datos.

Si la OAT ocurrió hace un mes entonces toda la basura que las siguientes transacciones fueron dejando al modificar o borrar registros están ahora dentro de la Base de Datos.

Y tener basura dentro de la Base de Datos no te provee de algún beneficio y sí te causa problemas: todo es más lento de lo que debería ser. Y cuanta más basura se vaya acumulando, más y más lentas serán todas las operaciones.

Por lo tanto, aunque tú estés 100% seguro de que todas tus transacciones finalizan correctamente con un COMMIT o con un ROLLBACK, como debe ser, siempre debes verificar los identificadores de las transacciones porque nunca podrás descartar que no haya ocurrido un corte de la energía eléctrica o que un usuario no haya presionado las teclas CTRL-ALT-DEL.

Conclusión:

Aunque estés 100% seguro de que todas las transacciones que abren tus aplicaciones son luego cerradas con un COMMIT o con un ROLLBACK igualmente debes verificar periódicamente los identificadores de las transacciones con el comando GSTAT -h porque si por cualquier motivo una aplicación finalizó incorrectamente (por medio de un CTRL-ALT-DEL, por ejemplo) entonces esa aplicación que finalizó incorrectamente pudo haber dejado transacciones abiertas.

Y las transacciones que están abiertas pero que nadie las usa (porque la aplicación que las creó ya finalizó hace mucho) solamente causan problemas.

Por lo tanto, si hay transacciones abiertas pero no usadas debes eliminarlas.

Dependiendo del tamaño de tu Base de Datos y de la cantidad de usuarios concurrentes que puede tener deberías ejecutar el comando GSTAT -h cada día o cada semana o cada quince días, etc., pero debes hacerlo, no te olvides de hacerlo. O alguna vez podrías encontrarte con la desagradable sorpresa que la diferencia entre los identificadores de las transacciones es inmensa.

Artículos relacionados:

Entendiendo los identificadores de las transacciones

Conociendo el programa que mantiene una transacción abierta

El índice del blog Firebird21

 

Parámetros de las transacciones usando Visual FoxPro

12 comentarios

Como ya seguramente sabes, todas las operaciones (INSERT, UPDATE, DELETE, SELECT, FETCH, EXECUTE PROCEDURE, etc.) que puedes realizar con las bases de datos de Firebird requieren ser realizadas dentro de transacciones, sí o sí, en el 100% de los casos.

Esas transacciones deben tener un comienzo y un fin.

Antes de comenzar una transacción puedes (si quieres) establecer cuales serán los parámetros que se usarán en esa transacción. Si tú no los estableces entonces el Cliente del Firebird lo hará por tí.

Parámetros por defecto de las transacciones

Si tú no indicas cuales serán los parámetros que utilizará la siguiente transacción entonces el Cliente del Firebird determina los siguientes:

  • READ WRITE
  • WAIT
  • SNAPSHOT

READ WRITE significa que la transacción podrá cambiar el contenido de las tablas. La alternativa es READ ONLY que significa que se puede leer el contenido de las tablas pero no cambiarlos.

WAIT significa que si encuentra un conflicto con otra transacción entonces esperará hasta que la otra transacción termine. La alternativa es NO WAIT lo cual significa que si encuentra un conflicto con otra transacción inmediatamente se elevará una excepción con un mensaje de error.

SNAPSHOT significa que si otra transacción realizó cambios a las tablas después que esta transacción haya empezado, esta transacción no se enterará de esos cambios. Las alternativas son READ COMMITTED que le permite a esta transacción enterarse de todos los cambios que otras transacciones hayan realizado y confirmado (se confirma con un COMMIT) y SNAPSHOT TABLE STABILITY que abre a todas las tablas que usa esta transacción en forma exclusiva, impidiendo por lo tanto a las otras transacciones realizar cambios a esas tablas.

Especificando los parámetros de la siguiente transacción

Una vez que una transacción empezó ya no puedes cambiar los parámetros de la misma. Eso implica que todos los parámetros debes establecerlos antes de que la transacción comience. Y recuerda que si tú no estableces los parámetros de una transacción entonces el Cliente del Firebird los establece por tí usando los valores por defecto (READ WRITE, WAIT, SNAPSHOT), como vimos más arriba.

Esto es importante recordar, porque puede prestarse a confusión: una transacción nunca hereda los parámetros de la transacción anterior. Los parámetros de una transacción los estableces tú específicamente con los valores que deseas o los establece el Cliente del Firebird, pero en ambos casos solamente se aplican a la siguiente transacción, a ninguna otra.

La clase SQL_CLASES

Si programas con el lenguaje Visual FoxPro entonces puedes descargar SQL_CLASES.PRG, allí encontrarás propiedades y métodos que te facilitarán grandemente el uso de Firebird.

http://www.mediafire.com/view/l36nar3o6ap19b9/SQL_CLASES.PRG

A continuación, la explicación sobre las propiedades y métodos relacionados con las transacciones.

Propiedades

cTransaccionAcceso           = "READ WRITE"     && la alternativa es   : READ ONLY
cTransaccionAislamiento      = "READ COMMITTED" && las alternativas son: SNAPSHOT, SNAPSHOT TABLE STABILITY
cTransaccionModoBloqueo      = "WAIT"           && la alternativa es   : NO WAIT
cTransaccionRecordVersion    = "RECORD_VERSION" && la alternativa es   : NO RECORD_VERSION
cTransaccionReservacion      = "SHARED"         && las alternativas son: PROTECTED, READ, WRITE
cTransaccionTablasReservadas = ""               && la alternativa es   : RESERVING MisTablas FOR PROTECTED WRITE
cTransaccionTipo             = ""               && las alternativas son: ABM, INFORME
nTipoTransaccion             = 2                && 1 = Transacción automática, 2 = Transacción manual.

Estas propiedades serán luego usadas por la función .Abrir_Transaccion(), la cual como su nombre lo indica se encarga de abrir las transacciones.

Métodos

.Abrir_Transaccion() abre una transacción utilizando los valores que tienen las propiedades. Es en esas propiedades donde determinamos como queremos que sea la transacción que se abrirá.

.Abrir_Transaccion_ABM() abre una transacción que será utilizada para realizar cambios a las tablas (INSERT, UPDATE, DELETE). Primero, les asigna valores a las propiedades y luego llama a la función .Abrir_Transacción(), que vimos en el párrafo anterior. Desde luego que podrías tener funciones .Abrir_Transaccion_ABM_2(), .Abrir_Transaccion_ABM_3(), etc. con valores distintos.

.Abrir_Transaccion_Informe() abre una transacción que será utilizada solamente con el comando SELECT. Primero, le asigna valores a las propiedades y luego llama a la función .Abrir_Transaccion(). Si tus necesidades aumentan podrías tener funciones .Abrir_Transaccion_Informe_2(), .Abrir_Transaccion_Informe_3(), etc.

En realidad, podríamos tener solamente la función .Abrir_Transaccion(), sin necesidad de las otras dos funciones. Pero están allí para facilitarnos la vida, así cuando queremos una transacción que cambiará el contenido de alguna tabla llamamos a la función  .Abrir_Transaccion_ABM() y cuando queremos una transacción que solamente realizará consultas llamamos a la función .Abrir_Transaccion_Informe(). Si no tuviéramos a esas dos funciones entonces antes de llamar a .Abrir_Transaccion() tendríamos que establecer los valores de las propiedades, lo cual nos haría escribir más y con el riesgo de equivocarnos alguna vez. Gracias a estas dos funciones escribimos menos y nos aseguramos que las propiedades siempre tengan los valores correctos.

.Ejecutar() se encarga de ejecutar el comando que le enviamos como parámetro. Pero antes de ello verifica que tengamos una transacción abierta con la función .Abrir_Transaccion_ABM() o con la función .Abrir_Transaccion_Informe(). ¿Por qué? Porque así nos aseguramos que la transacción cumpla con nuestros requisitos. Si queremos ejecutar un comando y no tenemos una transacción abierta por nosotros entonces el comando no será ejecutado. En otras palabras, no aceptamos transacciones abiertas automáticamente por el Cliente del Firebird, todas las transacciones debemos abrirlas nosotros usando o la función .Abrir_Transaccion_ABM() o la función .Abrir_Transaccion_Informe(). De esta manera nos aseguramos que la transacción siempre tenga los valores que deseamos que tenga. Si el comando que queremos ejecutar es un COMMIT o un ROLLBACK entonces sabemos que allí la transacción finaliza y se coloca en la propiedad .cTransaccionTipo el valor “”, de esa manera podemos saber que no hay una transacción abierta.

Funciones de Visual FoxPro

Para escribir menos y facilitarnos la vida, en lugar de llamar a las funciones de SQL_CLASES llamamos a otras funciones que realizan la misma tarea. A esta técnica se le llama “enmascarar” y podemos leerla en este artículo:

Enmascarando los stored procedures

Las funciones enmascaradoras que usamos son:

  • ABRIR_TRANSACCION_ABM()
  • ABRIR_TRANSACCION_INFORME()

Como ves, tienen los mismos nombres que los usados en SQL_CLASES pero en este caso se trata de funciones independientes, no son funciones dentro de una clase.

FUNCTION ABRIR_TRANSACCION_ABM
Local lcAlias, llResultadoOK

   lcAlias = Alias()

   llResultadoOK = _SCreen.goSQL.ABRIR_TRANSACCION_ABM()

   if !Empty(lcAlias)
      select (lcAlias)
   endif

Return(llResultadoOK)
*
*
FUNCTION ABRIR_TRANSACCION_INFORME
Local lcAlias, llResultadoOK

   lcAlias = Alias()

   llResultadoOK = _SCreen.goSQL.ABRIR_TRANSACCION_INFORME()

   if !Empty(lcAlias)
      select (lcAlias)
   endif

Return(llResultadoOK)
*
*
FUNCTION CERRAR_TRANSACCION
LParameters tcModo
Local lcAlias, llResultadoOK

   if !Empty(_Screen.goSQL.cTransaccionTipo) && Solamente cierra las transacciones abiertas
      lcAlias = Alias()
      tcModo = iif(VarType(tcModo) <> "C", "COMMIT", tcModo)
      llResultadoOK = iif((tcModo == "COMMIT" .or. tcModo == "ROLLBACK"), .T., .F.)
      if llResultadoOK
         llResultadoOK = SQL_Ejecutar(tcModo, "CUR_CERRAR")
      endif
      if !llResultadoOK .and. tcModo == "COMMIT" && Si no se pudo conseguir que la transacción finalice con un COMMIT, hay que finalizarla con un ROLLBACK
         =SQL_Ejecutar("ROLLBACK", "CUR_CERRAR")
         do MENSAJE_ERROR with "Falló el COMMIT, eso es un error grave.*Se finalizó la transacción con un ROLLBACK"
      endif
      _Screen.goSQL.cTransaccionTipo = ""
      if !Empty(lcAlias) .and. Used(lcAlias)
         select (lcAlias)
      endif
   else
      llResultadoOK = .T.
   endif

Return(llResultadoOK)
*
*

Lo que hacen las dos primeras funciones es, primero guardar el nombre de la tabla .DBF que tenemos abierta (si hay alguna tabla .DBF abierta, por supuesto), segundo llamar a la función de SQL_CLASES que nos interesa y tercero si había una tabla .DBF abierta volver a seleccionarla. Esas funciones nos devuelven un valor indicando si la transacción se pudo abrir exitosamente o no.

La función Cerrar_Transaccion() como su nombre lo indica se encarga de cerrar la transacción que tenemos abierta. Si no le indicamos si queremos cerrarla con un COMMIT o con un ROLLBACK entonces asume que queremos cerrarla con un COMMIT, ya que eso es lo más común. Para que sepamos que ya no hay una transacción abierta le asigna “” a la propiedad .cTransaccionTipo.

Ejemplos de uso de las transacciones

Ahora que ya hemos visto la clase y las funciones relacionadas con las transacciones veamos algunos ejemplos de como usarlas.

=Abrir_Transaccion_Informe()
llResultado = SQL_Ejecutar("SELECT * FROM PERSONALIZAR WHERE PER_USUARI = " + lcUsuario)
=Cerrar_Transaccion()

En este caso queremos realizar una consulta entonces se llamó a la función enmascaradora Abrir_Transaccion_Informe(), se realizó la consulta y se cerró la transacción.

TEXT TO lcComando NOSHOW
   EXECUTE PROCEDURE GRABAR_PERSONALIZACION (?lcUsuario, ?lcImagenFondo, ?lnColorTextoMensajesAyuda, ?lcArchivoSonido)
ENDTEXT

=Abrir_Transaccion_ABM()
llGrabacionOK = SQL_Ejecutar(lcComando)
=Cerrar_Transaccion()

En este caso llamamos a un stored procedure que cambiará el contenido de una tabla, por lo tanto llamamos a la función enmascaradora Abrir_Transaccion_ABM(), ejecutamos el comando y cerramos la transacción.

IMPORTANTE:

En Firebird es extremadamente importante tener a las transacciones abiertas durante el menor tiempo posible. Cuanto menos tiempo dure una transacción mucho mejor, porque eso ayudará a evitar conflictos con otras transacciones que quieran acceder a las mismas filas de las mismas tablas.

Como puedes ver en los ejemplos anteriores el procedimiento es abrir la transacción, ejecutar un comando, e inmediatamente cerrar la transacción. Eso es lo correcto.

Conclusión:

Los parámetros de las transacciones los puede establecer el Cliente del Firebird o los podemos establecer nosotros. Si los establecemos nosotros entonces tenemos un mucho mayor control sobre el actuar de esa transacción.

El lenguaje Visual FoxPro nos permite crear clases, esa es una muy buena característica del lenguaje y debemos aprovecharla. Aquí vemos un ejemplo del uso de las clases, en este caso para usarlas con Firebird.

Enmascarar las llamadas a rutinas o funciones nos facilita la vida porque escribimos menos y nos aseguramos de que todo esté siempre correcto. Las transacciones deben durar el menor tiempo posible, por eso lo correcto es abrirlas, ejecutar operaciones en ellas, y cerrarlas inmediatamente, tal como se ha visto en los ejemplos.

Artículos relacionados:

Entendiendo las transacciones

Enmascarando los stored procedures

El índice del blog Firebird21

El foro del blog Firebird21

Usando Trace Manager

11 comentarios

Con la versión 2.5 de Firebird se agregó algo que puede ser muy útil: Trace Manager.

¿Qué hace Trace Manager?

Nos permite conocer todas las conexiones realizadas a una Base de Datos y todas las transacciones, en tiempo real, o sea apenas ocurren.

El programa que realiza esta tarea se llama FBTRACEMGR.EXE y lo encontrarás en la carpeta \BIN\ de tu instalación del Firebird.

Tarea a realizar:

Queremos monitorear todas las actividades que realiza el Servidor del Firebird, para ello necesitaremos hacer lo siguiente:

  1. Crear un archivo de configuración donde podremos determinar, entre otras cosas:
    • Monitorear todo lo que ocurre en una conexión
    • Monitorear todas las actividades de un usuario
    • Monitorear todas las transacciones en una o varias bases de datos
  2. Ejecutar el programa FBTRACEMGR.EXE con el archivo de configuración anterior
  3. Opcionalmente (y preferiblemente) en lugar de enviar los resultados a la pantalla del monitor podemos enviarlos a un archivo de texto y de esa manera será muy fácil posteriormente revisar ese archivo de texto y conocer exactamente lo que ocurrió en la Base de Datos

Enconces, empezaremos creando con el bloc de notas del Windows un archivo de texto donde guardaremos la configuración a utilizar:

TRACE1

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

Las de arriba no son las únicas opciones posibles, hay más, en la Captura 2 las encontrarás a todas:

TRACE2

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

Ahora que ya hemos creado un archivo de configuración podemos ejecutar el programa FBTRACEMGR.EXE y empezar a monitorear todo lo que acontece dentro de la Base de Datos.

TRACE3

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

En lugar de localhost: puedes necesitar escribir la dirección IP de tu Servidor, opcionalmente seguido por una barra y el número del puerto de conexión, algo como:

192.168.0.1/3051:

Abrimos ahora otra ventana “Símbolo del sistema” y ejecutamos en ella el programa ISQL.EXE y nos conectamos a una Base de Datos para ver lo que sucede con nuestro monitoreo:

TRACE4

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

Y en la otra ventana del sistema (en la cual habíamos ejecutado el programa FBTRACEMGR.EXE) vemos esto:

TRACE5

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

“Trace session ID 3 started” nos indica que esta es la tercera sesión del TraceManager que hemos iniciado hoy.

“TRACE_INIT” nos indica que se inició el Trace.

“COMMIT_TRANSACTION” nos indica que una transacción terminó con un COMMIT.

“ATT_4979” nos indica que esta es la conexión número 4.979 a la Base de Datos desde que fue creada.

“SYSDBA” nos indica quien fue el usuario que se conectó a la Base de Datos. El “:NONE” después de su nombre nos indica que no usó un rol para conectarse. Si un usuario utiliza un rol, el nombre de ese rol aparecerá aquí.

“XNET:WALTER-PC” nos indica la forma de conexión (a través de XNET en este caso, también podría haber sido a través de una dirección IP) y el nombre de la computadora (o la dirección IP si aplica).

“C:\Archivos de programa\…” es el nombre del programa que se usó para conectarse a la Base de Datos. En este caso el nombre de ese programa fue ISQL.EXE pero lo normal es que se trate del nombre de tu aplicación (Contabilidad, Facturación, Ventas, Sueldos, etc.)

“TRA_90742” es el número de la transacción. En este caso fue la transacción número 90.742.

“CONCURRENCY | WAIT | READ_WRITE” indican los parámetros de la transacción. En este caso la transacción fue SNAPSHOT, WAIT, READ WRITE, en otros casos podría ser: READ COMMITTED, NO WAIT, READ ONLY, REC_VERSION, NO_REC_VERSION, etc.

“START_TRANSACTION” nos indica que se inició una transacción.

“EXECUTE_TRIGGER_FINISH” nos indica que finalizó la ejecución de un trigger cuyos datos vemos un poco más abajo.

Y bueno, puede mostrarte más información relacionada pero ya la puedes descubrir por tí mismo, para eso eres lo suficientemente inteligente como para entender lo que ocurre.

Si quieres que el programa FBTRACEMGR.EXE no envíe la información a la pantalla sino que la envíe a un archivo de texto le puedes redireccionar la salida con el símbolo mayor que, como se muestra a continuación:

TRACE6

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

Entonces, más tarde podrás revisar el contenido del archivo MONITOREO1.TXT (desde luego que ese nombre es un ejemplo, tú puedes elegir cualquier otro nombre).

TRACE7

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

Conclusión:

Poder “trazar” las operaciones que ocurren dentro de una Base de Datos puede resultarnos muy pero muy útil cuando vemos que hay problemas con ella, por ejemplo si hay transacciones que no terminan eso nos indica un problema con una aplicación (Contabilidad, Facturación, Ventas, etc.) y debemos encontrar cual es el módulo que abrió una transacción pero nunca la cerró porque las transacciones que se abren y nunca se cierran solamente pueden causar problemas y nunca nos otorgarán algún beneficio. También podemos saber si un usuario se conectó hoy, a que hora se conectó, que operaciones realizó, etc.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

Un formulario para mostrar la transacción actual

Deja un comentario

Como conocer los datos de la transacción actual puede ser muy útil cuando estamos en la etapa de crear y depurar aplicaciones hice un pequeño formulario para que muestre esos datos.

Esta es una captura de pantalla del formulario cuando está en ejecución.

FORMULARIO1

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

Como puedes ver en la Captura 1, toda la información relevante se encuentra ahí. Además, colocando el cursor del mouse sobre cualquiera de los campos se muestra un corto mensaje explicativo, digamos que es un “ayuda memoria”.

Puedes descargar el formulario desde este enlace:

http://www.mediafire.com/download/566t3p6eryburfv/MOSTRAR_TRANSACCION.ZIP

NOTA: Debes reemplazar la llamada a la función SQL_Ejecutar() por una llamada a la función SQLEXEC() porque SQL_Ejecutar() es una función mía que aumenta la potencia de SQLEXEC()

Artículos relacionados:

Una vista para verificar la transacción actual

El índice del blog Firebird21

Older Entries