Torna al blog Migration Architecture

Preparazione alla migrazione del database per applicazioni self-hosted: è possibile eseguire un rollback in sicurezza?

Usa questa checklist per il rollback delle migrazioni del database prima di aggiornare un'applicazione self-hosted. Distingui il rollback del codice dal ripristino del database, valuta compatibilità e lock, verifica i backup e definisci le decisioni di recovery prima del deployment.

Operatore che esamina una checklist di migrazione del database prima di distribuire un aggiornamento di un'applicazione self-hosted

Perché il rollback dell'applicazione e il rollback del database sono diversi

Sostituire un nuovo container dell'applicazione con l'immagine precedente può essere rapido. Questo è un rollback del codice. Non riporta automaticamente il database alla struttura e ai contenuti previsti dalla release precedente.

Una migrazione del database può aggiungere tabelle, rinominare colonne, trasformare valori memorizzati, creare indici, rendere più restrittivi i vincoli, eliminare dati o avviare attività che proseguono dopo il deployment. Una volta che una migrazione è stata confermata, un ROLLBACK a livello di transazione non è più disponibile per quel lavoro già confermato. Un recovery sicuro può richiedere invece il ripristino dei dati, la riparazione dei dati o il completamento di una correzione in avanti.

Considera la release dell'applicazione e la modifica del database come due oggetti di deployment collegati ma distinti. L'approvazione dovrebbe dipendere dal fatto che la vecchia applicazione possa operare in sicurezza sullo stato del database successivo alla migrazione, non semplicemente dalla disponibilità di un'immagine container precedente.

  • Domanda sul rollback del codice: la release precedente dell'applicazione può essere avviata e rimessa in servizio?
  • Domanda sul recovery del database: lo schema precedente e i dati aziendali necessari possono essere ripristinati o ricostruiti senza perdite inaccettabili?
  • Domanda sulla compatibilità: il vecchio codice, il nuovo codice e il database modificato possono coesistere durante la transizione pianificata?
  • Regola decisionale: non descrivere una release come reversibile finché non siano stati esaminati sia il percorso del codice sia quello del database.
Perché il rollback dell'applicazione e il rollback del database sono diversi

Mappa ogni modifica che interessa il database prima dell'approvazione

Crea una mappa delle modifiche a partire dalle note di rilascio dell'applicazione, dai file di migrazione, dagli script di deployment e dai comandi del database. Non fare affidamento su un'etichetta come “migrazione automatica”; individua ciò che quell'automazione modifica effettivamente e quando viene eseguita.

Classifica ogni operazione in base al suo effetto su schema, dati, disponibilità e reversibilità. L'obiettivo non è prevedere ogni dettaglio di implementazione. È mettere in evidenza le operazioni che modificano la strategia di recovery o richiedono controlli operativi.

Per i deployment containerizzati, individua anche dove risiedono i dati persistenti del database. I volumi Docker Compose sono archivi persistenti gestiti dal motore dei container, quindi il volume del database, ogni servizio di database esterno e la fonte del backup devono essere inequivocabili. Ricreare un workload dell'applicazione non dimostra che il database sia stato preservato.

  • Schema: tabelle, colonne, tipi, indici, chiavi esterne, valori predefiniti e vincoli nuovi o rimossi.
  • Trasformazione dei dati: backfill, conversioni di valori, deduplicazione, modifiche alla crittografia, riscrittura di identificatori ed eliminazioni.
  • Prestazioni e lock: riscritture di tabelle, creazione di indici, query di lunga durata e operazioni che possono attendere altre transazioni.
  • Lavoro in background: job in coda, worker, attività pianificate o attività di avvio dell'applicazione che continuano a modificare i dati dopo la migrazione dello schema.
  • Dipendenze: strumenti di reporting, integrazioni, esportazioni, viste, consumer API e script personalizzati che possono dipendere da campi o valori esistenti.
  • Metadati della migrazione: identificatore della migrazione, ordine di esecuzione, strumento o framework e presenza di un'operazione inversa documentata per ogni passaggio.
