Experimente las migraciones de la base de datos 1440



Imagine Oracle DBA. Ya tiene más de treinta años, tiene un poco de sobrepeso, usa un chaleco, tiene un token de acceso secreto a todas las bases que cuelgan de su cuello, y en un resumen de media página de las certificaciones que pasó. Sabado Gran día de lanzamiento. Climax Tiempo para rodar cambios a la base de datos. Escribe sqlplus, presiona ENTER y, en algún lugar de la pantalla negra en el vacío, se precipitan kilómetros de comandos SQL. Como en la guerra de las galaxias. Cinco minutos después, todo está listo. Una hora después, el lanzamiento está completo. El trabajo está hecho, el día fue un éxito. Ahora puedes tomar un par de cervezas.

Otra cosa es el lunes. Resulta que algunos comandos no se ejecutaron debido a errores, que, sin embargo, no detuvieron el script en su búsqueda desenfrenada del vacío negro. La ya difícil tarea de descubrir lo que está roto se complica por la presión de los líderes. En general, el lunes no funcionó.

Por supuesto, esta es una historia de ficción. Esto nunca le ha pasado a nadie. Al menos, no habría sucedido si el trabajo para cambiar el esquema de la base de datos se hubiera organizado a través de migraciones.

¿Qué es una herramienta de migración de base de datos?


La idea de administrar los cambios en el esquema de la base de datos mediante migraciones es extremadamente simple:

  1. Cada cambio se emite como un archivo de migración separado.
  2. El archivo de migración incluye cambios directos e inversos.
  3. La aplicación de migraciones a la base de datos se lleva a cabo mediante una utilidad especial.

El ejemplo de migración más simple:
-- 20180618152059: create sequence for some_table CREATE SEQUENCE some_table_seq; --//@UNDO DROP SEQUENCE some_table_seq; 

Este enfoque ofrece muchas ventajas sobre la organización de cambios en un archivo SQL común. La mera ausencia de conflictos de fusión vale la pena.

Es aún más sorprendente que el enfoque en sí haya ganado popularidad relativamente recientemente. Parece que el marco de Ruby on Rails, en el que se construyó originalmente la herramienta de migración, fue la principal fama del enfoque, fue a finales de 2005. Un poco antes, Martin Fowler, 2003 escribió sobre el enfoque. Probablemente, el punto es que el desarrollo comenzó a adaptar activamente el uso del sistema de control de versiones solo a principios de este siglo. En el año 2000, el primer párrafo de la prueba de Joel Spolsky fue "¿Utiliza el control de fuente?" - esto sugiere que no todos usaban sistemas de control de versiones en ese momento. Pero estábamos distraídos.

Ocho años con migraciones MyBatis


En Wrike comenzamos a utilizar el enfoque de cambio de base de datos a través de migraciones en 2010, el 29 de marzo, a las doce y media. Desde entonces, hemos implementado 1.440 migraciones, que contienen 6.436 cambios directos y 5.015 inversos. En general, hemos adquirido algo de experiencia usando la herramienta MyBatis Migrations junto con PostgreSQL .

En resumen, nunca nos hemos arrepentido. Si sucede que no está utilizando Migraciones o algo similar, es hora de comenzar. Sí, Pentium 4 también está desactualizado.

Pero es aburrido hablar de los méritos de cualquier cosa, vayamos directamente a las dificultades.

Detalles de PostgreSQL


No hay dificultades para escribir migraciones para Postgres, tal vez, excepto por dos:
  • No puedes crear índices,
  • No puede agregar columnas NOT NULL.

No, en realidad es posible, solo que no de una manera completamente obvia. Al crear un índice, siempre debe especificar CREAR ÍNDICE CONCURRENTEMENTE , de lo contrario, interrumpirá la producción, porque Postgres bloqueará la tabla durante la creación del índice, y esto puede ser bastante tiempo. Por supuesto, los desarrolladores lo olvidan una vez, siempre hay que tener en cuenta esta sutileza. Aquí uno podría escribir una prueba. Pero esto es solo un pequeño inconveniente.

Crear columnas NOT NULL es más complicado, aquí debe hacer un cambio en cuatro pasos:
  1. Crea una columna NULL (en Postgres es gratis).
  2. Establezca la columna DEFAULT en un valor.
  3. En un bucle, actualice incrementalmente los valores NULL en DEFAULT.
  4. Establezca SET NOT NULL.

La mayor captura aquí está en el tercer párrafo. Los valores NULL deben actualizarse en porciones, porque UPDATE some_table SET some_column='' WHERE some_column IS NULL ; bloqueará la tabla, como es el caso con el índice, con las mismas consecuencias. Y las migraciones solo pueden ejecutar comandos SQL, por lo que dichos scripts deben implementarse manualmente. Placer por debajo de la media. Ahora, si se pudiera escribir un ciclo en Migraciones, no habría problema. Quizás esto se implementa a través de ganchos .

Crear un índice UNIQUE y cambiar una PRIMARY KEY también requiere cierta habilidad, pero estas operaciones son relativamente raras para detenerse.

Especificaciones del clúster


La herramienta de gestión de migración de base de datos es buena siempre que tenga una base de datos. Mucho más divertido si tienes varias bases. Especialmente si tiene varios tipos de bases de datos, cada una de las cuales tiene varias instancias.

