3 Punkte von GN⁺ 5 시간 전 | Noch keine Kommentare. | Auf WhatsApp teilen
  • Noch vor wenigen Jahren war die Echtzeit-Synchronisierung von Multiplayer-Daten eines der schwierigsten Probleme und erforderte Spezialist:innen sowie Investitionen auf Unternehmensniveau. Heute lässt sich jedoch schon mit einem einzigen npm install selbst in Hobbyprojekten eine Multiplayer-UI umsetzen
  • Automerge ist ein Werkzeug zum Aufbau von Datenmodellen mit Local-first, Multiplayer-Sicherheit und Versionsverwaltung. Es verarbeitet Datenpersistenz, Verlaufspflege, Broadcasts an Mitwirkende und Konfliktauflösung automatisch, ohne dass sich die UI darum kümmern muss – in einer Arbeitsweise, die dem React-useState-Muster ähnelt
  • Am Beispiel des browserbasierten Multiplayer-Audio-Editors Ducking zeigt sich, dass es entscheidend ist, das Datenmodell so zu entwerfen, dass es sich natürlich auf CRDT-Operationen abbilden lässt
  • In Fällen wie dem Umordnen von Listen, für die Automerge keine Garantien bietet, müssen Anwendungslogik auf Applikationsebene stärkere Invarianten selbst implementieren
  • Die zentrale Bedeutung liegt darin, dass kollaboratives Editieren in Echtzeit – früher Industrie-Magie – heute frei auch in kleinen Apps für wenige Menschen eingesetzt werden kann

Hintergrund — das Projekt Ducking

  • In den letzten Monaten entstand mit Ducking für den Podcast der Partnerin ein browserbasierter Multiplayer-Audio-Editor
  • Es wirkte seltsam, dass Audiobearbeitung noch immer bei 20 Jahre alten Desktop-Apps für Einzelpersonen und dem Austausch von Dateien stehengeblieben ist
    • Während eine Person Clips bearbeitet, sollte eine andere das Transkript korrigieren oder EQ-Einstellungen anpassen können – also ein kollaborativer Workflow wie in Google Docs oder Figma
    • Dazu kommen moderne Kollaborationswerkzeuge wie Kommentare, Verlauf und Änderungsverfolgung
  • Der im vorigen Artikel behandelte ungewöhnliche UI-Entwurf und das Audio-Layout-Modell machten einen einzelnen Editor zwar besser, aber das eigentliche Ziel war ein kollaborativerer Workflow

Wie Automerge funktioniert

  • Alle Daten in Ducking außer den Audio-Blobs werden in Automerge-Dokumenten gespeichert
  • Das Kernmuster ist für React-Entwickler:innen vertraut: Daten werden per Hook geladen und gerendert, anschließend werden asynchrone Änderungsanfragen dispatcht; nach einer Datenänderung löst der Hook ein Re-Render aus
    • Beispiel mit dem Hook useDocument: const [doc, changeDoc] = useDocument<Episode>(docUrl) lädt das Dokument, und bei Änderungen eines Eingabewerts wird mit changeDoc((d) => { d.title = e.target.value }) aktualisiert
  • Datenaktualisierungen sehen imperativ aus, unterscheiden sich aber von nativen JS-Objekten und -Arrays
    • Es gibt weniger Methoden, die Objekte werden nicht sofort mutiert, und Automerge fängt Änderungen selbst ab und wandelt sie in Einträge der Changelist des Dokumentverlaufs um
  • Für einfache Einsatzzwecke erledigt Automerge vieles automatisch, aber es ist keine Magie. Seine Invarianten stimmen nicht immer mit der gewünschten fachlichen Bedeutung überein, daher ist sorgfältiges Datenmodell-Design wichtig
    • Die meisten semantisch zusammenhängenden Nutzeraktionen sollten jeweils einer einzelnen von Automerge bereitgestellten Operation entsprechen
    • Getrennte Nutzeraktionen auf zusammenhängenden Daten sollten sich aus Sicht der Invarianten dieser Automerge-Operation natürlich auflösen lassen
    • Kanonische gespeicherte Daten und durch Berechnung abgeleitete Daten sollten klar getrennt werden

