Wie synchronisiert man eigentlich sinnig und gut Daten zwischen verschiedenen Endgeräten? Das ist eine Frage, die sich Entwickler:innen von Smartphone-Apps immer wieder stellen. Denn natürlich wollen die Nutzer:innen konfliktfrei alle ihre Daten nutzen können, ohne dass es dabei zu Inkonsistenten in den unterschiedlichen Datensätzen etwa auf dem Smartphone, welches vielleicht gerade offline ist und dem Tablet, mit dem sich Nutzer:innen per WLAN verbunden haben, kommt.
„Conflict-free distributed data types“ – übersetzt: Konfliktfreie verteilte Datentypen – sind dafür eine Lösung. In ihrem Vortrag „Wie man offline-fähige Produkte mit Hilfe von CRDTs entwickelt“ beschäftigten sich Dr. Lars Hupel und Lucas Dohmen von INNOQ auf dem Digitale Leute Summit 2021 intensiv mit dem Thema. Sie geben einen Überblick über diese recht neue Methode.
→ Hier findest du die komplette Aufzeichnung des Talks
- In diesem Text lernt ihr
- Wieso es ein Problem sein kann, wenn Applikationen nur noch online funktionieren
- Welche Vorzüge „Offline-first“ hat
- Was das (mathematische) Grundkonzept von Conflict-free distributed data types ist
- Welche Rolle die Fachlichkeit bei der Entscheidung für unterschiedliche Konzepte spielt
- Wie konkret sich das Konzept mit Automerge umsetzen lässt
Probleme einer üblichen Cloud-Architektur
Üblicherweise gehen Anforderungen auf dem Smartphone über einen Frontend-Monolithen, in React oder Angular, ein und werden dann über eine „Backend-for-Frontend“-Schnittstelle gesammelt und verarbeitet. Das Resultat ist, dass dann die gewünschten Services in der Cloud ausgeführt werden. Das Problem: Das bedeutet, dass bei fehlender Internetverbindung „die Anwendung kaputt ist“, beschreibt Lucas in seinem Vortrag auf dem Digitale Leute Summit 2021.
„Wir schließen damit auch grundsätzlich Menschen aus“, nämlich Nutzer:innen, die nur einen eingeschränkten Zugang zum Internet haben oder ein zu geringes Datenvolumen, um dauerhaft mit dem Internet verbunden zu sein. „Das Prinzip, dass wir etwas lokal speichern und es dann manuell synchronisieren, ist heute nur noch in wenigen Produkten vorhanden“, so Lucas.
Ein interessanter Gegensatz sei, dass die Entwickler:innen wiederum sehr gerne offline arbeiten. Bei Git zum Beispiel ist es so, dass alle Operationen zunächst lokal auf dem Rechner durchgeführt werden. Es wird hier gar nicht infrage gestellt, dass der Code nicht ständig online bearbeitet wird. Ein Prinzip, „welches für bestimmte Anwendungen eine Option sein kann“, so Lucas. Als Beispiel nennt er einen Chat: Zum direkten Dialog ist es sinnvoll, immer online zu sein. Aber vielleicht kann das Chat Archiv, in dem man noch mal etwas nachschauen möchte, genauso gut zusätzlich auch offline verfügbar sein.
Heute verwenden Nutzer:innen mehrere Geräte gleichzeitig. Auch hier sollen die Daten synchron gehalten werden, auch dann, wenn zwischendurch keine Internetverbindung besteht.
Technologie, um offline und online zu kombinieren
Anwendenden die Möglichkeit zu geben, „offline first“ zu arbeiten, aber ihnen dennoch Updates und eine Synchronisierung auf mehreren Geräten zur Verfügung zu stellen, wäre eine Lösung. Als Beispiel beschreibt Lars, wie man im Zug zum Beispiel in ein Dokument einerseits auf dem – nicht mit dem Internet verbundenen Smartphone – ein Ausrufezeichen entfernt. Und dann im nächsten Augenblick auf dem Tablett ein Komma ergänzt. „Ich muss jetzt sicherstellen, dass ich das wieder miteinander verbinden kann“, so Lars. Auch das sei ein typischer Konflikt, wie er auch in der Code-Entwicklung vorkomme, so Lars. Das Problem: Es gibt dann mehrere Versionen des gleichen Dokuments. Diese müssen gegenüber gestellt werden.
Aber wichtig ist, dass keine Daten verloren gehen, wenn man offline geht, „was ganz, ganz viele Apps nicht erfüllen“, so Lars.
Die „Conflict-free distributed data types“ – übersetzt: Konfliktfreie verteilte Datentypen – sorgen einerseits dafür, dass keine Daten verloren gehen. Sie stellen aber auch sicher, dass man immer einen gemeinsamen Zustand finden kann.
Lattices als mathematische Grundlagen für diese Aufgabe
Lattices – übersetzt Lattenzaun – ist das dahinter liegende mathematische Prinzip. Wenn man sich an einem Kreuzungspunkt in diesem Lattenzaun befindet, kann man einen bestimmten Zielpunkt immer erreichen. „Völlig egal, ob man nun rechts oder links abbiegt“, so Lars. Man gelangt über das Lattenrost immer nach oben zum Zielpunkt.
Wenn man also mit dem Beispiel „Hallo Welt“ als Startpunkt beginnt, dann gibt es eine Abbiegung nach links – etwa die Entfernung des Ausrufezeichens – und eine nach rechts – etwa die Setzung des Kommas -, „und beide Wege kann man laufen, aber irgendwie kann man sich immer wieder auf einen gemeinsamen Punkt einigen“.
Die Definition der Conflict-free distributed data types
Um das theoretische Konzept zu durchdringen, schaut Lars auf die Definition von Conflict-free distributed data types:
- Data-Types: Das sind zum Beispiel Listen, Maps und Strukturen, „also abstrakte Dinge, in denen ich Daten speichern kann“, so Lars.
- Replicated: Das bedeutet, dass die Daten auf verschiedenen Geräten/Instanzen gespeichert sein können. Das sind dann die sogenannten Replikas, „man hat dann eine Person, die das benutzt und diese hat mehrere Geräte“.
- Conflict-free: „Ich kann immer einen konsistenten Zustand herbeiführen, wenn ich die Daten irgendwann mal synchronisiere“, so Lars.
Wie ein Grow-Set funktioniert
Lars erklärt das am Beispiel der sogenannten Grow-Sets. Das Prinzip dahinter: Alles darf nur höchstens einmal vorkommen, etwa bei einer gemeinsamen Einkaufsliste, bei der eine Person Skyr und Grapefruit und die andere Person Müsli eingibt. Und herauskommen soll eine gemeinsame Einkaufsliste, auch dann, wenn einer der beiden Personen zwischendurch offline ist. „Auch das erfüllen viele Anwendungen nicht“, ergänzt Lucas. Das Grundprinzip eines Grow-Sets ist es, dass sie immer nur wachsen können.
Aber was passiert dann mit Dingen, die man wieder löschen will? So soll vielleicht das Müsli aus der Einkaufsliste wieder entfernt werden. Die Lösung sind die sogenannten „Tomb-Stones“/Grabsteine. Denn das Problem mit dem Lattenrost ist, dass man beim kompletten Löschen eines Datensatzes Datenprobleme herbeiführt
Die Lösung ist einfach: Wenn etwas entfernt werden soll, wird es nur als gelöscht markiert, es ist also ein Grabstein gesetzt. Die Daten sind aber weiterhin vorhanden. Der Nachteil daran ist, dass sich irgendwann ein riesiger „Friedhof“ bildet mit Daten, die eigentlich nicht mehr benötigt werden. Lars empfiehlt, das mit einer periodischen wirklichen Löschung von Daten zu beheben, etwa innerhalb der Datenbankadministration.
Wie ein 2P-Set funktioniert
Somit können die Daten dann zwei Eigenschaften haben: Es kann hinzugefügt worden sein und es kann gelöscht worden sein. Das ist ein sogenanntes 2-P-Set“.
Am Beispiel der Einkaufliste: Die beiden Personen haben sich darauf geeignet, dass nun Äpfel aufgenommen werden und dafür das Müsli gelöscht wird. Beim Zusammenführen der Daten erkennt die jeweilige andere Instanz, dass nicht etwa die andere Person das Müsli noch nie gesehen hat, sondern dass diese das Müsli löschen wollte. Diese Ansicht ist nur möglich, weil die Daten die zwei Zustände einnehmen können.
„Der Nachteil bei dieser Datenstruktur ist allerdings, dass etwas, was gelöscht wurde, nicht noch einmal hinzugefügt werden kann“, so Lars. „Deshalb muss ich mir jetzt über Zeit Gedanken machen.“ Es müsse jetzt geklärt werden, wer zuerst eine Veränderung vorgenommen hat.
Das Problem: Es gibt auf unterschiedlichen Geräten keine globale einheitliche Zeit, schon gar nicht, wenn die Geräte offline sind, „das kann auseinanderlaufen“. Die Lösung sind die sogenannten Vektor-Clocks. Jeder Eintrag erhält einen Zeitstempel, aber der Gesamtzeitstempel setzt sich aus allen diesen Geräte-Zeitstempeln zusammen.
Das Prinzip: Bei der Synchronisation der Daten teilen sich beide Instanzen ihren Zeitstempel mit. „Damit kann die App auch Veränderungen anzeigen, die sich widersprechen“, so Lars, etwa, wenn zum Beispiel eine der beiden Instanzen eine Zeitlang offline war.
Multivalue-Register als eine Lösung
Um das Zeitproblem richtig zu lösen, bieten sich sogenannte Multi-Value-Register an. „In einem Konfliktfall werden einfach beide Werte gespeichert“, so Lars. Anhand des Zeitstempels kann man dann erkennen, das eine Änderung auseinandergelaufen ist.
Nun muss mit diesen Konfliktfällen irgendwie umgegangen werden. Dafür gibt es dann mehrere Lösungen:
- Nachfragen: In der App wird angezeigt, dass es bei einem Eintrag einen Konflikt gibt und der Nutzer wird aufgefordert, sich zu entscheiden.
- Zusammenrechnen: Wenn in einer Offline-Zeit jemand schreibt, füge 300 Gramm Müsli zur Einkaufsliste hinzu und die andere Person fügt 500 Gramm Müsli hinzu, könnte die App daraus einfach 800 Gramm Müsli machen. Oder sie könnte sich für den höchsten Wert entscheiden.
„Anhand der Fachlichkeit muss dann entschieden werden, was die beste Lösung ist“, so Lars. Besser sei es, den Nutzer:innen eine fachlich sinnvolle Zusammenführung zu ermöglichen, als sie mit einer Fehlermeldung zu konfrontieren.
Idee zur Umsetzung mit Automerge
Eine Idee, dieses Konzept umzusetzen, ist die Open-Source-Bibliothek Automerge (JavaScript). Sie sorgt dafür, dass zwei Dinge automatisch synchronisiert werden. Dazu liefert sie JSON-Dateien aus. Man kann diese Bibliothek in existierenden Services integrieren.
Praktisch: Da diese ohnehin oft JSON-Dateien erzeugen, können diese dann mit Automerge entsprechend der „Conflict-free distributed data types“-Methode weiterverarbeitet werden. Die Bibliothek garantiert also, dass auf den unterschiedlichen Geräten immer das gleiche Ergebnis/Dokument steht. Und die Einträge werden auch auf allen Geräten in der gleichen Reihenfolge angezeigt.
Die Bibliothek Hypermerge ermöglicht das gleiche ganz ohne Internetverbindung als direkte Kommunikation zwischen verschiedenen Instanzen, also als Peer-to-Peer-Anwendung.
„Es sind sehr viele Tools vorhanden, die uns ermöglichen, offline-first mitzudenken“, so Lars. Und somit kann die Idee „Conflict-free distributed data types“ sinnvoll umgesetzt werden.
Product Jobs
Über Dr. Lars Hupel: Lars Hupel ist Senior Berater bei INNOQ in München. Er ist unter anderem Mitbegründer der Typelevel Initiative, die sich mit prinzipienbasierter Bereitstellung von Libraries beschäftigt. Als regelmäßiger Speaker ist er auch sehr aktiv in der Open-Source-Community, insbesondere in Bezug auf Scala. Lars mag es, zu programmieren, etwa in Haskell, TypeScript, Prolog und Rust.
Über Lucas Dohmen: Für Lucas Dohmen ist Programmierung, etwa in Ruby und JavaScript, ebenfalls eine wichtige Tätigkeit. Er arbeitet als Senior-Berater bei INNOQ und beschäftigt sich dort stark mit Architektur, Design und der Implementierung von Web-Applikationen, sowohl im Front- als auch im Backend. Zudem engagiert er sich auch in der Open-Source-Community, etwa bei der lokalen Initiative CoderDojo.
Auf dem Digitale Leute Summit 2021 erläuterten die beiden, warum es sinnvoll sein kann, auch auf Smartphone-Apps „offline first“ zu konzipieren – und welche Rolle dabei das Konzept der conflict-free distributed data types spielen kann.
Autor: Jörg Stroisch