Mappa ogni modifica che interessa il database prima dell'approvazione

Verifica esplicitamente la compatibilità all'indietro e in avanti

Un deployment sicuro dipende spesso da una finestra di compatibilità: un periodo durante il quale sia la release precedente sia quella nuova dell'applicazione possono utilizzare lo stesso stato del database. Senza questa finestra, un rollback del codice dopo la migrazione può fallire anche se la vecchia immagine si avvia normalmente.

Esamina separatamente il comportamento di lettura e scrittura. Il vecchio codice può tollerare una nuova colonna nullable, ma fallire se una colonna che scrive è stata rimossa, rinominata, resa obbligatoria o ha cambiato significato. Il nuovo codice può avviarsi prima del completamento di un backfill solo se è in grado di gestire correttamente sia la vecchia sia la nuova rappresentazione.

Rendi la compatibilità una decisione registrata, anziché un presupposto. Se il vecchio codice non è compatibile con il database migrato, il piano di recovery deve dare priorità a un ripristino, a una riparazione dei dati o a una correzione in avanti rispetto a un semplice rollback del codice.

  • Vecchio codice con schema esteso: può ignorare tabelle, colonne e indici aggiuntivi?
  • Nuovo codice prima del completamento della migrazione dei dati: può leggere sia il vecchio sia il nuovo formato dei valori?
  • Vecchio codice dopo un backfill: sovrascriverà dati trasformati o creerà record in un formato obsoleto?
  • Tempistica dei vincoli: un nuovo vincolo NOT NULL, di unicità o di chiave esterna rifiuterà le scritture effettuate da una release precedente?
  • Consumer esterni: le integrazioni dipendono da un nome di colonna, un formato di output, un identificatore o un comportamento API interessato dalla modifica?
  • Operatività con versioni miste: se più worker dell'applicazione vengono riavviati gradualmente, è supportato il funzionamento simultaneo di vecchia e nuova versione?

Segnala le operazioni distruttive e le modifiche sensibili ai lock

Alcune modifiche meritano un piano di recovery esplicito perché rimuovono informazioni, rendono non valide le vecchie assunzioni o incidono sulla disponibilità. L'eliminazione di una colonna PostgreSQL può rimuovere anche indici e vincoli della tabella che coinvolgono quella colonna; le dipendenze esterne alla tabella, come chiavi esterne o viste, possono richiedere ulteriori azioni. Una modifica distruttiva non dovrebbe mai essere approvata solo perché compare nelle fasi finali di una sequenza di migrazione.

Gli effetti dei lock sono altrettanto importanti. In PostgreSQL, i requisiti di lock di ALTER TABLE differiscono a seconda del sottocomando e ACCESS EXCLUSIVE è l'impostazione predefinita, salvo diversa indicazione nella documentazione. Esamina le istruzioni esatte anziché trattare ogni operazione ALTER TABLE come equivalente.

Anche la creazione degli indici richiede una scelta di deployment. Una normale creazione di indice PostgreSQL blocca le scritture durante l'esecuzione. CREATE INDEX CONCURRENTLY evita di bloccare INSERT, UPDATE e DELETE concorrenti, ma non può essere eseguito all'interno di un blocco di transazione, esegue scansioni aggiuntive della tabella e attende il completamento delle transazioni pertinenti. Ciò modifica sia le tempistiche sia la gestione degli errori.

Le forme di ALTER TABLE che riscrivono la tabella e TRUNCATE richiedono una revisione più approfondita in presenza di accesso concorrente. PostgreSQL documenta avvertenze relative a MVCC per queste operazioni, inclusi casi in cui snapshot concorrenti possono vedere una vista vuota o incoerente dopo il commit.

  • Distruttive: DROP COLUMN, DROP TABLE, TRUNCATE, backfill con eliminazioni, conversione irreversibile dei valori e sostituzione di identificatori.
  • Che interrompono la compatibilità: rinomina o rimozione di un campo usato dal vecchio codice, rafforzamento di un vincolo e modifica del significato dei valori memorizzati.
  • Sensibili alla disponibilità: riscritture di tabelle, operazioni ALTER TABLE con lock pesanti e normali creazioni di indici su tabelle soggette a scritture attive.
  • Passaggi non in una singola transazione: creazione concorrente di indici e lavoro in background che avvengono al di fuori di un unico confine transazionale.
  • Risposta richiesta: indica il metodo di recovery esatto per ogni operazione segnalata prima del deployment.