Datenmodellierung für Multiplayer

  • Im Datenmodell von Ducking ist ein Clip ein Fenster, das einen Teil einer unveränderlichen zugrunde liegenden Audioquelle abspielt; er definiert den Abspielbereich, wendet Effekte an und belegt Platz auf der Timeline
    • Der häufigste Effekt ist, dass ein Clip die Lautstärke des zugrunde liegenden Audios über die Zeit verändert, etwa für Crossfades oder zur Rauschunterdrückung
  • Anfangs hatte jeder Clip eine Liste von Lautstärkepegeln mit Zeitindizes relativ zum Beginn des Clips, doch das führte zu Problemen, weil sich die meisten Lautstärkeänderungen eigentlich auf das zugrunde liegende Audio und nicht auf den Clip beziehen
    • Wird der Startzeitpunkt eines Clips etwas nach vorn verschoben, wirken alle Lautstärkeänderungen plötzlich auf einen anderen Teil des Audios
    • Code zu schreiben, der bei einer Änderung des Clip-Starts alle Lautstärke-Zeitstempel aktualisiert, ist eine schlechte Wahl
  • Wenn zwei Mitarbeitende gleichzeitig den Startzeitpunkt eines Clips bearbeiten, umfasst jede Änderung sowohl den Startzeitpunkt als auch alle Zeitstempel der Lautstärkeautomation
    • Automerge kennt die kausale Beziehung zwischen diesen Änderungen nicht, sodass sie beim Mergen chaotisch aufgelöst werden können
    • Das ist ein typisches Problem, wenn eine semantisch einheitliche Aktion mehrere persistente Daten auf eine Weise aktualisieren will, deren Kausalität das CRDT nicht versteht
  • Die Lösung bestand darin, die Audiodaten für Effekte nicht mehr am Clip, sondern im Zeitbezug des zugrunde liegenden Audios zu speichern
    • Änderungen an Start oder Länge des Clips erfordern dann keine Updates mehr; wenn mehrere Editoren Startzeit, Lautstärkeautomation oder andere Effekte ändern, sind diese Änderungen unabhängiger und lassen sich mit höherer Wahrscheinlichkeit korrekt zusammenführen
  • Der Unterschied zwischen Singleplayer- und Multiplayer-UI
    • In einer Singleplayer-UI kann man oft beim bestehenden Datenmodell bleiben und Schreibvorgänge durch zusätzliche Berechnungen ergänzen
    • In einer Multiplayer-UI ist es viel üblicher, das Datenmodell zu migrieren, damit alle persistenten Daten orthogonal zueinander bleiben
  • Dadurch entsteht eine starke Präferenz für vereinfachtes Schreiben und Berechnung beim Lesen, um Automerges automatisches Merging maximal auszunutzen
  • Hinweise zu Datenmigrationen
    • Es hilft, früh Zeit zu investieren und das Migrieren von Datenformaten im Build-Prozess einzuüben, damit die erste große Migration nicht abschreckt
    • Es gibt verschiedene Muster, etwa Verarbeitung beim Lesen auf dem Client oder Batch-Upgrades auf dem Server
    • Praktische Invarianten, mit denen sich vor und nach der Migration Gleichheit prüfen lässt, erleichtern die Arbeit stark
    • Bei Ducking werden vor und nach der Migration die Audiodaten aller Projekte exportiert und per Audio-Fingerprint auf Änderungen geprüft, sodass sich auch große Schema-Änderungen ohne Angst ausrollen lassen

