Local-First
CRDT
Sincronização de Dados
Arquitetura
Tempo Real

CRDT: come sincronizzare i dati serverless per arbitrare i conflitti

I CRDT risolvono il problema più difficile local-first: unire le modifiche offline senza perdere dati e senza un server centrale per decidere chi vince.

CRDT: come sincronizzare i dati serverless per arbitrare i conflitti

Immagina due utenti che modificano lo stesso documento su piani diversi, senza Internet. Ciascuno cambia lo stesso campo. Quando atterrano e i dispositivi si sincronizzano, qualcuno deve decidere quale versione vince. La risposta tradizionale era semplice e brutale: l'ultimo da salvare sovrascrive quello precedente. L'altro perde il lavoro e non se ne rende nemmeno conto.

Questo è il problema principale di qualsiasi applicazione local-first. I dati risiedono prima sul dispositivo, la modifica avviene offline e la riconciliazione avviene successivamente. La questione non è se ci sarà conflitto, ma come unire due verità divergenti senza perdere informazioni e, idealmente, senza fare affidamento su un server centrale che faccia da giudice. I CRDT sono la risposta matematica più elegante che abbiamo a questo proposito.

Cos'è un CRDT, senza misticismo

CRDT sta per tipo di dati replicati senza conflitti. Il nome spaventa più del concetto. In pratica si tratta di una struttura dati progettata in modo che più copie possano essere modificate in modo indipendente e, quando si incontrano, convergono automaticamente allo stesso stato finale, senza coordinamento preventivo.

La parola chiave è convergenza. Non importa l'ordine in cui arrivano le modifiche, né quante volte la stessa modifica viene applicata, né quanti salti ha attraversato la rete. Se due dispositivi vedono lo stesso insieme di operazioni, risultano identici. Questa garanzia è matematica, non una promessa di buona volontà da parte del codice.

Per raggiungere questo obiettivo, le operazioni di un CRDT devono avere tre proprietà. Sono commutativi, quindi l'ordine non cambia il risultato. Sono associativi, quindi il raggruppamento non ha importanza. E sono idempotenti, quindi applicare la stessa cosa due volte non causa danni. Le strutture che rispettano queste regole formano quello che la matematica chiama semireticolo, e da qui deriva la garanzia di convergenza.

Perché questo risolve il problema del local-first

Senza CRDT, la sincronizzazione offline richiede un arbitro. Normalmente un server che riceve tutte le versioni, applica qualsiasi regola, spesso la famigerata ultima scrittura, vince, e restituisce la verità ufficiale. Ciò ha due costi. I dati persi in sovrascrittura e la dipendenza da un punto centrale sempre online per risolvere qualsiasi divergenza.

I CRDT dissolvono questa dipendenza. Poiché la fusione è deterministica e integrata nella struttura stessa, qualsiasi dispositivo può fondersi con qualsiasi altro, in qualsiasi topologia. Due telefoni cellulari possono sincronizzarsi direttamente tramite Bluetooth, tre repliche possono adattarsi insieme in una rete e il risultato è lo stesso che con un server che coordina tutto. Il server, quando esiste, diventa solo un comodo relè, non un'autorità.

Ciò cambia la natura dell'applicazione. L'utente non attende una risposta dalla rete per vedere applicata la modifica, perché la verità locale è già valida. La sincronizzazione avviene in background e non ritorna mai dicendo "il tuo lavoro è stato scartato". È ciò che rende l'esperienza dell'app come editor collaborativi moderni così fluida, ed è il fondamento tecnico di qualsiasi architettura seria local-first.

I gusti CRDT che troverai

Dominano due stili principali. I CRDT basati sullo stato scambiano l'intera struttura tra repliche e utilizzano una funzione di unione per combinarla. Sono semplici da ragionare, ma gravano sulla rete quando i dati crescono. Basato sulle operazioni o basato sulle operazioni, propaga solo le singole modifiche, il che è più economico, ma richiede un livello di consegna delle operazioni affidabile.

