Cómo no pegarte un tiro en el pie con Liquibase

¡Nunca sucedió, y aquí está de nuevo!

En el próximo proyecto, decidimos usar Liquibase desde el principio para evitar problemas en el futuro. Al final resultó que, no todos los miembros jóvenes del equipo saben cómo usarlo correctamente. Hice un taller interno, que luego decidí convertir en un artículo.

Este artículo incluye consejos útiles y descripciones de tres de las trampas más obvias en las que puede caer cuando trabaja con herramientas de migración de bases de datos relacionales, Liquibase en particular. Diseñado para desarrolladores de Java de nivel Junior y Middle, para desarrolladores más experimentados puede ser interesante para estructurar y repetir lo que probablemente ya se sabe.

Cómo no pegarte un tiro en el pie con Liquibase

Liquibase y Flyway son las principales tecnologías competidoras para resolver los problemas de control de versiones de estructuras relacionales en el mundo Java. El primero es completamente gratuito, en la práctica se elige con mayor frecuencia para su uso, por lo que se eligió a Liquibase como el héroe de la publicación. Sin embargo, algunas de las prácticas descritas pueden ser genéricas, según la arquitectura de su aplicación.

Las migraciones relacionales son una forma forzada de lidiar con la débil flexibilidad de los almacenes de datos relacionales. En la era de la moda de la programación orientada a objetos, el estilo de trabajar con la base de datos significaba que describíamos el esquema una vez y no lo volvíamos a tocar. Pero la realidad siempre es que las cosas cambian y se requieren cambios en la estructura de las tablas con bastante frecuencia. Naturalmente, el proceso en sí es doloroso y desagradable.

No profundizaré en la descripción de la tecnología y las instrucciones para agregar la biblioteca a su proyecto, se han escrito suficientes artículos sobre este tema:

Además, ya había un gran artículo sobre el tema de los consejos útiles:

Tips

Quiero compartir mis consejos y comentarios, que nacieron con el sudor, la sangre y el dolor de solucionar los problemas de la migración.

1. Antes de comenzar, debe leer la sección de mejores prácticas en sitio web Liquibase

Hay se describen cosas sencillas pero muy importantes, sin las cuales el uso de la biblioteca puede complicarte la vida. Por ejemplo, un enfoque no estructural para la gestión de conjuntos de cambios tarde o temprano generará confusión y migraciones rotas. Si no implementa cambios mutuamente dependientes en la estructura de la base de datos y la lógica de los servicios al mismo tiempo, existe una alta probabilidad de que esto conduzca a pruebas rojas o un entorno roto. Además, las recomendaciones para usar Liquibase en el sitio web oficial contienen un párrafo sobre el desarrollo y verificación de scripts de reversión junto con los principales scripts de migración. Bueno, en el artículo https://habr.com/ru/post/178665/ hay ejemplos de código relacionado con las migraciones y el mecanismo de reversión.

2. Si comenzó a usar herramientas de migración, no permita correcciones manuales en la estructura de la base de datos

Como dice el dicho: "Una vez Persil, siempre Persil". Si la base de su aplicación comenzó a ser administrada por las herramientas de Liquibase, cualquier cambio manual conduce instantáneamente a un estado inconsistente y el nivel de confianza en los conjuntos de cambios se vuelve cero. Riesgos potenciales: varias horas dedicadas a restaurar la base de datos, en el peor de los casos, un servidor inactivo. Si su equipo tiene un arquitecto DBA de la "vieja escuela", explíquele con paciencia y atención lo mal que estarán las cosas si simplemente edita la base de datos a su manera desde el desarrollador de SQL condicional.

3. Si el conjunto de cambios ya se envió al repositorio, evite editar

Si otro desarrollador extrajo y aplicó un conjunto de cambios que se editará más tarde, definitivamente lo recordará con una palabra amable cuando reciba un error cuando se inicie la aplicación. Si la edición del conjunto de cambios se filtra de alguna manera en el desarrollo, tendrá que descender por la pendiente resbaladiza de las revisiones. La esencia del problema se basa en la validación de cambios por hash sum, el mecanismo principal de Liquibase. Al editar el código del conjunto de cambios, la suma hash cambia. La edición de conjuntos de cambios solo es posible cuando es posible implementar toda la base de datos desde cero sin perder datos. En este caso, refactorizar el código SQL o XML puede, por el contrario, hacer la vida más fácil, hacer que las migraciones sean más legibles. Un ejemplo sería una situación en la que, al inicio de la aplicación, el esquema de la base de datos de origen se coordinó dentro del equipo.

4. Tenga copias de seguridad de la base de datos verificadas si es posible