Umordnen von Listen implementieren

  • Manchmal müssen für Garantien, die Automerge nicht liefert, stärkere Invarianten in der Anwendungslogik selbst implementiert werden
  • Das Problem trat bei der Umsetzung der magnetic timeline in Ducking auf, also der sortierten Liste der abzuspielenden Clips
    • Automerge bietet Array-Operationen zum Löschen und Einfügen per Index, aber keine Operation zum atomaren Umordnen bestehender Elemente
  • Es gibt bekannte Lösungsansätze
    • Martin Kleppmann hat ein Paper zu atomaren Reorder-Operationen für Listen veröffentlicht
    • Zusammen mit Liangrun Da erschien außerdem das Paper „Extending JSON CRDTs with Move Operations“
    • Es gibt auch einen Draft-PR zur Integration in Automerge, der aber noch nicht gemergt wurde
  • Das Problem mit einfacher Neuordnung
    • Ein Objekt wird am aktuellen Index gelöscht und am Zielindex wieder eingefügt
    • Selbst wenn man die Invarianten dieser beiden Operationen zusammennimmt, ist damit nicht die gewünschte Invariante garantiert, dass ein Objekt bei vielen gleichzeitigen Umordnungen genau einmal in der Liste vorkommt
    • Bei mehreren gleichzeitigen Lösch- und Einfügevorgängen kann dasselbe Objekt an mehreren Stellen der Liste erscheinen. Wenn Alice und Bob etwa B jeweils per delete+insert verschieben, werden die beiden Löschungen zu einem Tombstone zusammengeführt, die beiden Einfügungen erzeugen jedoch jeweils neue Elemente – B erscheint also doppelt
  • Umsetzung der „genau einmal“-Invariante auf Applikationsebene
    • Wenn ein Clip in die Timeline eingefügt wird, erhält er eine semantic id
    • Beim Umordnen werden wie oben Lösch- und Einfügeoperationen ausgelöst
    • Beim Lesen scannt die Anwendung nach Duplikaten mit derselben semantic id, wählt willkürlich das erste nicht gelöschte Element und ignoriert die übrigen
    • So erscheint jedes Objekt nur einmal in der Liste, und alle Leser kommen immer zum selben Endzustand
  • Das Umordnen von Listen war in Ducking die einzige Operation, die Automerge nicht bereitstellte; sobald der PR gemergt ist, dürfte die Logik auf Anwendungsebene entfallen

Dokumentverlauf (Document history)

  • Eine gute Multiplayer-UI braucht Werkzeuge zur Verlaufspflege: Mitwirkende wollen sehen, was sich während ihrer Abwesenheit geändert hat, Kommentare zu Diffs hinterlassen, ältere Versionen vergleichen und Rollbacks durchführen
  • Automerge verfolgt den Versionsverlauf von Dokumenten und stellt hervorragende Grundbausteine für Verlauf und Vergleich bereit
    • Wie diese Informationen sichtbar gemacht werden und welche Konzepte Nutzer:innen sehen, entscheidet jedoch die Anwendung selbst
  • Empfohlen werden die Patchwork lab notes von Ink & Switch
    • Besonders interessant sind die Arbeit daran, Branches für Nutzer:innen sichtbar zu machen, und die Arbeit an universellen Kommentaren
  • Ducking hat sich für ein relativ einfaches Kollaborations- und Verlaufsmodell entschieden
    • Einen linearen Versionsverlauf mit benannten checkpoints, wobei Checkpoints die Gruppierungseinheit für Änderungen sowie die Einheit für Diskussion, Diff und Rollback bilden
    • Kommentar-Threads, die sich mit einem bestimmten Punkt im Audio, einem Bereich des Transkripts oder einem Versions-Checkpoint verknüpfen lassen
  • Bisher gab es noch keinen ausreichenden Grund für Branches, aber künftig könnten sie nützlich werden

Text und Marks

  • Rich-Text ist besonders knifflig, wenn auf editierbaren Text zusätzliche eigene Logik aufgesetzt werden soll
    • Empfehlenswert ist das Peritext-Paper, das die Schwierigkeiten von Rich Text und Multiplayer-Software allgemein beschreibt
  • Das Rich-Text-Schema von Automerge enthält marks – Anmerkungen, die auf Textbereiche angewendet werden und auch während Textbearbeitungen konsistent bleiben
    • Meist werden sie für Formatierungen wie Fett oder Kursiv verwendet, es lassen sich aber auch anwendungsspezifische eigene Marks anlegen
  • Ducking nutzt benutzerdefinierte Marks auf zwei Arten
    • Zum Nachverfolgen von Transkriptbereichen, auf die sich Kommentar-Threads beziehen
    • Zum Nachverfolgen von Zeitstempeln von Wörtern im Transkript, während Bearbeitungen weiterhin möglich bleiben
      • Der Transkriptionsdienst speichert das Transkript in Automerge als Rich-Text-Objekt, bei dem jedes Wort per Mark mit Timing-Informationen versehen ist
      • Wird nur ein einzelner Tippfehler in einem Wort korrigiert, bleibt der Mark erhalten und damit die gesamte Timing-Information
      • Wird ein ganzer Satz geändert, verschwinden manche Marks in der Mitte, aber die Marks am Satzanfang und -ende bleiben erhalten, sodass zumindest grobe Timing-Informationen erhalten bleiben
  • Eine Einschränkung von Marks ist, dass ihre Daten nur einfache Werte sein dürfen – üblicherweise Strings – und nicht multiplayer-fähig gemergt werden
    • Kleine, unveränderliche Daten wie Transkript-Timing-Informationen werden daher als JSON in einen String serialisiert
    • Bei komplexeren oder veränderlichen Daten wie Kommentar-Threads wird im Mark nur die id gespeichert, während die eigentlichen Daten an anderer Stelle im Dokument liegen
  • Marks bieten eine hervorragende Grundlage, um Anwendungsfunktionen auf Multiplayer-Rich-Text aufzubauen

