- Local-First-Apps versprechen schnelle Reaktionszeiten und grundlegenden Schutz der Privatsphäre, haben in der Praxis aber die Einschränkung, dass eine echte Offline-Unterstützung sehr schwer umzusetzen ist
- Der wichtigste Grund ist die Komplexität der Synchronisierung: Wenn Daten auf mehreren Geräten gleichzeitig geändert werden, müssen sie am Ende exakt in denselben Zustand konvergieren
- Es gibt zwei große technische Herausforderungen: Unsicherheit der zeitlichen Reihenfolge und Konflikte
- Um dieses Problem zu lösen, ist ein Ansatz mit verteilten Systemdesigns wie Hybrid Logical Clocks(HLCs) und CRDTs erforderlich
- Durch die Nutzung von SQLite-basierten Erweiterungen lässt sich eine zuverlässige und einfache Synchronisierungsarchitektur bereitstellen, die auf allen Plattformen eingesetzt werden kann
Das Versprechen und die Realität von Offline-First-Apps
- Offline-First-Apps werben mit sofortiger Reaktion, standardmäßig gewährleisteter Privatsphäre und Nutzbarkeit auch in instabilen Netzwerken ohne Ladezeiten
- In der Realität setzen die meisten Apps Offline-Unterstützung nicht wirklich sauber um; meist werden Änderungen nur lokal zwischengespeichert und später bei bestehender Netzwerkverbindung übertragen
- Solche Implementierungen sind unzuverlässig und führen letztlich zu Warnmeldungen wie „Änderungen werden möglicherweise nicht gespeichert“
Die grundlegende Schwierigkeit der Synchronisierung
- Wer eine Local-First-App baut, erstellt zwangsläufig ein verteiltes System
- Mehrere Geräte können Daten offline unabhängig voneinander ändern, und wenn sie später wieder verbunden werden, müssen sie präzise in denselben Zustand konvergieren
- Dafür gibt es zwei große Herausforderungen
- Unsicherheit in der Reihenfolge von Ereignissen
- Konflikte bei denselben Daten
1. Unsicherheit bei der Ereignisreihenfolge
- Ereignisse treten auf mehreren Geräten zu unterschiedlichen Zeitpunkten auf, und je nach Reihenfolge kann sich der Zustand unterscheiden
- Beispiel: Gerät A setzt x=3, Gerät B setzt x=5 → nach getrennten Offline-Änderungen kann die Synchronisierung zu unterschiedlichen Ergebnissen führen
- Klassische zentrale Datenbanken lösen das mit starker Konsistenz, doch dafür ist globale Synchronisierung nötig, was nicht zu Local-First-Systemen passt
- Am Ende muss für jedes Ereignis auch in einer dynamischen, verteilten Umgebung eine passende Reihenfolge festgelegt werden; es wird also eine Methode benötigt, die ohne zentrale Uhr auskommt
Einführung von Hybrid Logical Clocks(HLCs)
- Hybrid Logical Clocks(HLCs) sind ein einfacher, aber effektiver Algorithmus, mit dem einzelne Geräte sich praktisch auf die Reihenfolge von Ereignissen einigen können
- HLC kombiniert physische Zeitinformationen mit einem logischen Zähler
- Zum Beispiel:
- Gerät A protokolliert um 10:00:00.100 ein Ereignis, HLC ist dann (10:00:00.100, 0)
- Gerät B empfängt die Nachricht und erhöht seinen HLC selbst dann auf (10:00:00.100, 1), wenn seine eigene Uhr nachgeht
- Dadurch lässt sich die exakte Reihenfolge von Ereignissen unabhängig von der Differenz zwischen den physischen Uhren der beiden Geräte bestimmen
2. Das Konfliktproblem
- Die korrekte Reihenfolge allein reicht nicht aus; wenn verschiedene Geräte dieselben Daten unabhängig voneinander ändern, entstehen Konflikte zwangsläufig
- Die meisten Systeme verlangen, dass Entwickler den Code zur Konfliktauflösung manuell schreiben, was Fehlerrisiken und zusätzlichen Wartungsaufwand verursacht
Einsatz von CRDTs
- Der beste Ansatz ist die Verwendung von Conflict-Free Replicated Data Types(CRDTs)
- CRDTs garantieren, dass der Zustand auf jedem Gerät am Ende immer identisch ist, unabhängig von der Reihenfolge der Synchronisierung oder mehrfacher Anwendung
- Die einfachste CRDT-Strategie ist Last-Write-Wins(LWW)
- Jeder Aktualisierung wird ein Zeitstempel zugewiesen
- Bei der Synchronisierung wird der Wert mit dem neueren Zeitstempel ausgewählt
Die Vorteile von SQLite
- Für den Aufbau von Local-First-Apps ist eine zuverlässige und leichte lokale Datenbank unverzichtbar, und SQLite ist dafür die beste Wahl
- Wird die Synchronisierung über SQLite-basierte Framework-Erweiterungen implementiert, ergeben sich folgende Vorteile
- Das Anwenden von Nachrichten ist einfach: aktuellen Wert abfragen → überschreiben, wenn der neue Zeitstempel aktueller ist → andernfalls ignorieren
- Dieser Ansatz garantiert die Konvergenz des Zustands auf allen Geräten unabhängig von der Reihenfolge der Synchronisierung
Die Bedeutung dieser Architektur
- Diese Struktur ermöglicht eine einfache und hochzuverlässige Synchronisierung
- Zuverlässigkeit ohne Datenverlust auch nach wochenlangem Offline-Betrieb
- Deterministische Eigenschaften, die immer zum endgültigen Zustand konvergieren
- Umsetzung allein mit einer leichtgewichtigen SQLite-Erweiterung ohne schwere Abhängigkeiten
- Unterstützung für alle wichtigen Plattformen wie iOS, Android, macOS, Windows, Linux und WASM
Empfehlungen für Entwickler
- Es ist sinnvoll, Ansätze zu vermeiden, die Offline-Modus nur mit einer einfachen Request-Queue „nachahmen“
- Stattdessen sollte das Konzept der Eventual Consistency akzeptiert und auf bewährte Technologien aus verteilten Systemen wie HLC und CRDT gesetzt werden
- Statt großer und komplexer Frameworks ist eine kleine Architektur ohne Abhängigkeiten vorzuziehen
- So kann eine App letztlich Vorteile wie sofortige Ausführung, Offline-Nutzbarkeit und standardmäßig gewährleistete Privatsphäre realisieren
Hinweis auf das Open-Source-Projekt SQLite-Sync
- Wer an einer sofort produktionsreifen, plattformübergreifenden Offline-First-Engine interessiert ist, kann sich die Open-Source-Erweiterung SQLite-Sync ansehen
1 Kommentare
Hacker-News-Kommentare
Cache-Controlin API-Antworten korrekt nutzt und dies auf der Netzwerkebene sauber beachtet. So kann man die Cache-Lebensdauer serverseitig ändern, ohne ein App-Update auszurollen.