Aquí, creo, todo está claro. Si de repente la migración no tuvo éxito, se puede devolver todo. Liquibase tiene una herramienta de reversión, pero los scripts de reversión también los escribe el propio desarrollador, y pueden tener problemas con la misma probabilidad que en los scripts de conjunto de cambios principales. Esto significa que ir a lo seguro con copias de seguridad es útil en cualquier caso.

5. Use copias de seguridad de bases de datos verificadas en desarrollo si es posible

Si esto no contradice los contratos y la privacidad, no hay datos personales en la base de datos y no pesa ni dos soles: antes de aplicarlo en los servidores de migración en vivo, puede verificar cómo funciona en la máquina del desarrollador y calcular casi el 100% de los problemas potenciales durante la migración.

6. Chatea con otros desarrolladores del equipo

En un proceso de desarrollo bien organizado, todos los miembros del equipo saben quién está haciendo qué. En realidad, a menudo este no es el caso, por lo tanto, si está preparando cambios en la estructura de la base de datos como parte de su tarea, es recomendable notificarlo adicionalmente a todo el equipo. Si alguien está haciendo cambios en paralelo, debe organizarse con cuidado. Vale la pena comunicarse con los colegas incluso al final del trabajo, no solo al comienzo. Muchos problemas potenciales con los conjuntos de cambios se pueden resolver en la etapa de revisión del código.

7. ¡Piensa en lo que estás haciendo!

Un consejo aparentemente evidente aplicable a cualquier situación. Sin embargo, muchos problemas podrían haberse evitado si el desarrollador hubiera analizado una vez más lo que estaba haciendo y lo que podría afectar. Trabajar con migraciones siempre requiere atención y precisión adicionales.

Trampas

Veamos ahora las trampas típicas en las que puede caer si no sigue los consejos anteriores y, de hecho, ¿qué se debe hacer?

Situación 1. Dos desarrolladores intentan agregar nuevos conjuntos de cambios al mismo tiempo

Cómo no pegarte un tiro en el pie con Liquibase
Vasya y Petya quieren crear un conjunto de cambios de la versión 4 sin saberse el uno del otro. Hicieron cambios en la estructura de la base de datos e implementaron una solicitud de extracción, con diferentes archivos de conjuntos de cambios. A continuación se propone el siguiente mecanismo:

como decidir

  1. De alguna manera, los colegas deben ponerse de acuerdo sobre el orden en que deben ir sus conjuntos de cambios, digamos que Petin debe aplicarse primero.
  2. Una persona debe verter la otra y marcar el conjunto de cambios de Vasya con la versión 5. Esto se puede hacer a través de Cherry Pick o una combinación ordenada.
  3. Después de los cambios, asegúrese de comprobar la validez de las acciones realizadas.
    De hecho, los mecanismos de Liquibase te permitirán tener dos conjuntos de cambios de la versión 4 en el repositorio, para que puedas dejar todo como está. Es decir, simplemente tendrás dos revisiones de la versión 4 con nombres diferentes. Con este enfoque, las versiones de la base de datos se vuelven muy difíciles de navegar más adelante.

Además, Liquibase, al igual que las casas de los hobbits, guarda muchos secretos. Una de ellas es la clave validCheckSum, que ha aparecido desde la versión 1.7 y le permite especificar un valor hash válido para un conjunto de cambios específico, independientemente de lo que esté almacenado en la base de datos. Documentación https://www.liquibase.org/documentation/changeset.html dice lo siguiente:

Agregue una suma de verificación que se considere válida para este conjunto de cambios, independientemente de lo que esté almacenado en la base de datos. Se utiliza principalmente cuando necesita cambiar un conjunto de cambios y no desea que se produzcan errores en las bases de datos en las que ya se ha ejecutado (no es un procedimiento recomendado)

Sí, esto no es recomendable. Pero a veces un mago ligero fuerte también domina técnicas oscuras.

Caso 2: Migración basada en datos

Cómo no pegarte un tiro en el pie con Liquibase

Digamos que no puede usar copias de seguridad de bases de datos de servidores en vivo. Petya creó un conjunto de cambios, lo probó localmente y, con plena confianza de que tenía razón, realizó una solicitud de incorporación de cambios al desarrollador. Por si acaso, el líder del proyecto aclaró si Petya lo verificó y luego lo vertió. Pero la implementación en el servidor de desarrollo ha caído.

De hecho, esto es posible, y nadie es inmune a esto. Esto sucede si las modificaciones a la estructura de la tabla están vinculadas de alguna manera a datos específicos de la base de datos. Obviamente, si la base de datos de Petya se llena solo con datos de prueba, es posible que no cubra todos los casos problemáticos. Por ejemplo, al eliminar una tabla resulta que hay registros en otras tablas por Foreign Key asociados a registros en la que se está eliminando. O al cambiar el tipo de columna, resulta que no se puede convertir el 100% de los datos al nuevo tipo.

