1 Punkte von GN⁺ 2023-10-27 | 1 Kommentare | Auf WhatsApp teilen
  • Flawless ist eine Engine für dauerhafte Berechnungsausführung, die dafür sorgt, dass Code auch bei Ausfällen bis zum Ende weiter ausgeführt werden kann
  • Workflows werden als normale Rust-Funktionen geschrieben, aber nicht zu nativem Code, sondern zu WebAssembly kompiliert und in einer deterministischen Umgebung ausgeführt
  • Nichtdeterministische Nebenwirkungen entstehen nur an den Stellen, an denen mit der Außenwelt interagiert wird, etwa bei HTTP-Anfragen oder der Erzeugung von Zufallszahlen; Flawless speichert diese als Logs
  • Wenn die Ausführung unterbrochen wird, nutzt es die gespeicherten Logs, um denselben Zustand erneut zu erreichen, und wiederholt bereits ausgeführte Nebenwirkungen nicht
  • Entwickler können dauerhaften Zustand über Code und lokale Variablen ausdrücken, ohne jeden Zustand direkt in der Datenbank modellieren zu müssen, sodass die Ausführung auch nach einem Serverneustart ab dem Unterbrechungspunkt fortgesetzt werden kann

Ausführungsmodell: Dauerhaften Zustand im Code ausdrücken

  • Flawless ist eine Engine für dauerhafte Berechnungen, die Code bis zur Fertigstellung ausführt, selbst wenn Hardware- oder Softwareausfälle auftreten
  • Komplexe Benutzererfahrungen erfordern komplexe UI und Zustände, aber es ist schwierig, jeden Zustand direkt in einer Datenbank zu modellieren
  • Damit Nutzer ihren Fortschritt nicht verlieren, selbst wenn sie eine Seite versehentlich neu laden, benötigen moderne Anwendungen persistente Speicherung
  • Flawless verfolgt das Ziel, dauerhaften Zustand über Code und lokale Variablen zu modellieren, damit sich komplexes Anwendungsverhalten einfacher ausdrücken lässt

WebAssembly-basierte Ausführung und Wiederherstellung nach Ausfällen

  • Workflows werden als normale Rust-Funktionen geschrieben und können beliebige Logik enthalten
  • Die Funktionen werden nicht zu nativem Code, sondern zu WebAssembly kompiliert und in einer vollständig deterministischen Umgebung ausgeführt
  • Nichtdeterminismus wird nur eingeführt, wenn mit der Außenwelt interagiert wird, etwa beim Ausführen von HTTP-Anfragen oder beim Erzeugen von Zufallszahlen
  • Flawless speichert solche Logs nichtdeterministischer Nebenwirkungen und verwendet sie zur Wiederherstellung
    • Wenn die Workflow-Ausführung unterbrochen wird, wird sie erneut ausgeführt, um denselben Zustand wieder zu erreichen
    • Bereits ausgeführte Nebenwirkungen werden nicht erneut ausgeführt
    • Die zu speichernde Datenmenge wird minimiert, alles Übrige wird bei einem Ausfall bei Bedarf neu berechnet
  • Dieses Ausführungsmodell macht das Verhalten des Gesamtsystems besser beobachtbar
    • Der genaue Ausführungspfad abgeschlossener oder noch laufender Workflows kann analysiert werden
    • Dank der deterministischen Ausführungsumgebung lassen sich schwer reproduzierbare Bugs leichter behandeln
  • Entwickler können den Aufwand für Zustandsverwaltung reduzieren und sich stärker auf die Business-Logik konzentrieren
  • Selbst wenn Server für Wartungsarbeiten neu gestartet werden müssen, laufen Workflows nach dem Neustart der Flawless-Engine ab dem Punkt weiter, an dem sie angehalten wurden
  • Mit Stand vom 9. Dezember 2024 ist Flawless Beta 3 verfügbar