Sopra questi stili c'è un catalogo di tipi già pronti. Contatori che aggiungono incrementi da più repliche senza perderne nessuno. Set che sanno mescolare addizioni e rimozioni in modo coerente, come OR-Set. E il caso più ambito, il testo sequenziale, in cui ogni carattere riceve un identificatore univoco e ordinabile in modo che gli inserimenti concorrenti non si sovrappongano. Algoritmi come Yjs e Automerge impacchettano tutto questo nelle librerie che usi senza reimplementare la teoria.

La buona notizia per i decisori architettonici è che raramente si scrive un CRDT da zero. Scegli la libreria, modelli i tuoi dati sui tipi che offre e ottieni la convergenza in regalo. Il lavoro intellettuale consiste nel mappare il dominio di queste strutture, non nel dimostrare teoremi.

Dove i CRDT brillano davvero

Sono imbattibili quando la regola di fusione è veramente neutrale, cioè quando mantenere entrambe le modifiche è sempre il comportamento corretto. Il testo collaborativo è l'esempio perfetto: se due persone digitano paragrafi diversi, vuoi entrambi i paragrafi, punto. Elenchi, bacheche kanban, note, disegni vettoriali e la maggior parte degli strumenti di produttività collaborativa rientrano in questa categoria.

Brillano anche in scenari in cui la connettività è scarsa o di natura intermittente. Applicazioni sul campo, raccolta dati in aree remote, dispositivi che trascorrono ore offline. Nelle applicazioni offline-first, CRDT è ciò che ti consente di lavorare con la totale sicurezza che nulla verrà tralasciato nella successiva sincronizzazione. La convergenza automatica è proprio la garanzia che questi contesti richiedono.

E brillano quando vuoi eliminare il server dal percorso critico. Architetture peer-to-peer, mesh locali, sincronizzazione tra dispositivi appartenenti allo stesso utente senza passare dal cloud. Tutto ciò è fattibile perché l’intelligenza della fusione risiede nei dati, non nell’infrastruttura.

Dove non sono la risposta giusta

Ecco la parte che spesso viene nascosta sotto il tappeto. I CRTD convergono verso uno stato valido, ma non necessariamente verso lo stato considerato corretto dall'azienda. La convergenza non è sinonimo di rispetto delle regole aziendali. Se due persone prenotano l'ultimo posto su un volo offline, CRDT unirà felicemente entrambe le prenotazioni e avrai una garanzia matematica di overbooking.

I conflitti che richiedono una decisione semantica non appartengono al CRDT. Equilibrio che non può essere negativo, unicità di un campo, approvazione che ne invalida un altro, qualsiasi invariante che necessiti di un “no, questo non può accadere” vuole un vero arbitro. In questi casi, la regola aziendale deve decidere il conflitto e il tentativo di spingerlo al livello dati produce bug subdoli e costosi.

C'è anche il costo della memoria e dell'archiviazione. Per garantire la convergenza, molti CRDT archiviano metadati che crescono con la cronologia delle modifiche. Rimosse lapidi degli elementi, identificatori per carattere, vettori di versione. Senza strategie di compattazione, una struttura apparentemente piccola può gonfiarsi in misura sorprendente. Non tutti i problemi diventano CRDT a buon mercato, e non tutti i problemi dovrebbero farlo.

La mia raccomandazione come CTO è pragmatica. Utilizza CRDT dove l'unione è naturalmente additiva e il guadagno di esperienza è reale. Laddove la correttezza dipende da un'invariante aziendale, accettare un arbitro, sia esso un server, una coda di comandi o un flusso di risoluzione esplicito presentato all'utente. L'errore più comune è considerare la CRDT come una soluzione miracolosa e scoprire l'overbooking nella produzione.

Se stai progettando adesso il livello di sincronizzazione di un prodotto local-first, vale la pena separare chiaramente ciò che è unificabile automaticamente da ciò che necessita del processo decisionale umano o del server prima di scegliere lo strumento. Questa divisione onesta farà risparmiare mesi di rielaborazione.

Leggi anche