Preferisci espandi, migra, riduci quando l'applicazione lo supporta

Per modifiche rilevanti, utilizza un modello espandi–migra–riduci quando l'applicazione e le indicazioni del suo fornitore lo supportano. Questo separa il lavoro di compatibilità dalla pulizia distruttiva, creando spazio per convalidare ed eseguire il rollback del codice prima di introdurre modifiche irreversibili.

Espandere significa aggiungere nuove strutture senza rimuovere quelle vecchie: per esempio, un nuovo campo nullable, una tabella o un indice. Migrare significa eseguire il backfill dei dati e insegnare alla nuova versione dell'applicazione a leggere e scrivere la rappresentazione compatibile. Ridurre significa rimuovere strutture obsolete solo dopo la fine della finestra di compatibilità e il completamento della convalida.

Non imporre questo modello a un'applicazione il cui percorso di migrazione fornito non supporta versioni a fasi. In tal caso, documenta la sequenza richiesta dal fornitore, testala fedelmente e scegli un piano di manutenzione e recovery adeguato. Il principio utile è la separazione del rischio, non una riscrittura artificiale delle migrazioni di terze parti.

  • Espandi: aggiungi la nuova struttura e conferma che il vecchio codice continui a funzionare.
  • Distribuisci codice compatibile: assicurati che il nuovo codice gestisca entrambe le rappresentazioni quando necessario.
  • Migra: esegui i backfill in batch osservabili dove l'applicazione supporta questo approccio.
  • Convalida: confronta conteggi, record richiesti, autorizzazioni, integrazioni e flussi di lavoro chiave.
  • Riduci: rimuovi campi o formati legacy solo dopo che la finestra di rollback è stata chiusa intenzionalmente.
  • Registra il punto di non ritorno: indica esattamente quando il semplice rollback del codice non è più sicuro.

Raccogli evidenze di deployment, non solo lo stato di un backup

Un backup è utile solo quando se ne comprendono metodo, copertura, posizione e processo di ripristino. PostgreSQL distingue dump SQL, backup del file system e archiviazione continua; ciascuno presenta punti di forza e limiti diversi. Registra quale metodo protegge questa modifica anziché usare l'affermazione generica “backup completato”.

Un dump logico PostgreSQL è uno snapshot internamente coerente del momento in cui pg_dump inizia, ma le operazioni che richiedono un lock esclusivo, incluse la maggior parte delle forme di ALTER TABLE, sono eccezioni al suo comportamento altrimenti non bloccante. Conferma che tempistica e metodo del backup siano compatibili con i requisiti di lock e recovery della migrazione.

Il ripristino point-in-time non è una promessa generica. Per PostgreSQL, richiede un backup fisico precedente adeguato e write-ahead log archiviati che coprano l'istante di destinazione. Se questi prerequisiti non sono presenti, non elencare il ripristino point-in-time come opzione disponibile.

Airbip fornisce backup configurabili giornalieri, settimanali e mensili per i deployment delle applicazioni. I team devono comunque verificare l'ambito, la configurazione della conservazione, la copertura del database e la procedura di ripristino applicabili alla propria istanza prima di fare affidamento su tali backup per una decisione di migrazione.

  • Identità del backup: metodo, ora di completamento, ambito, posizione di archiviazione e persona che lo ha verificato.
  • Affidabilità del ripristino: un recente test di ripristino, passaggi stimati, credenziali necessarie, ambiente di destinazione e limitazioni note.
  • Evidenze della migrazione: versione esatta della release o riferimento dell'immagine, identificatori delle migrazioni, orari di inizio e fine, log ed errori.
  • Controlli di riferimento: registra conteggi importanti, record rappresentativi, flussi di lavoro critici e stato delle integrazioni prima della modifica.
  • Controlli post-modifica: registra gli stessi controlli dopo la migrazione e definisci differenze accettabili.
  • Soglia di recovery: decidi il massimo downtime e l'esposizione alla perdita di dati accettabili prima di iniziare.