1 Kommentare

 
GN⁺ 2023-10-27
Hacker-News-Kommentare
  • Ich frage mich, wie Workflow-Versionierung gehandhabt wird. Das scheint mir bei Systemen wie Temporal/Cadence das schwierigste Problem zu sein

    • Ich bin der Autor. Für lang laufende oder praktisch ewig laufende Workflows scheint mir die intuitivste Lösung zu sein, Upgrades ohne Unterbrechung zu erlauben
      Das Upgrade gelingt nur, wenn sich mit dem neuen Code die vorhandenen Side-Effect-Logeinträge exakt wiedergeben lassen. Man holt mit dem neuen Code auf und lässt ihn dann einfach weiterlaufen, sobald er aufgeholt hat
      Weicht der neue Code von den bestehenden Aufzeichnungen ab, schlägt das Upgrade fehl und es wird auf den alten Code zurückgefallen. In diesem Fall muss ein Mensch eingreifen und prüfen, was schiefgelaufen ist
      Es gibt auch andere Ansätze, aber zum tatsächlichen Verstehen und Benutzen scheint mir das der einfachste zu sein. Auch während der Entwicklung kann man mit bestehenden Logs testen, ob der Code divergiert
    • Ich nehme an, es geht um das Problem, einen bereits lebenden und laufenden Workflow zu aktualisieren, ohne dass er kaputtgeht
      Ich frage mich, ob ihr mehrere Versionen gleichzeitig vorhalten wollt oder ob ihr einen Weg braucht, laufende Instanzen auf die neueste Workflow-Definition zu migrieren
    • Genau. Das ist der Bereich, den Conductor gut für einen übernimmt
  • Es fühlt sich an, als würde unser Feld inzwischen immer mehr in Richtung Architektur oder Medizin gehen. Dank solcher Techniken können wir die Bastelphase hinter uns lassen und in eine ernsthafte Engineering-Kultur eintreten
    Kurz gefragt: Wie verhindert man in solchen Systemen, dass sich die Wirkung eines DoS-Angriffs verstetigt?

    • Die Formulierung „nähert sich Architektur und Medizin an“ liest sich wie KI-generierter Unsinn
      Architektur beschäftigt sich eher mit subjektiven Urteilen unter regulatorischen Zwängen, Medizin mit empirischem Wissen, das durch Experimente validiert wird. Dass beide dem Ingenieurwesen ähneln, finde ich schwer zu begründen
    • Architektur und Medizin sind beide keine Ingenieurdisziplinen, daher verstehe ich nicht ganz, warum eine Annäherung daran ein Weg zu einer Engineering-Kultur sein soll
      Architektur ist eher eine Form von Kunst, und Fakultäten für Architektur sind an Hochschulen oft in Kunstbereichen angesiedelt
      Medizin ist wie das Ingenieurwesen eine angewandte Wissenschaft, aber keine Ingenieurdisziplin an sich
  • Ich frage mich, ob sich diese Deterministik auch auf Fließkommaberechnungen erstreckt
    Bei Multiplayer-Spielen musste der Client-Zustand regelmäßig wieder mit dem Server synchronisiert werden, weil sich durch Fließkommaberechnungen nach und nach kleine Abweichungen aufsummierten, und das war historisch gesehen ziemlich schmerzhaft

  • Ich frage mich, wo der Zustand von Side Effects gespeichert wird. Angenommen, es gibt eine AWS Lambda, die man idempotent machen möchte: Lambda hat keinen lokalen Speicher, der zwischen Ausführungen erhalten bleibt
    Wenn man nicht etwas wie ein EBS-Volume mountet, bleibt kein Zustand erhalten. Kann man also davon ausgehen, dass der Zustand in einer DB gespeichert wird?

    • Das dürfte der Teil sein, für den man zahlen wird, sobald das Produkt verfügbar ist
  • Mir gefällt die Animation, die das Kernprinzip und die Funktionsweise zeigt. Wirklich sehr gut gemacht

    • Danke. Ich habe sie selbst nur mit HTML, CSS und JavaScript gebaut und viel Mühe und Liebe hineingesteckt
      Der Code ist nicht besonders hübsch, aber die Implementierung ist intuitiv; falls du nachsehen willst, hier ist sie: https://flawless.dev/js/how-does-it-work-animation.js
    • Da externe Endpunkte viel häufiger wegen Timeouts scheitern, hätte ich auch gern gesehen, wie ein Fehlschlag im HTTP-Ausführungsschritt aussieht
  • Klingt interessant, aber ich frage mich, ob sich der Ansatz, Funktionen als mit Side Effects behaftet zu markieren, leicht fehlerfrei umsetzen lässt
    Ich nehme an, dass die Zufallszahlenerzeugung im Beispiel deshalb ein Side Effect ist, weil sie aus dem von flawless bereitgestellten Zufallszahlengenerator kommt. Wäre das auch mit einer normalen Rust-Funktion möglich gewesen?
    Ich gehe davon aus, dass es auch so etwas wie einen Test-Harness gibt, mit dem Entwickler ihre Workflows prüfen können

    • Ich bin der Autor von flawless. Mit WebAssembly lässt sich das grundsätzlich ziemlich sicher machen
      WebAssembly verlangt, dass Host-Aufrufe im Modul explizit deklariert werden. Wenn man andere Host-Aufrufe verwenden will, die flawless nicht bereitstellt, kann das Modul nicht instanziiert werden
      Im WebAssembly-Ökosystem laufen auch mehrere Standardisierungsarbeiten. Wenn man zum Beispiel das Rust-Crate rand verwendet und nach WebAssembly kompiliert, nutzt es zur Zufallszahlenerzeugung WASI-Host-Funktionen
      Während wir darauf warten, dass wasi, wasi-http usw. standardisiert werden, stellen wir vorerst unsere eigene Schnittstelle bereit
      Natürlich gibt es auch große Nachteile. Nicht jeder Rust-Code lässt sich nach WebAssembly kompilieren. Trotzdem halte ich einen Default-Deny-Ansatz, der garantiert, dass niemals unbeabsichtigte Side Effects entstehen, für besser
    • Wenn sowohl der Zufallszahlengenerator als auch HTTP-Anfragen unter dem Namespace flawless liegen, sieht es so aus, als würde man statt Zugriff auf das gesamte Rust-Ökosystem eher etwas wie std::flawless verwenden
      Der Harness wird wohl die meisten Probleme lösen, aber ich frage mich, wie viele Funktionen sich tatsächlich abbilden lassen
      Bisher wirkt es eher wie eine Scripting-Runtime mit Rust
  • Sieht aus wie eine Rust-Alternative zu Temporal, die WASM als Runtime nutzt. Gefällt mir.
    Ich bin Gründer von windmill.dev, und wir arbeiten ebenfalls an einer in Rust geschriebenen Durable Engine. Allerdings ist sie deutlich weniger elegant. Wir teilen Workflows in explizite Schritte in Python/TypeScript/Go/Bash auf, starten beim letzten Schritt neu, um an einem unvollendeten Schritt fortzusetzen, und speichern das Ergebnis jedes Schritts dauerhaft als jsonb in einer Postgres-Datenbank
    Die Anwendungsfälle sind eindeutig unterschiedlich, und flawless wirkt, wie auf der Website beschrieben, sehr leichtgewichtig, sodass es sich auch zur Modellierung von UI-Flow-Zuständen eignen und auf kleinen Servern bis auf Millionen skalieren könnte
    Fantastisch. Hoffentlich wird Rust eines Tages alle verteilten Systeme antreiben

  • „Jedes hinreichend komplexe nebenläufige Programm, das in einer anderen Sprache geschrieben ist, enthält als Ad-hoc-Lösung eine inoffizielle, fehlerhafte und langsame Halbimplementierung von Erlang“ — Virdings erstes Gesetz der Programmierung

    • Das Ziel von Flawless scheint zu sein, den Zwischenzustand eines Workflows zu speichern und ihn bei einem Fehler ab der Mitte neu zu starten
      Das ist ziemlich anders als Erlang. Erlang wurde hauptsächlich dafür entwickelt, Software auch dann entwickeln zu können, wenn nur Prototyp-Hardware mit vielen Defekten verfügbar war
      Für mich wirken die beiden Ansätze geradezu gegensätzlich. Flawless kann in einer Schleife festhängen, um einen Workflow zu Ende zu bringen, der in der Mitte abstürzt, während Erlang bereitwillig 50 % des Traffics verwerfen kann, der auf einen Hardwarefehler trifft
    • Im ersten Blogbeitrag wird Erlang/OTP erwähnt: https://flawless.dev/essays/when-letting-it-crash-is-not-eno...
    • Das scheint überhaupt nicht dasselbe Problem wie Erlang zu lösen
      Erlang löst es, indem persistenter Zustand praktisch eliminiert wird. Fast der gesamte Zustand befindet sich in Message Queues oder externen Datenbanken
      Flawless scheint das Problem mit einer Technik zu lösen, die dem Dateisystem-Journaling ähnelt, aber nicht ganz dasselbe ist. Beim Ausführen von Side Effects wird protokolliert
      Dateisystem-Journaling dient dazu, nach einem Crash erneut auszuführen; hier scheint das Ziel zu sein, eine erneute Ausführung überflüssig zu machen
      Es ist nicht klar, wie gut das in welchen Bereichen passt, aber die Überschneidung mit den Bereichen, in denen Erlang gut passt, scheint nicht vollständig zu sein
    • https://codesync.global/media/erlangrt-a-beam-vm-reimplement...
    • Dann braucht ein fehlerhaftes Programm mit einer inoffiziellen Spezifikation offenbar nur schnell genug zu sein, um die Erwartungen zu übertreffen
  • Sehr cool. In der WASM-Spiel-Runtime Ambient gibt es ein ähnliches Problem. Es gibt konkurrierende Prozesse, und Interaktionen müssen möglicherweise erneut versucht werden, daher ist der hier gezeigte Ansatz interessant
    Mich würde aber die Beziehung zu Lunatic interessieren. Wird an Lunatic noch gearbeitet, ist das hier ein Nebenprojekt oder etwas völlig Eigenständiges?
    https://lunatic.solutions/

    • Gute Beobachtung. Das hier stammt von Bernard Kolobara, dem CEO und Mitgründer von Lunatic
      https://kolobara.com
  • „Stell dir vor, du startest eine beliebige Berechnung, und das System garantiert, dass sie bis zum Abschluss läuft und jede Operation genau einmal ausgeführt wird“
    Ich frage mich, wie das garantiert werden soll. Ist Exactly-once Delivery in verteilten Systemen nicht unmöglich?

    • Mit Idempotenzschlüsseln auf einem At-least-once-Delivery-System lässt sich Exactly-once-Verarbeitung erreichen
    • Falls du das CAP-Theorem meinst, braucht das etwas Zusatzkontext: https://www.infoq.com/articles/cap-twelve-years-later-how-th...
    • Unmöglich ist es nicht. Man muss im CAP-Theorem nur CP wählen, also auf Verfügbarkeit verzichten
      Wenn das System Fortschritte machen kann, werden Nachrichten genau einmal zugestellt
      Falls du einen Existenzbeweis brauchst: NFSv3 hat das schon in den 1980er Jahren zum Laufen gebracht. Ich weiß allerdings nicht, ob es das erste war
    • Im Artikel steht „Stell dir vor“. Ich weiß nicht, wo die zitierte Passage herkommt, und ich kann diesen Satz im Originaltext ebenfalls nicht finden