Un sistema può funzionare perfettamente con centinaia di utenti e comportarsi in modo molto diverso quando l’attività raggiunge migliaia di richieste simultanee.
A quel punto, la capacità dei server rappresenta solo una parte della sfida. Più processi possono leggere, modificare e scambiare gli stessi dati quasi nello stesso momento. Un pagamento può essere in fase di conferma mentre un altro processo ne verifica lo stato. Un documento può essere aggiornato mentre un altro servizio lo sta utilizzando. Una richiesta può essere inviata nuovamente perché la prima risposta tarda ad arrivare, anche se l’operazione iniziale è già stata eseguita.
In sistemi di questo tipo, la velocità ha valore solo quando è accompagnata dal controllo su ciò che avviene in parallelo.
Quando il sistema cresce, cambia anche la natura del problema
Una delle convinzioni più comuni sulla scalabilità è che un sistema più grande abbia bisogno soprattutto di una maggiore potenza di elaborazione.
In realtà, la crescita aumenta anche le interazioni tra i processi. Più utenti, servizi e integrazioni operano contemporaneamente, più diventa importante coordinare il modo in cui le informazioni vengono modificate.
Questo aspetto è particolarmente rilevante nelle piattaforme in cui una singola azione attiva diversi processi. Una richiesta può aggiornare un database, comunicare con un altro sistema, generare un documento e inviare una notifica.
Dall’esterno è una sola azione. All’interno dell’architettura è una catena di processi che deve mantenere la stessa logica anche quando migliaia di catene simili vengono eseguite in parallelo.
La stessa richiesta può arrivare due volte. Il sistema deve saperlo.
Una delle sfide più interessanti dei sistemi distribuiti è che una richiesta ripetuta non significa necessariamente che l’utente abbia eseguito due volte la stessa azione.
Una connessione può interrompersi dopo che il server ha elaborato la richiesta, ma prima che la risposta raggiunga l’applicazione. Dal punto di vista dell’applicazione, il risultato è incerto: l’operazione è stata eseguita oppure no?
In molte architetture, la risposta naturale è riprovare.
Ma se il sistema non è stato progettato per gestire questa situazione, un nuovo tentativo può trasformarsi in una seconda operazione: un altro pagamento, un record duplicato o lo stesso processo eseguito due volte.
È qui che entra in gioco un principio importante dei sistemi moderni: l’idempotenza. Il sistema deve essere in grado di riconoscere che una richiesta è già stata elaborata e, quando il processo lo richiede, restituire il risultato esistente invece di eseguire nuovamente l’operazione.
È un buon esempio di come l’affidabilità di un sistema venga costruita anche intorno a situazioni che l’utente potrebbe non vedere mai.
Non tutto deve avvenire nello stesso secondo
Un altro aspetto importante nell’architettura dei sistemi ad alto volume riguarda il modo in cui viene gestito il tempo.
Non tutti i processi devono necessariamente essere completati all’interno della stessa richiesta.
Alcune azioni richiedono una conferma immediata. Altre possono essere inserite in una coda ed elaborate pochi istanti dopo. Per questo i sistemi moderni utilizzano code di messaggi, eventi e processi asincroni per distribuire il carico ed evitare che una parte del sistema rallenti le altre.
Questo approccio consente di creare un’architettura più resiliente, ma introduce anche un’altra responsabilità: ogni componente deve conoscere lo stato corrente del processo.
Un’operazione può essere stata accettata senza essere ancora completata. Un servizio può averla già elaborata mentre un altro non lo ha ancora fatto. A questo livello, gestire lo stato di un processo diventa importante quanto elaborare l’operazione stessa.
“Corretto” non significa sempre “aggiornato ovunque nello stesso millisecondo”
Quando un sistema è composto da più applicazioni, database e servizi, emerge un’altra decisione architetturale: quanto rapidamente deve propagarsi una modifica nell’intero ecosistema?
Per alcuni dati, la coerenza deve essere immediata. Per altri, un ritardo di pochi secondi può essere perfettamente accettabile.
Questa distinzione è importante perché cercare di sincronizzare immediatamente ogni informazione in ogni parte del sistema può generare complessità e carichi non necessari.
Una buona architettura non applica lo stesso approccio a ogni tipo di dato. Stabilisce dove è necessaria una coerenza immediata, dove è accettabile una sincronizzazione graduale e quale sistema rappresenta la fonte di riferimento dell’informazione.
La scalabilità è una questione di coordinamento, non solo di capacità
È possibile aggiungere server, aumentare la capacità del database e distribuire il traffico su un numero maggiore di istanze.
Ma senza regole chiare per coordinare le operazioni, una maggiore capacità può semplicemente tradursi in più processi che competono per gli stessi dati a una velocità ancora maggiore.
È proprio qui che scalabilità e architettura si incontrano.
In ALSoft, progettare sistemi destinati a operare su larga scala significa andare oltre le sole prestazioni e l’infrastruttura. Significa considerare anche il modo in cui vengono gestiti i dati, coordinati i processi paralleli e organizzata la comunicazione tra i servizi, oltre al comportamento del sistema quando un componente risponde in ritardo o una richiesta viene ripetuta.
Una piattaforma affidabile deve mantenere la stessa logica anche quando i volumi cambiano.
L’architettura rivela tutta la sua importanza quando il sistema è sotto pressione
Durante il normale funzionamento, molte decisioni architetturali rimangono invisibili. La loro importanza emerge quando migliaia di richieste arrivano contemporaneamente, un servizio risponde più lentamente del previsto, un messaggio viene inviato due volte o diversi processi tentano di modificare la stessa informazione.
Come sintetizza Ermal Beqiri, fondatore di ALSoft:
«In un sistema complesso, ogni azione fa parte di una catena più ampia. L’architettura crea la struttura che mantiene coordinati questi processi, accurati i dati e affidabile il sistema, anche quando molte operazioni avvengono contemporaneamente.»
Un sistema progettato per operare su larga scala non punta semplicemente a eseguire più operazioni nello stesso momento. Deve garantire che, anche quando tutto avviene in parallelo, il sistema continui a funzionare come un insieme coerente.
