Eliminare i tempi di inattività tramite gli aggiornamenti del servizio con controllo delle versioni

In passato, gli amministratori dovevano portare offline un server per aggiornare e aggiornare il software locale. Tuttavia, il tempo di inattività è un nonstarter completo per i servizi globali 24×7. Molti servizi cloud moderni sono una dipendenza fondamentale per gli utenti che eseguono le proprie attività. Non c'è mai un buon momento per arrestare un sistema, quindi come un team può fornire un servizio continuo durante l'installazione di importanti aggiornamenti della sicurezza e delle funzionalità?

Usando gli aggiornamenti con controllo delle versioni, questi servizi critici possono essere trasferiti facilmente da una versione a un'altra mentre i clienti li usano attivamente. Non tutti gli aggiornamenti sono difficili. L'aggiornamento di layout o stili front-end è semplice. Le modifiche apportate alle funzionalità possono essere difficili, ma esistono procedure note per attenuare i rischi di migrazione. Tuttavia, le modifiche che provengono dal livello dati introducono una nuova classe di sfide che richiedono considerazioni speciali.

Aggiornare i livelli separatamente

Con un servizio online distribuito in più data center e un archivio dati separato, non tutto può cambiare contemporaneamente. Se il servizio tipico è suddiviso in codice e database dell'applicazione, che sono presumibilmente con controllo delle versioni indipendentemente l'uno dall'altro, uno di questi lati deve assorbire la complessità della gestione del controllo delle versioni.

Spesso il controllo delle versioni è più semplice da gestire nel codice dell'applicazione. I sistemi di grandi dimensioni hanno in genere un po' di codice legacy, ad esempio SQL che si trova all'interno dei relativi database. Invece di complicare ulteriormente questo codice SQL, il codice dell'applicazione deve gestire la complessità. In particolare, è possibile creare un set di classi factory che comprendano il controllo delle versioni di SQL.

Durante ogni sprint, creare una nuova interfaccia con tale versione, in modo che sia sempre presente codice corrispondente a ogni versione del database. È possibile eseguire facilmente il rollback di tutti i file binari durante la distribuzione. Se si verifica un errore dopo la distribuzione dei nuovi file binari, ripristinare il codice precedente. Se la distribuzione binaria ha esito positivo, avviare la manutenzione del database.

Quindi come funziona effettivamente questo? Si supponga, ad esempio, che il team stia attualmente distribuendo Sprint 123. I file binari comprendono lo schema del database Sprint 123 e conoscono lo schema Sprint 122. Il modello generale prevede l'uso di entrambe le versioni/sprint N e N-1 dello schema SQL. I file binari eseguono query sul database, determinano la versione dello schema a cui comunicano e quindi caricano l'associazione appropriata. Il codice dell'applicazione gestisce quindi il caso quando il nuovo schema di dati non è ancora disponibile. Quando la nuova versione è disponibile, il codice dell'applicazione può iniziare a usare la nuova funzionalità abilitata dalla versione più recente del database.

Eseguire il roll forward solo con il livello dati

Dopo l'aggiornamento dei database, il servizio si trova in una situazione di roll forward se si verifica un problema. Le migrazioni di database online sono complesse e spesso in più passaggi, quindi il rollforward è in genere il modo migliore per risolvere un problema. In altre parole, se l'aggiornamento ha esito negativo, è probabile che anche il rollback abbia esito negativo. C'è poco valore nell'investire nel tentativo di compilare e testare il codice di rollback che il team non si aspetta mai di usare.

Sequenza di distribuzione

Si consideri uno scenario in cui è necessario aggiungere un set di colonne a un database e trasformare alcuni dati. Questa transizione deve essere invisibile agli utenti, il che significa evitare blocchi di tabella il più possibile e quindi mantenere i blocchi per il tempo più breve possibile in modo che non siano percepibili.

La prima cosa da fare è modificare i dati, possibilmente in tabelle parallele usando un trigger SQL per mantenere sincronizzati i dati. Le migrazioni e le trasformazioni di dati di grandi dimensioni a volte devono essere in più passaggi in più distribuzioni in più sprint.

Dopo aver creato i dati aggiuntivi o il nuovo schema in parallelo, il team passa alla modalità di distribuzione per il codice dell'applicazione. In modalità di distribuzione, quando il codice effettua una chiamata al database, afferra prima un blocco sullo schema e quindi lo rilascia dopo l'esecuzione della stored procedure. Il database non può cambiare tra l'ora in cui viene eseguita la chiamata al database e l'esecuzione della stored procedure.

Il codice di aggiornamento funge da writer di schema e richiede un blocco writer sullo schema. Il codice dell'applicazione ha priorità nell'accettare un blocco lettore e il codice di aggiornamento si trova in background cercando di acquisire il blocco writer. Nel blocco writer sono consentiti solo un numero ridotto di operazioni molto veloci nelle tabelle. Il blocco viene quindi rilasciato e l'applicazione registra che la nuova versione del database è in uso e usa l'interfaccia corrispondente alla nuova versione del database.

Tutti gli aggiornamenti del database vengono eseguiti usando un modello di migrazione. Un set di codice e script esamina la versione del database e quindi apporta modifiche incrementali per eseguire la migrazione dello schema dalla versione precedente alla nuova. Tutte le migrazioni vengono automatizzate e implementate tramite il servizio di gestione delle versioni.

L'interfaccia utente Web deve anche essere aggiornata senza interrompere gli utenti. Quando si aggiornano file JavaScript, fogli di stile o immagini, evitare di combinare versioni precedenti e nuove caricate dal client. Ciò può causare errori che potrebbero perdere il lavoro in corso, ad esempio un campo modificato da un utente. Pertanto, è consigliabile eseguire la versione di tutti i file JavaScript, CSS e image inserendo tutti i file associati a una distribuzione in una cartella separata con controllo delle versioni. Quando l'interfaccia utente Web effettua chiamate al livello applicazione, vengono caricati gli asset con una versione specificata. Solo quando un'azione utente genera un aggiornamento a pagina intera, la nuova interfaccia utente Web viene caricata nel browser. L'esperienza dell'utente non viene interrotta dall'aggiornamento.

Passaggi successivi

Microsoft è stata una delle più grandi aziende di sviluppo software al mondo per decenni. Informazioni su come Microsoft gestisce sistemi affidabili con DevOps.