¿Por qué una aplicación autohospedada ejecuta dos veces la misma migración de base de datos después de volver a implementarse?

Eva Wong es la Redactora técnica y manitas residente en ZimaSpace. Una geek de toda la vida con pasión por los homelabs y el software de código abierto, se especializa en traducir conceptos técnicos complejos en guías accesibles y prácticas. Eva cree que el autoalojamiento debe ser divertido, no intimidante. A través de sus tutoriales, empodera a la comunidad para desmitificar las configuraciones de hardware, desde construir su primer NAS hasta dominar los contenedores Docker.

Una migración de base de datos puede ejecutarse dos veces cuando más de una ruta de inicio, contenedor o programador considera que debe encargarse del mismo paso de actualización.

Las aplicaciones autoalojadas suelen iniciar migraciones desde un punto de entrada, un proceso web, un trabajador, un contenedor sidecar, una unidad de systemd o un enlace de despliegue. Después de un nuevo despliegue, un contenedor antiguo puede solaparse con uno nuevo, una política de reinicio puede volver a iniciar un migrador fallido o dos réplicas pueden llegar a la base de datos antes de que una registre la finalización. El diagnóstico debe identificar todos los posibles procesos ejecutores y demostrar si el sistema de migraciones utiliza un bloqueo persistente o un registro del historial del esquema antes de comenzar cualquier reparación de datos.

Identifica todos los procesos que pueden iniciar la migración

Busca el punto de entrada de la imagen, el comando de Compose, el comando del trabajador, la unidad de systemd, el trabajo de cron, el script de despliegue y el registro de inicio de la aplicación para localizar el comando de migración. Registra los ID de proceso y los nombres de los contenedores en los dos momentos de ejecución.

Docker Compose puede iniciar los servicios según las dependencias declaradas, pero el orden de inicio por sí solo no convierte una migración a nivel de aplicación en una operación con un único propietario. La guía oficial sobre el orden de inicio muestra por qué que una base de datos esté disponible y que una migración tenga un único responsable son condiciones independientes.

Si el mismo comando aparece tanto en el punto de entrada web como en un servicio de migración dedicado, elimina uno de los responsables. Si solo existe un comando, continúa comprobando las réplicas, los bucles de reinicio y los registros del estado de la migración.

Comprueba si hay contenedores antiguos y nuevos solapados

Enumera los contenedores en ejecución, reinicio, detenidos y huérfanos durante el despliegue. Compara los nombres del proyecto, los nombres de los servicios, los ID de los contenedores y las marcas de tiempo de creación.

La documentación de systemd indica que la política de reinicio de un servicio puede volver a iniciar un comando fallido según la configuración de la unidad. Su modelo de reinicio de servicios ayuda a explicar por qué un iniciador del host puede ejecutar de nuevo la migración después de que el intento dentro del contenedor termine con un código distinto de cero.

Elimina los procesos ejecutores huérfanos confirmados solo después de conservar sus registros. Una segunda marca de tiempo de migración poco después de la primera suele indicar un reintento, no un trabajo programado por separado.

Usa un bloqueo de base de datos antes de aplicar cambios de esquema

Determina si la aplicación adquiere un bloqueo a nivel de base de datos antes de leer el estado de la migración y aplicar los cambios. Prueba dos intentos de inicio simultáneos en un entorno desechable.

PostgreSQL proporciona bloqueos de asesoría para la coordinación definida por la aplicación, lo que permite que un ejecutor de migraciones excluya a otro incluso cuando ambos se inician casi al mismo tiempo.

El bloqueo debe abarcar toda la ventana de decisión y ejecución. Comprobar la versión actual del esquema antes de adquirir el bloqueo aún permite que dos ejecutores elijan la misma migración pendiente.

Verifica la semántica de bloqueo en MySQL o MariaDB

En aplicaciones compatibles con MySQL, comprueba si la herramienta de migración utiliza un bloqueo con nombre, una transacción o una tabla de bloqueos, y si la conexión permanece activa durante toda la migración.

MySQL documenta los bloqueos con nombre asociados a la conexión, que se liberan cuando termina la sesión propietaria y, por tanto, deben volver a adquirirse de forma segura después de un fallo o reinicio.

Una conexión fallida puede liberar el bloqueo antes de que el sistema de migraciones registre la finalización. Compara los registros de la base de datos con las marcas de tiempo de reinicio del contenedor para identificar esta secuencia.

Inspecciona la tabla del historial del sistema de migraciones

Enumera los identificadores de las migraciones, el orden de ejecución, los indicadores de éxito, las sumas de comprobación y las marcas de tiempo. Compara las dos ejecuciones registradas con los datos realmente confirmados en la base de datos.

Flyway utiliza una tabla del historial del esquema para realizar un seguimiento de las migraciones aplicadas y sus estados.

Si la primera ejecución modificó el esquema, pero falló antes de registrar el éxito, la segunda puede volver a intentar una migración que no se diseñó para ser idempotente. Repara el historial solo después de comparar el esquema real con el resultado esperado de la migración.

Comprueba la identidad del registro de cambios y las modificaciones de las sumas de comprobación

Compara los nombres de archivo, los ID, los autores, las rutas y las sumas de comprobación de las migraciones antes y después de actualizar la imagen. Determina si la imagen contiene entradas de registro de cambios duplicadas o renombradas.

Liquibase registra los cambios ejecutados en la tabla DATABASECHANGELOG, donde la identidad del cambio depende de su ID, autor y ruta de archivo.

Mover un archivo de registro de cambios o volver a generar identificadores puede hacer que un trabajo antiguo parezca nuevo, aunque el código SQL sea similar. Restablece una identidad de migración estable en lugar de eliminar manualmente amplios rangos del historial.

Vuelve a desplegar con un único responsable de migraciones y verifica la idempotencia

Elige un único responsable de las migraciones, añade un bloqueo persistente, haz que los servicios web y de trabajadores esperen a que la migración finalice correctamente y vuelve a desplegar en una copia de prueba de la base de datos.

El artículo de ZimaSpace sobre los límites de programación de contenedores aporta la regla relacionada: una tarea de mantenimiento debe tener un único responsable de ejecución comprobado.

El problema se resuelve cuando los inicios simultáneos o repetidos producen una migración aplicada, un único registro persistente en el historial y ninguna segunda modificación del esquema después de un reinicio.

Preguntas frecuentes

¿Ejecutar una migración dos veces siempre daña la base de datos?

No. Las migraciones idempotentes pueden detectar de forma segura los objetos existentes, pero las transformaciones de datos no idempotentes, la creación de índices o los cambios de columnas pueden fallar o duplicar datos.

¿Debe permitirse que cada réplica web ejecute migraciones?

Solo cuando el sistema proporciona una coordinación fiable a nivel de base de datos. Un único responsable de migración que se ejecute una sola vez es más fácil de auditar en un despliegue de servidor doméstico.

¿Puedo marcar manualmente la migración como completada?

Solo después de demostrar que el esquema y los datos activos coinciden con el resultado esperado de la migración. Editar primero el historial puede ocultar un cambio aplicado parcialmente.

Soporte y Consejos

Más para leer

Get More Builds Like This

Stay in the Loop

Get updates from Zima - new products, exclusive deals, and real builds from the community.

Stay in the Loop preferences

We respect your inbox. Unsubscribe anytime.