Blog ·
Proiettare, mai sostituire
Una vista di avanzamento su migrazioni, backup, ripristini e reinstallazioni, senza diventare un secondo registro. Due guasti sono stati del tutto silenziosi.
Di Provibr
Un pannello di hosting accumula lavoro di lunga durata. Una migrazione fra due pannelli di controllo, un backup, un ripristino verso una macchina nuova, un container che viene sostituito. Ognuno ha creato la propria tabella, il proprio vocabolario di stati e il proprio schermo, e questo regge finché qualcuno non pone la domanda ovvia: cosa sta girando adesso sul mio account, e se qualcosa è bloccato.
La risposta allettante è un sistema di attività. Una tabella, un elenco di stati, tutto il resto scrive dentro. La risposta allettante è anche la via verso due fonti di verità sullo stesso fatto, e verso una conversazione di assistenza un anno dopo in cui l’attività dice fallita e la tabella dei backup dice che l’archivio è sullo storage.
Per questo la vista delle operazioni è una proiezione. Legge le righe esistenti e scrive un riepilogo sopra di esse. Non va mai nulla nella direzione opposta: nemmeno una funzione del modulo di specchio tocca le esecuzioni di backup, quelle di ripristino, quelle di migrazione o le reinstallazioni. Quelle tabelle restano la verità, e al riepilogo è consentito essere superato, sbagliato o assente senza che si rompa nulla a valle.
Uno specchio per tabella, e un test che legge lo schema
Quattro tabelle poggiano su questa cucitura, e ciascuna ha esattamente una funzione che la proietta.
| Tabella di origine | Colonna di stato | Tipo di operazione |
|---|---|---|
backup_runs | status | backup.run |
backup_restore_runs | status | backup.restore |
migration_runs | status | migration.run |
container_reinstall_intents | state | container.reinstall |
L’ultima riga è il motivo per cui i nomi di colonna sono scritti come testo e non dati per scontati. Quella tabella chiama la colonna state, e uno specchio che leggesse status non riceve nulla e scivola senza lamentarsi nel ramo di fallimento.
Quella tabella non è documentazione. Un test deduce dallo schema della base dati quali di quelle tabelle hanno una colonna di stato, richiede una voce qui per ciascuna, richiede che la funzione nominata esista davvero e richiede, ed è la parte che fa il lavoro vero, che ogni file che scrive una di quelle colonne nomini anche il proprio specchio. Una quinta tabella che arriva sulla cucitura senza proiezione è così un test rosso anziché un cassetto che smette di muoversi in silenzio.
Tanta severità serve perché gli scrittori sono sparsi: un’esecuzione di backup cambia stato in sei punti di un unico file, tre dei quali sul percorso che tratta le risposte della riconciliazione che gira di notte. Lasciare che ogni scrittore invochi la proiezione da sé significa che il sesto se ne dimentica, e la dimenticanza è invisibile. La riga è corretta. Solo che lo schermo non si aggiorna mai.
Il collettore che esisteva due volte
Lo specchio vive nel pacchetto che possiede le scritture su base dati, perché sia un’azione server dell’applicazione web sia un componente del gateway chiamano le stesse funzioni. Quel pacchetto non può pubblicare un evento in diretta: dipende dalla base dati e da codice condiviso e da nient’altro, e il bus dei messaggi è un oggetto diverso su ciascun lato.
Espone quindi un collettore. Ogni applicazione ne registra uno, e il pacchetto non sa chi lo abbia riempito. Se non lo fa nessuno, la scrittura su base dati prosegue lo stesso e non parte alcun evento in diretta, e il cassetto si aggiorna al caricamento di pagina successivo. È un degrado onesto, e la funzione non solleva mai eccezioni di proposito, perché una registrazione mancante non deve poter far cadere un backup.
Il collettore era una variabile di modulo, e questo era sbagliato in un modo che non produceva alcun errore. Next esegue il proprio file di strumentazione come pacchetto a sé, separato dai processi di rotta. La variabile esisteva perciò due volte: la registrazione finiva in un esemplare e la funzione che emette leggeva l’altro. Le righe erano giuste, gli stati avanzavano, il cassetto restava fermo, e in nessun registro compariva una sola riga.
Non è stato trovato ragionando. È stato trovato mettendo un ascoltatore sul bus dei messaggi e contando: è arrivato un evento dove dovevano essercene tre. La riparazione è un alloggiamento su globalThis dietro un Symbol.for, che sopravvive a un confine di impacchettamento dentro lo stesso processo, stessa forma e stesso motivo della connessione condivisa alla base dati e di quella a Redis.
Due lezioni che valgono più del guasto. Un guasto senza riga di errore richiede uno strumento fuori dal codice che si sospetta. E «questo modulo ha un esemplare» è un’ipotesi su un impacchettatore, non una proprietà del linguaggio.
Lo stesso ciclo ne ha trovato un secondo silenzioso. Concludere un’operazione e bloccarla lasciavano entrambe intatta la colonna di fase, quindi un backup perfettamente riuscito restava per sempre su «archiviazione». Non falliva nulla; semplicemente l’ultimo passo non arrivava mai.
Annullare è una richiesta, e una sola scrittura ne è la prova
È nell’annullamento che una proiezione è più tentata di mentire. Il cassetto conosce l’operazione, l’operazione ha un pulsante di annullamento, e l’attuazione più economica mette un contrassegno e chiude il pannello.
Per questo ogni tipo dichiara se annullare abbia perfino un senso. Un vzdump in corso non possiamo annullarlo: il comando è presso l’agente, l’attività presso l’ipervisore, e nessuno sa cosa rappresenti mezzo archivio sullo storage del cliente. Un ripristino è identico, con un motivo in più: la riga di ripristino tiene una prenotazione sull’identificativo di destinazione finché resta aperta, e chiuderla in anticipo libera quell’identificativo mentre forse ci sta atterrando sopra un ospite. Entrambi sono dichiarati non supportati, e non c’è alcuna via che lo aggiri.
Dove annullare è possibile, decide comunque la riga. Una migrazione si annulla finché è pianificata e non più quando un programma ausiliario sta creando server sull’host di qualcun altro. Una reinstallazione di container si annulla fino all’anteprima e non più quando l’agente ha iniziato a sostituire.
Quando funziona, la prova che non è successo nulla è un’unica scrittura condizionata: porre il comando in coda su annullato là dove il suo stato è ancora in attesa. Se è già stato inviato, quell’istruzione tocca zero righe e la richiesta resta onestamente su «richiesta». Consultare e poi scrivere sarebbero due istruzioni e quindi due occasioni per inserirsi in mezzo.
E allora la frase conta. L’evento dice che il comando è stato tolto dalla coda prima che l’agente lo ricevesse, e non che è stato annullato. Non è stato interrotto nulla, perché nulla era iniziato. Quella riga va nel registro generale degli eventi e non nell’avanzamento dell’operazione, perché l’avanzamento viene ripulito dopo novanta giorni e «una persona è intervenuta sull’infrastruttura» non è avanzamento.
Quattro blocchi, e nessuno è un fallimento
Quattro situazioni mettono un’operazione su richiede attenzione anziché su fallita.
Un vzdump ha segnalato OK e l’archivio non è nell’indice. Un tentativo di backup ha superato il proprio tempo e non sappiamo se l’archivio esista. Un ripristino non è rileggibile oppure l’ospite ottenuto non è stato registrabile. Una reinstallazione di container si è fermata dopo che il vecchio container non c’era più.
Chiamare fallita una di queste sarebbe una bugia con una conseguenza. Fallita invita il cliente a rifarlo, e rifare un backup sono altre decine di gigabyte sullo storage di qualcuno per un archivio che forse c’è già. Rifare una reinstallazione è un secondo container. Il ciclo di riconciliazione continua a guardare finché l’indice non si pronuncia, e nel frattempo l’operazione resta visibilmente bloccata, che è la giusta dose di allarme.
Il blocco porta anche a chi appartiene. Tre di essi sono nostri; un comando davvero fallito dalla parte del cliente lo dice.
Una prova a vuoto finisce alla pianificazione, e non è una lacuna
Una migrazione in prova a vuoto ha cinque passi; una reale ne ha otto, e i tre in più sono la scrittura e il riconteggio. L’elenco delle fasi ne contiene sei, quindi una prova a vuoto riuscita mostra le ultime due come non eseguite.
Sembra un difetto di una barra di avanzamento ed è la verità: una prova a vuoto non scrive e non riconta. Le alternative erano un secondo tipo, che raddoppia nove voci di catalogo in nove lingue per lo stesso lavoro, oppure un elenco di fasi variabile per riga, che farebbe divergere il cassetto e la pagina di dettaglio su quanti passi ci sono. Mostrare la verità costava meno di entrambe.
Cosa metteremmo per iscritto
- Una proiezione proietta. Se il suo strato di riepilogo può riscrivere nell’origine, è un secondo registro e ora ne ha due.
- Metta la cucitura in una tabella che un test possa leggere, derivata dallo schema, e gli faccia verificare che ogni scrittore di una colonna di stato nomini il proprio specchio. Una chiamata dimenticata non produce errori.
- Non dia per scontato che un modulo abbia un esemplare. Un’intelaiatura che esegue parte del suo codice come pacchetto separato gliene darà due, e il sintomo è il silenzio.
- Misuri fuori dal sospettato. Un ascoltatore sul bus ha trovato in pochi minuti ciò che la lettura non aveva trovato affatto.
- Dica cosa è successo davvero. «Tolto dalla coda prima che l’agente lo ricevesse» è più lungo di «annullato» ed è l’unica versione esatta.
- Riservi fallita al fallimento. Gli esiti sconosciuti hanno bisogno di uno stato proprio, altrimenti le persone ripetono operazioni costose forse già riuscite.
Per chiarezza su cosa sia tutto questo: i tipi qui descritti sono dietro interruttori di rilascio disattivati per impostazione predefinita, quindi migrazioni e backup dei clienti non sono acquistabili oggi. Diverse cose restano non provate. L’evento di avanzamento in diretta è stato misurato solo per le migrazioni. Il lato gateway della registrazione del collettore non è stato provato con un gateway realmente in funzione. La catena dell’agente è stata riprodotta con SQL. E annullare una reinstallazione di container dallo schermo delle operazioni non è mai stato guidato in un browser.
Dal lavoro di piattaforma su Provibr. Perché una barra di avanzamento rifiuta di stimare un orario di fine è in una barra di avanzamento che non indovina, e perché una migrazione saltata in silenzio è peggio di una fallita è in la migrazione che non è mai partita.