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