Flawless – Engine für dauerhafte Ausführung in Rust
(flawless.dev)- 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
Hacker-News-Kommentare
Ich frage mich, wie Workflow-Versionierung gehandhabt wird. Das scheint mir bei Systemen wie Temporal/Cadence das schwierigste Problem zu sein
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 frage mich, ob ihr mehrere Versionen gleichzeitig vorhalten wollt oder ob ihr einen Weg braucht, laufende Instanzen auf die neueste Workflow-Definition zu migrieren
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?
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 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
Siehe https://webassembly.org/docs/faq/#why-is-there-no-fast-math-... und https://github.com/WebAssembly/design/blob/main/Nondetermini...
https://asawicki.info/news_1741_myths_about_floating-point_n...
Früher konnten auch CPU-Ergebnisse je nach Ausführungspfad unterschiedlich ausfallen. Siehe den Tweet im Artikel
f64eine Big-Number-Struktur zu synchronisieren, oder ob das in Bezug auf Komplexität und Größe unrealistisch istIch 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?
Mir gefällt die Animation, die das Kernprinzip und die Funktionsweise zeigt. Wirklich sehr gut gemacht
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
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
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
randverwendet und nach WebAssembly kompiliert, nutzt es zur Zufallszahlenerzeugung WASI-Host-FunktionenWährend wir darauf warten, dass
wasi,wasi-httpusw. standardisiert werden, stellen wir vorerst unsere eigene Schnittstelle bereitNatü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
flawlessliegen, sieht es so aus, als würde man statt Zugriff auf das gesamte Rust-Ökosystem eher etwas wiestd::flawlessverwendenDer 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
jsonbin einer Postgres-DatenbankDie 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 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
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
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/
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?
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