Una migrazione del database può essere eseguita due volte quando più percorsi di avvio, container o pianificatori ritengono di essere responsabili dello stesso passaggio di aggiornamento.
Le applicazioni self-hosted spesso avviano le migrazioni da un entrypoint, un processo web, un worker, un sidecar, un’unità systemd o un hook di deployment. Dopo un nuovo deployment, un vecchio container potrebbe sovrapporsi a uno nuovo, una policy di riavvio potrebbe rilanciare un migratore non riuscito oppure due repliche potrebbero raggiungere il database prima che una delle due registri il completamento. La diagnosi deve identificare ogni possibile processo esecutore e dimostrare se il framework di migrazione utilizza un lock persistente o un record nella cronologia dello schema prima di iniziare qualsiasi riparazione dei dati.
Identifica ogni processo che può avviare la migrazione
Cerca il comando di migrazione nell’entrypoint dell’immagine, nel comando Compose, nel comando del worker, nell’unità systemd, nel job cron, nello script di deployment e nei log di avvio dell’applicazione. Registra gli ID dei processi e i nomi dei container nei due momenti di esecuzione.
Docker Compose può avviare i servizi in base alle dipendenze dichiarate, ma l’ordine di avvio da solo non rende una migrazione dell’applicazione di proprietà esclusiva di un unico processo. Le indicazioni ufficiali sull’ordine di avvio mostrano perché la disponibilità di un database e l’esecuzione esclusiva di una migrazione siano condizioni separate.
Se lo stesso comando compare sia nell’entrypoint web sia in un servizio di migrazione dedicato, rimuovi uno dei due responsabili. Se esiste un solo comando, continua controllando le repliche, i cicli di riavvio e i record dello stato della migrazione.
Controlla la sovrapposizione tra container vecchi e nuovi
Elenca i container in esecuzione, in riavvio, terminati e orfani durante il deployment. Confronta i nomi dei progetti, i nomi dei servizi, gli ID dei container e i timestamp di creazione.
La documentazione di systemd indica che la policy di riavvio di un servizio può rilanciare un comando non riuscito in base alle impostazioni dell’unità. Il suo modello di riavvio dei servizi aiuta a spiegare perché un launcher dell’host possa eseguire nuovamente la migrazione dopo che il tentativo containerizzato termina con un codice diverso da zero.
Rimuovi i processi esecutori orfani comprovati solo dopo averne conservato i log. Un secondo timestamp di migrazione poco dopo il primo spesso indica un nuovo tentativo, non un job pianificato separatamente.
Usa un lock del database prima di applicare le modifiche allo schema
Determina se l’applicazione acquisisce un lock a livello di database prima di leggere lo stato della migrazione e applicare le modifiche. Esegui un test con due tentativi di avvio simultanei in un ambiente usa e getta.
PostgreSQL fornisce advisory lock per il coordinamento definito dall’applicazione, consentendo a un processo di migrazione di escluderne un altro anche quando entrambi vengono avviati quasi contemporaneamente.
Il lock deve coprire l’intera finestra di decisione ed esecuzione. Controllare la versione corrente dello schema prima di acquisire il lock consente comunque a due processi di scegliere la stessa migrazione in sospeso.
Verifica la semantica dei lock su MySQL o MariaDB
Per le applicazioni compatibili con MySQL, verifica se lo strumento di migrazione utilizza un lock denominato, una transazione o una tabella dei lock e se la connessione rimane attiva per l’intera durata della migrazione.
MySQL documenta i lock denominati associati alla connessione, che vengono rilasciati quando termina la sessione proprietaria e devono quindi essere acquisiti nuovamente in modo sicuro dopo un arresto anomalo o un riavvio.
Una connessione non riuscita può rilasciare il lock prima che il framework di migrazione registri il completamento. Confronta i log del database con i timestamp di riavvio dei container per identificare questa sequenza.
Esamina la tabella della cronologia del framework di migrazione
Elenca gli identificatori delle migrazioni, l’ordine di esecuzione, gli indicatori di successo, i checksum e i timestamp. Confronta le due esecuzioni registrate nei log con i record effettivamente salvati nel database.
Flyway utilizza una tabella della cronologia dello schema per tenere traccia delle migrazioni applicate e dei relativi stati.
Se la prima esecuzione ha modificato lo schema ma non è riuscita prima di registrare il successo, la seconda potrebbe riprovare una migrazione che non è stata progettata per essere idempotente. Ripara la cronologia solo dopo aver confrontato lo schema reale con il risultato previsto della migrazione.
Controlla l’identità del changelog e le modifiche ai checksum
Confronta nomi dei file di migrazione, ID, autori, percorsi e checksum prima e dopo l’aggiornamento dell’immagine. Determina se l’immagine contiene voci di changelog duplicate o rinominate.
Liquibase registra le modifiche eseguite nella tabella DATABASECHANGELOG, dove l’identità della modifica dipende dal relativo ID, dall’autore e dal percorso del file.
Spostare un file di changelog o rigenerare gli identificatori può far apparire come nuova un’attività già eseguita, anche quando l’SQL è simile. Ripristina un’identità stabile delle migrazioni invece di eliminare manualmente ampi intervalli della cronologia.
Esegui nuovamente il deployment con un solo responsabile della migrazione e verifica l’idempotenza
Scegli un unico responsabile della migrazione, aggiungi un lock persistente, fai attendere ai servizi web e worker il completamento corretto e ripeti il deployment su una copia di test del database.
L’articolo di ZimaSpace sui confini della pianificazione dei container fornisce la regola correlata: ogni attività di manutenzione dovrebbe avere un unico responsabile di esecuzione comprovato.
Il problema è risolto quando gli avvii simultanei o ripetuti producono una sola migrazione applicata, un unico record persistente nella cronologia e nessuna seconda modifica dello schema dopo il riavvio.
Domande frequenti
L’esecuzione di una migrazione due volte danneggia sempre il database?
No. Le migrazioni idempotenti possono rilevare in sicurezza gli oggetti già esistenti, ma le trasformazioni dei dati non idempotenti, la creazione di indici o le modifiche alle colonne possono non riuscire o duplicare i dati.
Ogni replica web dovrebbe poter eseguire le migrazioni?
Solo quando il framework fornisce un coordinamento affidabile a livello di database. In un deployment su home server, un responsabile dedicato per una migrazione eseguita una sola volta è più facile da controllare.
Posso contrassegnare manualmente la migrazione come completata?
Solo dopo aver dimostrato che lo schema e i dati attivi corrispondono al risultato previsto della migrazione. Modificare prima la cronologia può nascondere una modifica applicata solo parzialmente.
Supporto e consigli
Altro da leggere

Perché il ripristino di un volume Docker ricrea il contenuto dei file, ma elimina gli attributi estesi?
Una diagnosi del ripristino del volume che copre l’inventario degli xattr, le opzioni di tar e Rsync, gli spazi dei nomi, il supporto della...

Perché un container in esecuzione mantiene il vecchio limite di memoria dopo la modifica del file Compose?
Una diagnosi dei limiti di memoria che copre i cgroup attivi, il riavvio rispetto alla ricreazione, i campi di Compose, i limiti rigidi e...

Perché il riavvio di un proxy inverso invalida ogni sessione per una determinata app self-hosted?
Una diagnosi della perdita di sessione che copra l’ambito dei riavvii, la gestione dei cookie, la rotazione dei segreti, le sessioni basate sulla cache,...