como decidir

  • Escriba scripts especiales que se aplicarán una vez junto con la migración y lleve los datos a la forma adecuada. Esta es una forma general de resolver el problema de transferir datos a nuevas estructuras después de aplicar migraciones, pero se puede aplicar algo similar antes, en casos especiales. Esta ruta, por supuesto, no siempre está disponible, porque la edición de datos en servidores activos puede ser peligrosa e incluso fatal.
  • Otra forma complicada es editar un conjunto de cambios existente. La dificultad es que habrá que restaurar todas las bases de datos en las que ya se ha aplicado en su forma actual. Es muy posible que todo el equipo de back-end se vea obligado a resumir localmente la base de datos desde cero.
  • Y la forma más universal es transferir el problema de los datos al entorno del desarrollador, recrear la misma situación y agregar un nuevo conjunto de cambios, a uno roto, que evitará el problema.
    Cómo no pegarte un tiro en el pie con Liquibase

En general, cuanto más similar sea la composición de la base de datos a la base de datos del servidor de producción, es menos probable que los problemas con las migraciones lleguen lejos. Y, por supuesto, antes de enviar el conjunto de cambios al repositorio, debe pensar varias veces si romperá algo.

Situación 3. Liquibase comienza a usarse después de entrar en producción

Suponga que el líder del equipo le pidió a Petya que incluyera Liquibase en el proyecto, pero el proyecto ya está en producción y ya existe una estructura de base de datos.

En consecuencia, el problema es que en cualquier servidor nuevo o máquina de desarrollador, los datos de la tabla deben recrearse desde cero y el entorno ya existente debe permanecer en un estado consistente, listo para aceptar nuevos conjuntos de cambios.

como decidir

También hay varias formas:

  • La primera y más obvia es tener un script separado que debe aplicarse manualmente al inicializar un nuevo entorno.
  • El segundo, menos obvio, es tener una migración de Liquibase que esté en un contexto de Liquibase diferente y aplicarla. Puede leer más sobre Liquibase Context aquí: https://www.liquibase.org/documentation/contexts.html. En general, este es un mecanismo interesante que se puede aplicar con éxito, por ejemplo, para realizar pruebas.
  • El tercer camino consta de varios pasos. Primero, se debe crear una migración para las tablas existentes. Luego debe aplicarse en algún entorno y así se obtendrá su suma hash. El siguiente paso es inicializar las tablas vacías de Liquibase en nuestro servidor no vacío, y puede colocar manualmente un registro de un conjunto de cambios "como si se aplicara" con los cambios que ya están en la base de datos en la tabla con el historial de aplicación de conjuntos de cambios. Por lo tanto, en un servidor ya existente, el historial comenzará desde la versión 2 y todos los entornos nuevos se comportarán de manera idéntica.
    Cómo no pegarte un tiro en el pie con Liquibase

Escenario 4: las migraciones se vuelven enormes y no pueden seguir el ritmo

Al comienzo del desarrollo del servicio, por regla general, Liquibase se usa como una dependencia externa y todas las migraciones se procesan cuando se inicia la aplicación. Sin embargo, con el tiempo, es posible que te encuentres con los siguientes casos:

  • Las migraciones se vuelven enormes y tardan mucho tiempo en completarse.
  • Existe la necesidad de migrar en entornos distribuidos, por ejemplo, en varias instancias de servidores de bases de datos al mismo tiempo.
    En este caso, aplicar migraciones durante demasiado tiempo provocará un tiempo de espera cuando se inicie la aplicación. Además, la aplicación de migraciones por instancia de aplicación puede provocar que diferentes servidores no estén sincronizados.

como decidir

En tales casos, su proyecto ya es grande, quizás incluso un adulto, y Liquibase comienza a actuar como una herramienta externa separada. El hecho es que Liquibase, como biblioteca, se ensambla en un archivo jar y puede funcionar como una dependencia dentro del proyecto, así como de forma independiente.

Sin conexión, puede dejar la aplicación de migraciones a su entorno de CI/CD o a los fuertes hombros de sus administradores de sistemas/implementadores. Para hacer esto, necesita la línea de comando Liquibase https://www.liquibase.org/documentation/command_line.html. En este modo, es posible iniciar la aplicación después de que se hayan completado todas las migraciones necesarias.

conclusión

De hecho, hay muchas más trampas cuando se trabaja con migraciones de bases de datos y muchas de ellas requieren un enfoque creativo. Es importante comprender que si usa la herramienta correctamente, la mayoría de estas trampas se pueden evitar. Específicamente, tuve que enfrentar todos los problemas enumerados en diferentes formas, y algunos de ellos fueron el resultado de mis jambas. Básicamente, esto sucede, por supuesto, debido a la falta de atención, pero a veces, debido a la incapacidad criminal para usar la herramienta.

Fuente: habr.com

Añadir un comentario