Decidi se è necessaria una finestra di manutenzione o una restrizione temporanea delle scritture

Una finestra di manutenzione è giustificata quando la migrazione può bloccare le scritture, creare stati misti incompatibili, richiedere un tempo incerto o richiedere un ripristino che non possa unire in sicurezza le modifiche successive degli utenti. Una restrizione temporanea delle scritture può essere sufficiente quando le letture rimangono sicure ma le scritture entrerebbero in conflitto con un backfill, una modifica dello schema o un potenziale rollback.

Basa la decisione sulle operazioni effettive e sull'impatto sul business. Per esempio, una normale creazione di indice PostgreSQL blocca le scritture, mentre una creazione di indice concorrente evita quello specifico blocco delle scritture ma presenta caratteristiche operative di maggiore durata e non può condividere un normale confine transazionale. Nessuna delle due scelte è automaticamente più sicura senza considerare workload, tempistiche e recovery.

Definisci cosa vedranno gli utenti e cosa faranno gli operatori. Una restrizione delle scritture può significare sospendere i worker in background, disabilitare le importazioni pianificate, mettere un'applicazione in una modalità di manutenzione supportata dal fornitore o rifiutare temporaneamente le richieste di scrittura. Assicurati che integrazioni e amministratori ricevano le stesse istruzioni; in caso contrario, potrebbero creare dati che complicano il recovery.

  • Usa una finestra di manutenzione completa quando le modifiche dello schema o il ripristino rendono non sicure le scritture concorrenti.
  • Usa una restrizione mirata delle scritture quando l'accesso in lettura può continuare in sicurezza e l'applicazione supporta quella modalità.
  • Sospendi o considera worker in background, importazioni, webhook e job pianificati.
  • Stabilisci un orario di inizio, una durata prevista, un punto decisionale per l'estensione e un canale di comunicazione con gli utenti.
  • Conferma come il lavoro in coda verrà ripreso, deduplicato o riconciliato dopo il deployment o il recovery.
  • Interrompi se lock, durata o tassi di errore superano la soglia approvata.

Scegli il percorso di recovery prima del deployment

Il recovery è un albero decisionale, non un singolo pulsante di rollback. Pre-approva le condizioni alle quali il team eseguirà il rollback del codice dell'applicazione, ripristinerà i dati, riparerà un insieme limitato di record o proseguirà con una correzione in avanti. Indica chi può autorizzare ciascuna azione, in particolare un ripristino che potrebbe scartare scritture legittime effettuate dopo il punto di backup selezionato.

Un rollback del codice è appropriato solo quando la compatibilità è stata confermata e la migrazione non ha creato uno stato del database non sicuro per la release precedente. Un ripristino è appropriato quando lo stato dei dati deve tornare a un punto noto, ma richiede una gestione attenta delle scritture avvenute dopo il backup. La riparazione dei dati può funzionare per un errore piccolo, pienamente compreso e verificabile. Una correzione in avanti è spesso più sicura quando il ripristino farebbe perdere più attività aziendale valida di quanta ne richieda la correzione del difetto.

