Tenemos una aplicación que tiene una combinación de migraciones de bases de datos rápidas (<1 segundo) y lentas (> 30 segundos). En este momento, estamos ejecutando migraciones de bases de datos como parte de CI, pero nuestra herramienta de CI tiene que conocer todas las cadenas de conexión de la base de datos para nuestra aplicación (en múltiples entornos), lo cual no es ideal. Queremos cambiar este proceso para que la aplicación ejecute sus propias migraciones de bases de datos cuando se inicie.
Aquí está la situación:
Tenemos varias instancias de esta aplicación, alrededor de 5 en producción. Vamos a llamarlos node1, ..., node5
. Cada aplicación se conecta a una única instancia de SQL Server, y no estamos usando implementaciones continuas (todas las aplicaciones se implementan simultáneamente hasta donde yo sé)
Problema: digamos que tenemos una migración de larga duración. En este caso, node1
comienza, luego comienza a ejecutar la migración. Ahora, node4
comienza, y la migración de larga duración aún no ha terminado, por lo que node4
también comienza a ejecutar la migración -> posible corrupción de datos? ¿Cómo evitarías este problema o es el problema lo suficientemente importante como para preocuparte?
Estaba pensando en resolver este problema con un bloqueo distribuido (usando etcd
o algo por el estilo). Básicamente, todas las aplicaciones intentan adquirir el bloqueo, solo una de ellas lo consigue y ejecuta las migraciones, luego se desbloquea. Cuando el resto de las aplicaciones se inician y entran en la sección crítica, todas las migraciones ya se han ejecutado, por lo que el script de migración acaba de salir.
Sin embargo, mi instinto dice "esto es excesivo, debe haber una solución más simple", así que pensé en preguntar aquí para ver si alguien más tiene mejores ideas.
Respuestas:
Como mencionó el servidor SQL: de acuerdo con esta publicación anterior de DBA.SE , los cambios de esquema pueden (y deben) ponerse en transacciones. Esto le brinda la capacidad de diseñar sus migraciones como cualquier otra forma de escritura simultánea en su base de datos: inicia una transacción y, cuando falla, la revierte. Eso evita al menos algunos de los peores escenarios de corrupción de la base de datos (aunque las transacciones por sí solas no evitarán la pérdida de datos cuando hay pasos de migración destructivos como eliminar una columna o tabla).
Hasta ahora, estoy seguro de que también necesitará alguna
migrations
tabla donde se registren las migraciones ya aplicadas, por lo que un proceso de solicitud puede verificar si una migración específica ya se aplicó o no. Luego utilice "SELECCIONAR PARA ACTUALIZAR" para implementar sus migraciones de esta manera (pseudocódigo):SELECT FROM Migrations FOR UPDATE WHERE MigrationLabel='MyMigration42'
INSERT 'MyMigration42' INTO Migrations(MigrationLabel)
Eso construye el mecanismo de bloqueo directamente en la prueba "fue la migración ya aplicada" .
Tenga en cuenta que este diseño permitirá, en teoría, dejar que sus pasos de migración no sean conscientes de qué aplicación realmente lo aplica; puede ser posible que el paso 1 se aplique por la aplicación1, el paso 2 por la aplicación2, el paso 3 por la aplicación 3, el paso 4 por la aplicación1 de nuevo, y así sucesivamente. Sin embargo, también es una buena idea no aplicar migraciones siempre que otras instancias de aplicaciones estén en uso. La implementación paralela, como se mencionó en su pregunta, ya puede tener en cuenta esta restricción.
fuente
Quizás pueda encontrar una biblioteca que admita la migración de bases de datos con múltiples nodos.
Conozco dos bibliotecas en el mundo de Java, ambas admiten lo que necesitas:
Probablemente hay otras herramientas para Java y otros lenguajes también.
Si no puede (o no quiere) usar dicha herramienta, una tabla se puede usar como un bloqueo o incluso como un registro de migración, consulte la respuesta de Doc Browns para ver un ejemplo.
fuente