Nächster Artikel — Aufbau der Serie

  • Dieser Artikel ist Teil 2 einer dreiteiligen Reihe über den Bau von Ducking
    • Teil 1: Erklärung des ungewöhnlichen UI-Designs der Software
    • Teil 2 (dieser Artikel): eine Empfehlung, sich Automerge anzusehen, und der Hinweis, dass Multiplayer-Hobbyprojekte heute realistisch umsetzbar sind
    • Ein geplanter letzter Teil 3: ein Rückblick auf die Erfahrungen beim Bau von Ducking
  • Hinweise zum geplanten dritten Teil
    • LLM-Unterstützung wurde nicht genutzt, um die Arbeit zu verstärken, sondern um mehr Zeit für Skizzen und Hängemattenzeit zu schaffen
    • Die Freude daran, narrowcast software zu bauen – also Software, die nur wenige Menschen glücklich machen muss

Erwartete Fragen

Was ist mit den Audiodaten?

  • Alle Multiplayer-Daten werden in Automerge gespeichert, aber die zugrunde liegenden Audio-Blobs werden für schnelle Wiedergabe nicht dort abgelegt und müssen separat behandelt werden
  • Das Ziel ist, dass neue Mitwirkende nach dem Laden der Seite innerhalb von 4 Sekunden mit Hören und Bearbeiten beginnen können – schneller als das Starten einer Desktop-App und deutlich schneller als der Download kompletter Projektdateien
    • Eine einstündige Episode kann, basierend auf vier Stunden hochwertiger Studioaufnahmen plus Effekten und Hintergrundmusik, auf rund 1 Gigabyte Audio beruhen
  • Um schnelle Cold Starts zu ermöglichen, erledigt der Audiodienst beim Upload mehrere Aufgaben
    • Sicherung des Original-Audios
    • Transkription der Sprache für die Transkriptansicht
    • Erzeugung von Waveforms für die Timeline-Ansicht
    • Aufteilung in kurze Fenster, damit Clients meist nur ein oder zwei kleine Ausschnitte laden müssen, wenn von 40 Minuten Aufnahme nur 1 Minute verwendet wird
    • Transkodierung dieser Ausschnitte in komprimierte Formate, damit sofort abspielbare verlustbehaftete Versionen verfügbar sind, während hochqualitatives Audio im Hintergrund heruntergeladen wird
  • Die UI-Datenschicht verwaltet entsprechend der Nutzerabsicht das Laden einer schnellen Version der unmittelbar benötigten Daten sowie der hochwertigen Version des tatsächlich verwendeten Audiomaterials
    • Die IndexedDB API des Browsers ist nützlich für mehrstufiges Caching und content-addressable Speicherung; mit automatischer Eviction bleibt gespeichert, was gebraucht wird, und Verschwendetes verschwindet
  • Wenn diese gesamte Verarbeitung und das lokale Caching abgeschlossen sind, kann die restliche UI von schnellem Random Access auf Audio ausgehen und sich ganz auf den Editier-Workflow konzentrieren

