Notizie del settore
Sicurezza delle modifiche di schema e delle migrazioni di dati su una piattaforma iGaming regolamentata
Il database di una piattaforma non è un documento che viene riscritto. È una struttura che sostiene un peso mentre delle persone ci stanno sopra, e le tabelle che contengono i saldi dei giocatori, la cronologia delle partite e le transazioni regolate sono i muri portanti. Cambiarne la forma è quindi una questione che riguarda ciò che la piattaforma può garantire nei minuti in cui la modifica è in esecuzione, non soltanto quale debba essere la nuova forma.
È per questo che il lavoro sullo schema di una piattaforma regolamentata appare diverso dal lavoro sullo schema di un sito web. Dalla piattaforma ci si aspetta che regoli le partite, rispetti i limiti e risponda a domande su una transazione passata mentre viene aggiunta una colonna, viene applicato un vincolo e diversi milioni di righe vengono riscritte sul posto. La disciplina ingegneristica descritta qui sotto consiste nel farlo in modo deliberato: sapere quale forma di istruzione acquisisce quale blocco, suddividere la modifica in fasi così che nessun singolo passo sia al tempo stesso grande e irreversibile, e conservare le prove che i dati dall’altra parte sono gli stessi dati.

Il blocco è la modifica, non l’istruzione
Ogni operazione sullo schema è in realtà una decisione di blocco con una sintassi allegata, e la disponibilità della piattaforma è decisa dal blocco più che dal tempo che l’istruzione impiega a essere pianificata.
PostgreSQL dichiara il comportamento predefinito senza giri di parole nella sua documentazione di ALTER TABLE: “Viene acquisito un blocco ACCESS EXCLUSIVE salvo esplicita indicazione contraria.” La stessa pagina aggiunge la frase che coglie i team impreparati, perché si applica a un’istruzione che sembra una sola modifica e si comporta come diverse: “Quando vengono forniti più sottocomandi, il blocco acquisito sarà il più restrittivo richiesto da uno qualsiasi dei sottocomandi.” Combinare in un’unica istruzione una modifica dei metadati poco costosa con una costosa non fa una media del costo; eredita il blocco peggiore della lista e lo mantiene per l’intero comando.
La documentazione segnala anche i casi in cui un blocco più leggero è sufficiente. Aggiungere una chiave esterna è uno di questi: ADD FOREIGN KEY “richiede solo un blocco SHARE ROW EXCLUSIVE”, anche se ne acquisisce uno anche sulla tabella referenziata. Convalidare un vincolo già aggiunto come NOT VALID è un altro caso, ed è la ragione per cui l’approccio per fasi descritto più sotto funziona.
La prima regola pratica è quindi un’abitudine di lettura più che una tecnica: prima di eseguire qualsiasi cosa, consultare il livello di blocco della forma utilizzata e dividere un’istruzione che mescola un sottocomando veloce con uno lento.
Quali modifiche riscrivono la tabella e quali toccano solo i metadati
La seconda domanda è se il motore possa modificare la definizione senza riscrivere le righe memorizzate. La documentazione del DDL online di MySQL rende esplicita la distinzione, che vale la pena copiare nel piano di modifica, perché su una tabella grande la differenza si misura in secondi contro ore.
La sua documentazione descrive tre modi in cui un’operazione può comportarsi. Un’operazione istantanea “modifica solo i metadati nel dizionario dei dati”, con un possibile breve blocco esclusivo sui metadati durante l’esecuzione e con DML concorrente consentito. Un’operazione in place modifica la tabella senza una copia completa ma può comunque richiedere una riorganizzazione dei dati. Un’operazione di copia riscrive la tabella.
| Modifica | Come la classifica il motore | Quanto costa alla piattaforma |
|---|---|---|
| Aggiunta di un indice secondario | In place, nessuna ricostruzione della tabella, DML concorrente consentito | Il tempo di costruzione dell’indice, più il percorso di scrittura con cui compete |
Aggiunta del primo indice FULLTEXT |
In place, ma il DML concorrente non è consentito | Un passo che blocca le scritture, non un passo in background |
| Aggiunta di una chiave primaria | In place, ricostruisce la tabella; il manuale definisce la riorganizzazione dei dati sostanziale e costosa | Una riscrittura di grande volume con DML concorrente consentito |
| Eliminazione di una chiave primaria | Ricostruisce la tabella e non consente DML concorrente | Un’interruzione delle scritture per tutta la durata |
| Modifica del tipo di indice | Possibile in modo istantaneo, solo metadati | Praticamente a costo zero, quindi va fatta presto e separatamente |
Due dettagli nella stessa documentazione sono facili da trascurare ed entrambi contano per un piano di modifica. Il primo è che una clausola LOCK è un’asserzione, non una preferenza: se richiede un livello meno restrittivo “di quanto sia consentito per una particolare operazione DDL, l’istruzione fallisce con un errore”. Quel fallimento è l’esito utile, perché arriva prima della modifica e non durante. Il secondo riguarda il momento in cui un indice è effettivamente utilizzabile: un indice secondario appena creato “contiene solo i dati confermati nella tabella al momento in cui l’istruzione CREATE INDEX … termina l’esecuzione”, ed è per questo che un indice appartiene alla fine della sequenza e non all’inizio.
L’abitudine equivalente in PostgreSQL si sblocca con lo stesso ragionamento ma con un vocabolario diverso. Una forma che riscrive la tabella sotto un blocco ACCESS EXCLUSIVE viene pianificata come una migrazione; una forma che acquisisce SHARE UPDATE EXCLUSIVE è una modifica di routine. Il piano di modifica dovrebbe dichiarare per iscritto di quale delle due si tratta, perché le due ottengono finestre diverse, approvazioni diverse e storie di rollback diverse.
Scomporre la modifica: expand, backfill, contract
Il modo per evitare un unico grande passo irreversibile è suddividere la modifica in tre passi singolarmente piccoli, che è la forma che la maggior parte dei team esperti usa già anche senza darle un nome.
Expand. Aggiungere la nuova colonna, tabella o indice senza chiedere al database di dimostrare alcunché sulle righe esistenti. La documentazione di ALTER TABLE di PostgreSQL spiega il meccanismo in modo chiaro: un vincolo aggiunto causa normalmente una scansione della tabella per verificare che tutte le righe esistenti lo soddisfino, ma “se viene usata l’opzione NOT VALID, questa scansione potenzialmente lunga viene saltata”. Il vincolo continua comunque ad applicarsi a tutto ciò che viene scritto in seguito, quindi la piattaforma smette immediatamente di accumulare nuove violazioni, mentre le righe storiche restano non dimostrate.
Backfill. Spostare i dati a lotti, in un processo separato, a un ritmo scelto dalla piattaforma. La sezione seguente lo tratta per quello che è: un carico di lavoro.
Contract. Una volta che le righe storiche sono dimostrate valide e il percorso di lettura è stato spostato, convalidare il vincolo, poi eliminare ciò che non serve più — ed eliminarlo in una modifica successiva, non nella stessa.
Il passo di convalida è quello che rende la sequenza sostenibile. La stessa documentazione afferma che la convalida “non deve escludere gli aggiornamenti concorrenti, poiché sa che altre transazioni applicheranno il vincolo alle righe che inseriscono o aggiornano; solo le righe preesistenti devono essere controllate. Di conseguenza, la convalida acquisisce solo un blocco SHARE UPDATE EXCLUSIVE sulla tabella in fase di modifica.” Una chiave esterna aggiunge un blocco ROW SHARE sulla tabella referenziata.
La stessa suddivisione in fasi si applica a una colonna NOT NULL, dove il comportamento è documentato con più dettaglio di quanto un team di solito presuma. SET NOT NULL viene “normalmente verificato durante l’ALTER TABLE scansionando l’intera tabella, a meno che non sia specificato NOT VALID”; la scansione viene saltata del tutto “se esiste un vincolo CHECK valido (e non viene eliminato nello stesso comando) che dimostri che non può esistere alcun NULL”; e se un vincolo not-null è già stato aggiunto come not valid, SET NOT NULL lo convalida invece di ricontrollare tutto daccapo. In pratica questo significa: aggiungere il check come NOT VALID, convalidarlo e solo allora impostare la colonna come not null — tre piccoli passi il cui costo totale è una sola scansione a un livello che consente le scritture.
Costruire l’indice senza fermare le scritture — e quantificare il costo della concorrenza
Gli indici sono il punto in cui una modifica di schema diventa più spesso un’interruzione del servizio, perché una costruzione semplice dell’indice esclude le scritture per tutta la durata e su una tabella grande può richiedere ore. Ogni motore principale offre ormai un modo per evitarlo, e ciascuno baratta la disponibilità con il lavoro totale.
La documentazione di CREATE INDEX di PostgreSQL descrive insieme l’opzione e il suo costo: CONCURRENTLY costruisce l’indice “senza acquisire alcun blocco che impedisca inserimenti, aggiornamenti o eliminazioni concorrenti sulla tabella; mentre una costruzione standard dell’indice esclude le scritture (ma non le letture) sulla tabella fino al suo completamento”. Poi dice cosa la piattaforma compra con il suo budget di tempo: la costruzione concorrente esegue due scansioni, “deve attendere la conclusione di tutte le transazioni esistenti che potrebbero potenzialmente modificare o usare l’indice”, e quindi “richiede più lavoro totale di una costruzione standard dell’indice e impiega molto più tempo a completarsi”.
Tre comportamenti documentati devono entrare nel runbook, perché sono quelli che sorprendono le persone:
- Una costruzione concorrente può fallire e lasciare qualcosa dietro di sé. L’indice viene inizialmente registrato come indice “invalid”; se durante la scansione sorge un problema come un deadlock, il comando “fallirà ma lascerà dietro di sé un indice ‘invalid’”. Quell’indice viene ignorato per le interrogazioni perché potrebbe essere incompleto, ma “continuerà comunque a consumare overhead di aggiornamento” a ogni scrittura. Il ripristino documentato consiste nell’eliminarlo e rieseguire la costruzione concorrente.
- Un indice univoco inizia ad applicare il vincolo prima di essere utilizzabile. L’unicità viene applicata nei confronti delle altre transazioni a partire dalla seconda scansione, quindi una violazione “potrebbe essere segnalata in altre query prima che l’indice diventi disponibile per l’uso, o anche nei casi in cui la costruzione dell’indice finisca per fallire”, e un indice univoco non valido continua ad applicarla in seguito. Distribuire il vincolo e il codice che lo soddisfa nell’ordine sbagliato trasforma tutto questo in errori visibili sul traffico in produzione.
- Una costruzione concorrente non è componibile. Su una tabella può essere in esecuzione una sola costruzione concorrente di un indice alla volta, lo schema della tabella non può essere modificato mentre la costruzione è in corso, e
CREATE INDEX CONCURRENTLYnon può essere eseguito all’interno di un blocco di transazione. Su una tabella partizionata le costruzioni concorrenti non sono supportate direttamente; l’alternativa documentata è costruire l’indice in modo concorrente su ogni partizione e poi collegare l’indice partizionato, che è un’operazione che tocca solo i metadati.
Quest’ultimo vincolo è quello che di solito impone un rilascio coordinato in una versione successiva: se l’indice non può essere creato nella stessa transazione di migrazione del resto dello schema, la costruzione diventa un passo operativo separato con un proprio monitoraggio.
Un backfill è un carico di lavoro, non uno script
Riscrivere le righe storiche è la parte di una modifica di schema che consuma davvero la capacità della piattaforma, e la tentazione è trattarla come uno script una tantum che viene eseguito fino al completamento. Su una piattaforma di gioco attiva è un carico di lavoro concorrente con una via di ritorno.
Il pattern open source che la maggior parte dei team prende in prestito merita di essere letto per le sue scelte progettuali più che per la sua riga di comando. gh-ost di GitHub descrive la forma di una modifica online: crea una tabella ghost simile all’originale, migra quella tabella mentre è vuota, copia i dati dall’originale al suo interno “lentamente e in modo incrementale”, propaga allo stesso tempo le modifiche in corso, e poi, “al momento giusto”, sostituisce l’originale con la tabella ghost. Due delle sue decisioni progettuali sono quelle trasferibili. Evita deliberatamente i trigger e legge invece il binary log, perché, secondo le parole dei suoi autori, i trigger sono la fonte di “molte limitazioni e rischi” su un percorso di scrittura intenso. E il suo throttle è una vera pausa più che un ritmo rallentato: quando applica il throttle, “cessa davvero le scritture sul master: nessuna copia di righe e nessuna elaborazione degli eventi in corso”, restituendo al database il suo carico di lavoro originale.
Quest’ultima proprietà è ciò di cui un backfill ha bisogno in una piattaforma regolamentata: un interruttore di spegnimento che funziona immediatamente, perché l’innesco per usarlo è di solito un segnale esterno al backfill — la latenza di regolamento che sale, il ritardo di replica che cresce o una coda di assistenza che si riempie di scommesse fallite. Un backfill che può essere rallentato solo riavviandolo non è un backfill che un operatore possa lasciare in esecuzione in sicurezza durante le ore di punta.
Le proprietà che vale la pena scrivere nel piano:
- Lotti limitati con un indicatore di avanzamento confermato. Il processo deve poter riprendere dal punto in cui si è fermato senza riscrivere le righe già elaborate, e deve poter fermarsi tra un lotto e l’altro, non solo tra un’esecuzione e l’altra.
- Scritture idempotenti. Un backfill eseguito due volte deve lasciare lo stesso risultato, perché il guasto più probabile non è un valore sbagliato ma un passo ripetuto dopo uno interrotto.
- Throttling guidato dal segnale di salute della piattaforma stessa, non da un’attesa fissa. Il ritardo di replica, la latenza di scrittura e la profondità della coda di regolamento sono i segnali che dicono se il carico aggiuntivo è gratuito in questo momento.
- Una condizione di completamento verificabile, come il conteggio delle righe che attendono ancora il nuovo valore, invece di una riga di log che dice che lo script è terminato.
- Un ordine deliberato tra il backfill e il cut-over. Se la nuova colonna diventa la fonte di verità mentre il backfill è ancora in esecuzione, la piattaforma ha due scrittori e un problema di riconciliazione.
I limiti di tempo appartengono alla modifica
Un’istruzione sullo schema che attende è peggiore di una che fallisce, perché una modifica in attesa di un blocco occupa risorse e resta un passo incompiuto in una sequenza che nessuno sta più osservando. I timeout sono il modo in cui la piattaforma decide tutto questo in anticipo.
PostgreSQL documenta lock_timeout come un’impostazione di sessione o di istruzione che interrompe “qualsiasi istruzione che attende più a lungo del tempo specificato mentre tenta di acquisire un blocco su una tabella, un indice, una riga o un altro oggetto del database”, e osserva che il limite “si applica separatamente a ogni tentativo di acquisizione del blocco”. È disattivato per impostazione predefinita, e la documentazione sconsiglia esplicitamente di impostarlo nel file di configurazione del server “perché avrebbe effetto su tutte le sessioni” — ed è esattamente per questo che appartiene agli strumenti di modifica, limitato alla connessione che esegue la migrazione. Segnala anche la trappola dell’ordine: se statement_timeout è impostato a un valore uguale o inferiore, scatterà sempre per primo, quindi un lock timeout scelto per proteggere la piattaforma può essere mascherato da uno statement timeout scelto per uno scopo diverso.
L’impostazione correlata è quella che protegge dalla migrazione che si blocca senza fallire. idle_in_transaction_session_timeout termina qualsiasi sessione lasciata inattiva all’interno di una transazione aperta, e la documentazione ne indica la ragione operativa: può essere usata per garantire che le sessioni inattive “non mantengano blocchi per un periodo di tempo irragionevole”. Un processo di modifica che apre una transazione, esegue un passo e poi attende qualcos’altro mantiene ciò che ha bloccato, e su una piattaforma il costo lo pagano i giocatori la cui partita non può essere regolata.
Il piano di modifica dovrebbe quindi dichiarare, per ogni passo: con quali impostazioni di sessione viene eseguito, cosa fa quando va in timeout e se il fallimento lascia il database in uno stato dal quale il passo successivo può riprendere. Un timeout che lascia un indice non valido o un vincolo non convalidato è un punto di controllo, non un disastro, a condizione che il piano lo dica prima che accada.
La verifica è un confronto, non una spunta verde
Una modifica di schema è conclusa quando i dati dimostrano che è conclusa. La prova è un confronto tra due stati della stessa domanda, rilevati in due momenti diversi, e deve essere riproducibile da qualcuno che non ha partecipato alla modifica.
| Cosa si confronta | Perché è quello che conta |
|---|---|
| Conteggi delle righe per tabella e per partizione | Poco costosi, e individuano il lotto troncato e l’inserimento duplicato |
| Somme e conteggi sulle colonne monetarie, raggruppati come li raggruppa il registro contabile | Il confronto che chiederà un revisore finanziario o di conformità, e quello che individua un backfill che ha contato male |
| Un checksum su un insieme di chiavi definito, prima e dopo | Trasforma “crediamo che sia stato eseguito” in un valore che può essere ricalcolato |
| Partite aperte e transazioni non regolate al confine | Lo stato più probabilmente migrato a metà, perché era in corso quando la modifica è stata eseguita |
| L’insieme delle righe che il nuovo vincolo rifiuta | Un conteggio delle violazioni è al tempo stesso una misura di avanzamento e una misura di rischio |
| Letture attraverso il nuovo percorso rispetto a quelle attraverso il vecchio sulle stesse righe | Rileva l’errore di mappatura che lascia entrambi i percorsi singolarmente validi |
Due abitudini rendono tutto questo sostenibile. La prima è la finestra di riconciliazione: alla modifica segue un periodo in cui il registro contabile viene confrontato come viene confrontato dopo qualsiasi altro evento di regolamento, ed è qui che si applica già la disciplina di riconciliazione del portafoglio. La seconda è che il vecchio percorso resta leggibile finché il confronto non è superato, che è il contenuto tecnico di “expand and contract” e la ragione per cui l’eliminazione di una colonna viene pianificata in una modifica successiva.
Nessuno di questi confronti dimostra che una partita possa ancora essere interpretata nel modo in cui è stata interpretata quando è stata giocata. Vale la pena enunciare questa proprietà separatamente, perché una modifica di schema può romperla silenziosamente: la disciplina di replay deterministico esiste proprio perché una partita passata a volte deve essere ricostruita dai dati così come erano, e una modifica che aggiunge, rinomina o reinterpreta una colonna è una modifica a quel registro.
Decidere il rollback prima che venga eseguita la prima istruzione
Ogni piano di modifica ha un punto oltre il quale tornare indietro costa più che andare avanti. Dare un nome a quel punto in anticipo è la differenza tra una decisione e un’improvvisazione alle tre del mattino.
La parte reversibile è di solito più ampia di quanto i team presumano, per come si comportano i vincoli suddivisi in fasi. Un vincolo NOT VALID aggiunto può essere eliminato. Un backfill può essere fermato e riavviato. Un indice appena costruito può essere eliminato — ed eliminare un indice, a differenza di costruirne uno, è una modifica dei metadati che consente DML concorrente. La parte irreversibile è ristretta e identificabile: eliminare una colonna o una tabella, e qualsiasi modifica che sia già stata letta e scritta dal nuovo percorso di codice.
Questo dà a un piano di modifica una forma anziché una lista di controllo:
- Expand e backfill sono reversibili e possono essere abbandonati in qualsiasi momento, lasciando colonne extra e un indice extra che occupano spazio ma non cambiano nulla.
- Il cut-over è il passo reversibile con sforzo: il codice può essere riportato indietro, ma solo finché il vecchio percorso è ancora mantenuto.
- Contract è il punto di non ritorno e appartiene a una modifica separata e successiva, con una propria approvazione, una propria finestra e una propria verifica — dopo il periodo di osservazione definito dal piano, non alla fine della finestra di modifica.
La stessa disciplina determina che aspetto ha un incidente. Una modifica fallita con uno schema non riportato indietro è un incidente diverso da una modifica fallita che è stata abbandonata in modo pulito, e il piano di risposta agli incidenti è il luogo in cui si scrive la differenza tra i due, insieme a chi è autorizzato a chiamare il rollback.
Provare su una copia della dimensione reale
Una modifica di schema provata su una tabella vuota non dimostra quasi nulla, perché ciò che viene testato è come l’operazione si comporta rispetto alla forma reale dei dati.
La prova richiede tre proprietà. Dimensione: una copia della tabella con il volume di produzione, perché la differenza tra una modifica istantanea dei metadati e una che riscrive compare solo quando ci sono righe da riscrivere. Concorrenza: scritture in corso mentre la modifica viene eseguita, perché un blocco invisibile su un database inattivo è l’intero problema su uno occupato. Iniezione: un guasto indotto deliberatamente — un’istruzione terminata a metà backfill, un vincolo che dovrebbe rifiutare una riga, una costruzione di indice che fallisce — perché il percorso di ripristino è la parte del piano che nessuno ha testato.
È per questo che esiste l’ambiente non di produzione, e i requisiti degli ambienti non di produzione sono la sede giusta per farlo. Una prova può anche prendere in prestito il percorso di ripristino: una modifica provata su una copia ripristinata del database di produzione testa sia la modifica sia la ripristinabilità del backup da cui proviene, che è la stessa prova richiesta dalla disciplina di verifica di backup e ripristino.
Per una modifica a una tabella di proprietà di un altro team — una tabella di integrazione con un fornitore, un’estrazione per il reporting, una tabella che un partner legge — la prova è anche il momento di confermare che la modifica sia compatibile con le loro letture. La guida alla migrazione e al cutover di piattaforma copre la versione più ampia di quel problema: un trasferimento di piattaforma è una sequenza di modifiche coordinate, e lo schema è una delle cose che vengono coordinate, non un dettaglio implementativo del trasferimento.
Trasformarlo in prove di accettazione
Il fascicolo che un revisore, un auditor o un acquirente dovrebbe poter leggere è breve, e ogni voce al suo interno è verificabile anziché asserita:
- Il piano di modifica, che per ogni passo indica la forma dell’istruzione, il livello di blocco che il motore documenta per essa e se l’operazione riscrive la tabella.
- La scomposizione, che mostra che nessun singolo passo riscrive al tempo stesso una tabella grande ed è irreversibile.
- La specifica del backfill: dimensione dei lotti, indicatore di avanzamento, comportamento alla ripresa, segnale di throttle e condizione di completamento.
- Le impostazioni di sessione con cui viene eseguito ogni passo, inclusi i timeout di blocco e di transazione inattiva e cosa succede quando scattano.
- Le prove del confronto: i conteggi, le somme e i checksum rilevati prima e dopo, con le query che li hanno prodotti.
- Il registro della prova, incluso il guasto iniettato e il ripristino osservato.
- Il punto di rollback dichiarato, i passi che restano reversibili e chi è autorizzato a chiamare il rollback.
- La pianificazione del passo di contract, tenuta separata dalla modifica che lo ha reso possibile.
Il lavoro qui descritto è in gran parte preparazione, ed è per questo che viene così spesso compresso nella finestra di modifica stessa, dove è al tempo stesso più costoso e più visibile ai giocatori. L’abitudine a cui appartiene — specificare il comportamento prima di costruirlo e mantenere aggiornata la specifica quando il sistema cambia — è la stessa che sta dietro alla disciplina di sviluppo della piattaforma che questo sito applica altrove. Per un database la specifica è insolita sotto un aspetto: deve restare vera mentre sotto di essa vengono riscritte diversi milioni di righe, ed è l’unica ragione per cui vale la pena mettere per iscritto la sequenza prima di eseguirla.
Domande che si pone un team di piattaforma
Possiamo semplicemente pianificare la migrazione in un’ora tranquilla?
Si può fare, e vale comunque la pena scomporre l’istruzione. Un’ora tranquilla riduce la probabilità che qualcuno sia a metà di una partita quando viene acquisito il blocco; non cambia quanto dura una riscrittura, e su una tabella abbastanza grande da contare la riscrittura dura più di qualsiasi ora tranquilla di cui la piattaforma disponga. Le due mitigazioni sono separate: la finestra riduce il numero di persone coinvolte, e la modifica per fasi riduce ciò da cui sono coinvolte. Una modifica che si affida solo alla finestra fallisce la prima volta che la tabella cresce oltre di essa.
CREATE INDEX CONCURRENTLY è sicuro da eseguire in qualsiasi momento?
È progettato per non bloccare inserimenti, aggiornamenti o eliminazioni concorrenti, ed è comunque un’operazione più pesante di una costruzione semplice, perché esegue due scansioni e attende le transazioni che potrebbero toccare l’indice. Due dei suoi comportamenti documentati decidono se sia sicuro in una data ora: può fallire e lasciare un indice non valido che consuma overhead di aggiornamento finché non viene eliminato, e un indice univoco applica l’unicità a partire dalla seconda scansione, il che significa che può far emergere violazioni sul traffico in produzione prima di essere utilizzabile. La risposta è quindi una sequenza più che un sì: costruirlo quando i dati sono noti come validi, sorvegliare lo stato non valido e trattare l’indice come un passo operativo separato dal resto della modifica.
Cosa deve essere davvero vero prima di eliminare la vecchia colonna?
Tre cose, in ordine. Il nuovo percorso deve essere l’unico scrittore. Il confronto sui dati monetari e sulle partite deve essere stato superato lungo un ciclo di regolamento completo, non solo con un controllo a campione. E deve essere trascorso un periodo di osservazione definito senza che alcun consumatore legga ancora la vecchia colonna — compresi l’estrazione per il reporting, il feed del partner e la dashboard che nessuno ricordava. Finché tutte e tre le condizioni non sono soddisfatte, la vecchia colonna è un’assicurazione a basso costo: occupa spazio, ed è anche l’unica copia rimasta dei dati nella forma che il vecchio codice comprendeva.
Come facciamo a sapere che il backfill è davvero concluso?
Non usare l’assenza di errori o una riga di log. Definisci una condizione di completamento a cui il database possa rispondere, come il conteggio delle righe che conservano ancora il valore precedente o che non soddisfano ancora il nuovo vincolo, e pretendi che quel conteggio raggiunga lo zero e vi rimanga per un intero ciclo di scrittura dopo il cut-over. Poi mantieni lo stesso conteggio come monitor per un periodo definito, perché un percorso di codice che scrive il vecchio valore può essere reintrodotto dal rollback di una release non correlata, e un backfill silenzioso non è la stessa cosa di un backfill che resta concluso.
Qual è il primo passo utile più piccolo se nulla di tutto questo esiste ancora?
Scrivi, per la prossima modifica che devi comunque fare, i quattro fatti che la documentazione ti dice già: la forma dell’istruzione, il blocco che acquisisce, se riscrive la tabella e il punto oltre il quale la modifica non è più reversibile. È un pomeriggio di lettura e cambia immediatamente il piano, perché le due decisioni che causano la maggior parte dei danni — combinare un sottocomando veloce con uno lento in un’unica istruzione e scoprire il punto di rollback a fatto avvenuto — vengono entrambe prese nel momento in cui il piano viene scritto.