Anche il comportamento del framework è importante. Per esempio, Django identifica come irreversibili i passaggi RunPython senza reverse_code e i passaggi RunSQL senza reverse_sql. Il comportamento delle transazioni di migrazione di Django varia anche in base al motore di database: la gestione predefinita differisce tra motori con transazioni DDL, come PostgreSQL e SQLite, e motori quali MySQL e Oracle. Leggi le indicazioni del framework di migrazione dell'applicazione e quelle specifiche del database prima di presumere che sia disponibile un'inversione.

  • Rollback del codice: specifica la release precedente compatibile e i controlli richiesti prima di reindirizzare il traffico.
  • Ripristino: specifica il punto di recovery, il metodo, il downtime previsto, le implicazioni di perdita dati e il responsabile della riconciliazione.
  • Riparazione dei dati: specifica i record interessati, lo script di riparazione, la traccia di audit, la query di convalida e il metodo di inversione della riparazione stessa.
  • Correzione in avanti: specifica lo stato intermedio sicuro, il responsabile, il percorso di escalation e i controlli sull'impatto per gli utenti.
  • Autorità: indica l'operatore tecnico, il responsabile dei dati aziendali e il decisore finale per ogni opzione di recovery.
  • Comunicazioni: prepara messaggi interni e rivolti agli utenti per manutenzione estesa, riconciliazione dei dati o ripristino del servizio.

Domande frequenti

Posso eseguire il rollback di una migrazione del database eseguendo il rollback del container dell'applicazione?

Non necessariamente. Tornare a un'immagine dell'applicazione precedente modifica il codice, non lo schema o i dati del database già confermati. Usa il rollback del codice solo dopo aver confermato che la release precedente sia compatibile con lo stato del database successivo alla migrazione.

Cosa dovrebbe includere una checklist per il rollback di una migrazione del database?

Includi le modifiche esatte a schema e dati, la reversibilità della migrazione, la compatibilità tra vecchio e nuovo codice, i rischi di lock e downtime, il metodo e l'ambito del backup, le evidenze del test di ripristino, la convalida prima e dopo la modifica, un piano di controllo delle scritture, le opzioni di recovery e i responsabili delle decisioni nominati.

Quando un backup non è sufficiente per un rollback sicuro?

Un backup da solo è insufficiente quando il team non sa se include il database, se può essere ripristinato, quale punto nel tempo rappresenta o come saranno gestite le modifiche degli utenti successive al backup. Un piano di ripristino richiede sia evidenze sia una decisione aziendale sulla perdita di dati accettabile.

CREATE INDEX CONCURRENTLY è sempre l'opzione giusta in PostgreSQL?

No. Evita di bloccare INSERT, UPDATE e DELETE concorrenti, ma non può essere eseguito all'interno di un blocco di transazione, utilizza scansioni aggiuntive della tabella e attende le transazioni pertinenti. Sceglilo in base a workload, strumenti di deployment, durata e gestione degli errori.

Quando dovremmo usare una finestra di manutenzione per una migrazione del database?

Usane una quando le operazioni possono bloccare le scritture, quando le versioni vecchia e nuova dell'applicazione non possono coesistere in sicurezza, quando l'attività in background complicherebbe il recovery o quando un ripristino sarebbe la risposta probabile a un errore. Una restrizione temporanea delle scritture può essere sufficiente per modifiche a impatto minore quando le letture possono continuare in sicurezza.

Come si inserisce Airbip nella preparazione alle migrazioni del database?

Airbip gestisce deployment di applicazioni basati su Docker e offre backup configurabili giornalieri, settimanali e mensili. La preparazione alla migrazione rimane una responsabilità operativa condivisa: il team deve verificare cosa viene sottoposto a backup, testare il ripristino quando appropriato, comprendere il comportamento delle migrazioni dell'applicazione e approvare le decisioni relative a dati e recovery.

Fonti e approfondimenti

  1. PostgreSQL transactions — PostgreSQL Global Development Group
  2. PostgreSQL ALTER TABLE — PostgreSQL Global Development Group
  3. PostgreSQL CREATE INDEX — PostgreSQL Global Development Group
  4. PostgreSQL backup and restore — PostgreSQL Global Development Group
  5. PostgreSQL SQL dump — PostgreSQL Global Development Group
  6. PostgreSQL write-ahead logging — PostgreSQL Global Development Group
  7. PostgreSQL MVCC caveats — PostgreSQL Global Development Group
  8. Django migration operations — Django Software Foundation
  9. Django migrations — Django Software Foundation
  10. Docker Compose volume reference — Docker