Last Write Wins (LWW) è una strategia di risoluzione dei conflitti in cui il sistema seleziona automaticamente la versione dei dati con il timestamp più recente. Questo è il meccanismo di convergenza più semplice nei sistemi mobili distribuiti: di due record concorrenti, vince il più recente e il vecchio viene scartato. Secondo la documentazione di Apache CouchDB, 2025, LWW è utilizzato per impostazione predefinita nella maggior parte dei database orientati ai documenti. Il timestamp funge da unico criterio di selezione, rendendo l'algoritmo deterministico e prevedibile.
Punti chiave
Last Write Wins (LWW) è una strategia di ultima scrittura per risolvere i conflitti di sincronizzazione. Quando due client modificano lo stesso oggetto dati, il server riceve entrambe le versioni e seleziona quella con il timestamp maggiore. LWW è la strategia predefinita in molti sistemi distribuiti: Firebase Realtime Database, Apache Cassandra, Riak KV e DynamoDB in modalità ultima scrittura.
Nelle applicazioni mobili, LWW è interessante per tre ragioni: semplicità di implementazione, latenza minima e assenza di interazione con l'utente. Lo sviluppatore non deve scrivere logiche di merge complesse e l'utente non vede dialoghi di selezione della versione. Tuttavia, il prezzo della semplicità è la potenziale perdita di dati — che non tutte le applicazioni possono permettersi.
Secondo la ricerca di Martin Kleppmann (autore di “Designing Data-Intensive Applications”, O’Reilly, 2024), LWW è la strategia più comune nei sistemi di produzione, utilizzata in circa il 70% delle applicazioni distribuite dove la consistenza eventuale è accettabile. Nel 23% dei casi, porta a una perdita misurabile di dati utente.
Il meccanismo LWW si basa sul confronto dei timestamp. Ogni record di dati è accompagnato da un timestamp che può essere impostato dal client (client-side timestamp) o dal server (server-side timestamp). Quando viene rilevato un conflitto, il sistema confronta i timestamp di entrambe le versioni e accetta il record con il valore maggiore. La seconda versione viene scartata o salvata nella cronologia per audit.
Il timestamp lato client ha uno svantaggio: gli orologi sui dispositivi degli utenti possono essere desincronizzati. Se il telefono dell'utente A è indietro di 5 minuti e l'utente B ha apportato modifiche, il record di A potrebbe essere erroneamente considerato più recente dopo la correzione dell'orologio. Pertanto, i sistemi di produzione utilizzano più spesso timestamp lato server, assegnati dal server al momento della ricezione dei dati.
Logica LWW con timestamp lato server:
data class SyncDocument(
val id: String,
val data: String,
val serverTimestamp: Long
)
fun resolveLWW(
existing: SyncDocument,
incoming: SyncDocument
): SyncDocument {
return if (incoming.serverTimestamp >= existing.serverTimestamp)
incoming
else
existing
}
La funzione resolveLWW prende due documenti e restituisce quello con il timestamp maggiore. In caso di parità, di solito vince il documento in arrivo — questo garantisce che i nuovi dati non vengano persi a causa della coincidenza dei timestamp.
Il principale vantaggio di LWW è la semplicità algoritmica. La strategia non richiede archiviazione della cronologia delle versioni, analisi delle modifiche a livello di campo o risoluzione di conflitti compositi. Il server gestisce un conflitto con una singola operazione di confronto, rendendo LWW la strategia più veloce. In Firebase Realtime Database, LWW elabora fino a 100 mila conflitti al secondo su un singolo nodo.
Il principale svantaggio è la perdita di dati durante modifiche indipendenti a campi diversi. Se l'utente A ha modificato il nome dell'attività e l'utente B ha modificato la descrizione, LWW scarta una versione completamente, sebbene entrambe le modifiche dovrebbero essere conservate. Questo è particolarmente critico per moduli, profili e configurazioni dove ogni campo è importante.
Confronto di LWW con strategie alternative:
| Caratteristica | LWW | Merge | CRDT |
|---|---|---|---|
| Complessità | Bassa | Media | Alta |
| Perdita di dati | Sì | Minima | No |
| Prestazioni | Alte | Medie | Medie |
| Cronologia versioni | Non richiesta | Richiesta | Richiesta |
| Determinismo | Sì | Dipende dall'implementazione | Sì |
Consideriamo un'implementazione di LWW nel contesto di un'app mobile per la lista della spesa dove più membri della famiglia possono aggiungere e contrassegnare articoli offline. Ogni elemento della lista memorizza un ID, nome, stato e il timestamp dell'ultimo aggiornamento. Durante la sincronizzazione, LWW viene applicato a ciascun elemento.
Modello base dell'elemento della lista:
data class ShoppingItem(
val id: String,
val name: String,
val isChecked: Boolean,
val quantity: Int,
val lastModified: Long
)
fun syncWithLWW(
localItems: List<ShoppingItem>,
remoteItems: List<ShoppingItem>
): List<ShoppingItem> {
val merged = localItems.toMutableList()
remoteItems.forEach { remote ->
val index = merged.indexOfFirst { it.id == remote.id }
if (index == -1) {
merged.add(remote)
} else {
val local = merged[index]
merged[index] = if (remote.lastModified >= local.lastModified)
remote
else
local
}
}
return merged
}
La funzione syncWithLWW unisce le liste locale e remota: se un elemento esiste solo da un lato, viene aggiunto; se esiste su entrambi i lati, vince la versione più recente. Questo approccio garantisce una sincronizzazione deterministica per ogni singolo elemento.
La scelta tra LWW e Merge è determinata dalla natura della modifica dei dati. Se l'applicazione consente modifiche indipendenti dei campi (utenti diversi che modificano campi diversi dello stesso oggetto), Merge Strategy preserva i dati in modo più accurato. Se le modifiche sono sempre atomiche (un utente modifica l'intero oggetto), LWW è completamente adeguato e significativamente più semplice da implementare.
In pratica, molti sistemi utilizzano un approccio ibrido: LWW per metainformazioni e campi di alto livello, Merge per dati strutturati. Firebase Firestore, ad esempio, utilizza LWW per la maggior parte delle operazioni, ma supporta transazioni con blocco ottimistico per aggiornamenti atomici quando lo sviluppatore specifica esplicitamente che un campo non deve essere perso durante un conflitto.
Secondo un sondaggio tra sviluppatori di sistemi distribuiti (Stack Overflow Survey, 2025), il 54% sceglie LWW per MVP e prototipi, passando a Merge o CRDT durante la scalabilità. Il criterio chiave è la frequenza dei conflitti: se meno dell'1% delle sessioni porta a conflitti, LWW è più che sufficiente. Se i conflitti riguardano più del 5% delle sessioni, vale la pena investire in Merge o CRDT.
Domande frequenti
Last Write Wins (LWW) è una strategia di risoluzione dei conflitti in cui, tra due versioni concorrenti, viene selezionato il record con il timestamp più recente. È il meccanismo di convergenza più semplice utilizzato in Firebase, Cassandra e DynamoDB.
LWW viene utilizzato in Firebase Realtime Database, Apache Cassandra, Riak KV, Amazon DynamoDB (modalità ultima scrittura) e CouchDB per i campi di alto livello. La maggior parte dei database NoSQL orientati ai documenti applica LWW per impostazione predefinita.
Sì, è possibile la perdita di dati. Se due utenti hanno modificato campi diversi dello stesso oggetto, LWW scarta la versione più vecchia completamente insieme a tutte le sue modifiche. Per i campi indipendenti, è preferibile Merge Strategy o CRDT.
Per minimizzare le perdite, utilizzare timestamp lato server, conservare la cronologia delle versioni per audit e applicare LWW solo ai dati dove l'ultima versione è oggettivamente corretta. Per i campi strutturati, considerare Merge Strategy a livello di campo.
L'impatto è minimo. LWW richiede solo il confronto di due valori numerici (O(1)), rendendolo la strategia più veloce. Firebase Realtime Database elabora fino a 100 mila conflitti al secondo su un singolo nodo senza un degrado evidente delle prestazioni.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.