Warum keine Local-first-App, sondern Server plus Browser-UI?

  • Bevorzugt werden Local-first-Apps wie Obsidian, die vollständig ohne Server funktionieren – besonders in einer Form, die zugleich einen verlässlichen Exit-Pfad und ein cloudbasiertes Bezahlangebot bietet
  • Ursprünglich begann das Projekt mit der Option einer Tauri-App mit lokalem Dateisystemspeicher und optionaler Server-Synchronisierung
    • Die UI wurde auf Basis einer Datenschnittstelle gebaut, die sich sowohl vom Server als auch von einer lokalen App bereitstellen ließe
    • Das sollte als Sicherheitsmaßnahme dienen, damit spätere Finanzierung nicht in Richtung stärkerer Monetarisierung per Lock-in verführt
  • Danach stellte sich heraus, dass es sich nicht um ein SaaS, sondern einfach um etwas für die Partnerin und ein paar Freund:innen handeln sollte
    • Der Anreiz, damit schlecht umzugehen, fiel weg, und bei niedrigen laufenden Kosten wurde die einfachste Lösung gewählt
  • Als Cold Starts von etwa 3 Sekunden erreicht waren, wollte niemand mehr Zeit mit dem Download und der Installation einer nativen App verschwenden
  • Die Hoffnung ist, dass Audio-Apps aus ihrer heutigen Desktop-only-Welt direkt in eine Local-first-Welt mit Synchronisierungsoptionen springen und so die dazwischenliegenden 10 bis 20 Jahre SaaS-Lock-in überspringen

Ist Automerge sicher und web-scale? Sollte ein Startup es einsetzen?

  • Die ehrliche und fröhliche Antwort lautet: keine Ahnung – nicht als Abwehr, sondern ganz wörtlich
  • Als der Autor in dieses Feld kam, war konfliktfreies Multiplayer-Editieren in Echtzeit noch Magie. Vor 10 Jahren gab es für bestimmte Probleme bekannte Lösungen, aber sie erforderten finanzierte Teams und Expertise aus mehreren Disziplinen
    • Heute reicht eine einzelne Dependency, um meist recht intuitiv eine UI zu bauen und mit Freund:innen in Echtzeit zusammenzuarbeiten
  • In Sachen Sicherheit ist Ducking derzeit durch begrenzten Netzwerkzugang und einen Autorisierungsschritt beim Aufbau einer WebSocket-Verbindung zum Automerge-Server geschützt
    • Nutzer:innen können keine Projekte entdecken oder bearbeiten, zu denen sie nicht eingeladen wurden
    • Die Zuordnung von Bearbeitungen und Kommentaren zu einzelnen Personen ist nur teilweise abgesichert und basiert darauf, dass Freund:innen nichts Böses tun
    • Feingranulare Berechtigungen wie nur kommentieren statt editieren, nur Teile eines Projekts bearbeiten oder kontrollierte Auffindbarkeit erfordern sorgfältige Designarbeit
  • Keyhive, an dem Ink & Switch arbeitet, bietet ein kryptografisch sicheres, capability-basiertes Zugriffsmodell
    • Damit ließen sich Automerge-Apps auch für nicht vertrauenswürdige Nutzer:innen leichter öffentlich teilen, aber es ist noch nicht fertig

Ist Automerge besser?

  • Eine andere Lösung in diesem Bereich ist Yjs, aber welche davon besser passt, lässt sich nicht pauschal beantworten
  • Der unveränderte Rat lautet
    • Das Problem gründlich durchdenken, grobe Überschlagsrechnungen zu den Grenzen machen, Prototypen mit mehreren Alternativen bauen und ehrlich anerkennen, dass das eigene Problem womöglich gar nicht so schwierig ist und vielleicht keine modernste High-End-Lösung braucht
  • Im Fall von Ducking zeigten schnelles Prototyping und das Studium der Dokumentation, dass Automerge für diesen Zweck ausreichend ausgereift und performant ist
  • Noch wichtiger ist die ästhetische Anziehungskraft des Ink-&-Switch-Ökosystems
    • Automerge ist nicht bloß eine Engine für Synchronisierung und Versionsverwaltung, sondern Teil einer größeren Vision von Software, die sicherer, kollaborativer, flexibler, angenehmer und persönlicher ist
    • Verbunden ist damit die Hoffnung auf den Erfolg von Keyhive und ähnlichen Projekten – und auf die Verbreitung kleiner, magischer Software für wenige Menschen

Noch keine Kommentare.

Noch keine Kommentare.