Como resultado, después de git pull desarrollador debe transferir los cambios a la primera instancia de la primera base de datos, luego a la segunda instancia, luego a la primera instancia de la segunda base de datos y así sucesivamente, tal principio. Aquí es correcto escribir una utilidad para administrar la utilidad de administración de migración de base de datos. Automatización total.

Malabares de roles


No es ningún secreto que los roles como entidades no viven en el nivel de una base de datos separada, sino en el nivel de todo el servidor de bases de datos, al menos en Postgres. En este caso, es posible que deba especificar REVOKE INSERT ON some_table FROM some_role ; Todavía es posible esperar que los roles estén preconfigurados en la producción, pero para el desarrollo o la puesta en escena esto ya es difícil. Al mismo tiempo, en desarrollo, por supuesto, todas las bases de datos existen en el mismo servidor local, por lo que simplemente no puede escribir CREATE ROLE en la migración, y IF NOT EXISTS no IF NOT EXISTS compatible. Todo se resuelve simplemente:
 DO $$ BEGIN IF NOT EXISTS (SELECT * FROM pg_roles WHERE rolname = 'some_role') THEN CREATE ROLE "some_role" NOLOGIN; END IF; END; $$; 

Míralo! Los atrapo y los lanzo, los atrapo y los lanzo, es muy simple.

Un poco de realidad de desarrollo


Los desarrolladores cometen errores, e incluso en las migraciones de SQL, esto sucede. Por lo general, se pueden notar errores en la revisión, pero también puede ser inusual. Si hablamos de cambios directos, entonces las jambas aún no llegan a la producción; hay demasiadas etapas de verificación. Pero con los cambios inversos, pueden surgir incidentes. Para evitar errores en la migración UNDO, al probar la migración, debe realizar no solo ./migrate up , sino ./migrate up , luego ./migrate down , luego nuevamente ./migrate up . Esto no es nada complicado, solo necesita asegurarse de que cuarenta desarrolladores siempre hagan esto. En el buen sentido, la utilidad podría realizar dicho combo para el entorno del desarrollador automáticamente.

Entornos de prueba


Si el entorno de prueba es de corta duración: supongamos que crea un contenedor, inicializa la base de datos y ejecuta pruebas de integración, no debería haber ningún problema. Llamamos ./migrate bootstrap , luego ./migrate up , y ya ./migrate up . Es justo cuando el número de migraciones supera los mil, este proceso puede retrasarse. Es una pena cuando la base de datos se inicializa más tiempo que las pruebas ejecutadas. Tenemos que esquivar

Con entornos de larga duración, aún es más difícil. Control de calidad, ya sabes, no les gusta ver una base de datos impecablemente limpia cuando vienen a trabajar. No sé por qué es así, pero el hecho es un hecho. Por lo tanto, el estado de las bases utilizadas en las pruebas manuales debe mantenerse en integridad. Y esto no siempre es fácil.

La sutileza es que si la migración se aplica a la base de datos, el identificador de migración se escribe en ella. Y si el código de migración se modificó posteriormente, la base de datos no se verá afectada. Si los cambios no son críticos, el código puede llegar con éxito a producción. Rssynchron. Por supuesto, esto es una desgracia. El primer principio de trabajar con migraciones es nunca cambiar las migraciones escritas, sino crear siempre nuevas. Pero a veces tengo ganas de hurgar: cambiaré un poco aquí, nada se romperá, porque la verdad es. Por supuesto! ¡Adelante!

Si las migraciones se firmaran después de la revisión, sería posible prohibir la aplicación de borradores para la puesta en escena. Y sería posible guardar no solo el identificador de migración en el changelog de changelog , sino también la checksum , también útil.

Regresa como estaba


Un giro particularmente insidioso ocurre cuando se cancela una tarea: lo hicieron, lo hicieron y cambiaron de opinión. Es una situación normal. Una vez que ya no se necesita el código, se debe eliminar la rama. Y hubo migración ... y ella ya está en escena ... ah, ... oops. Una buena razón para verificar si puede restaurar la copia de seguridad del repositorio. Aunque recordando que quizás fue más fácil.

Al mismo tiempo, la migración es texto. Y sería posible guardar este texto allí, en el changelog de changelog . Luego, si la migración del código se ha ido, no importa por qué razones, siempre podría revertirse. E incluso automáticamente.

Deshacer de nuevo


La sección UNDO es definitivamente necesaria. ¿Pero por qué escribirlo? Por supuesto, hay casos pegadizos, pero la mayoría de los cambios son CREATE TABLE o ADD COLUMN o CREATE INDEX . Para ellos, la utilidad podría generar operaciones inversas automáticamente, directamente usando código SQL. Por supuesto, hay una especificidad. CREATE TABLE ${name} : este es un equipo tan especial, de repente no estándar. Sí, y para generar DROP TABLE ${name} , debe poder analizar la expresión hasta la tercera palabra. Aunque, en general, esta es una tarea técnica totalmente factible. Podría estar fuera de la caja.

Conclusión


Por supuesto, me parece culpable. MyBatis Migrations fue concebido como una utilidad simple y universal, mínimamente vinculada a los detalles de las bases de datos. Y ella es más que justificarse a sí misma. Pero parece que algunas pequeñas mejoras lo harían mucho mejor, especialmente cuando se usa a largas distancias.
-
Dmitry Mamonov / Wrike

Source: https://habr.com/ru/post/es414441/


All Articles