- Der Kern von dauerhafter Ausführung ist nicht die Infrastruktur selbst, sondern die Persistenz des Workflow-Zustands; wenn der Fortschritt erhalten bleibt, sind erneute Ausführung und Wiederherstellung möglich
- Obelisk speichert den Workflow-Fortschritt im Ausführungsprotokoll, spielt ihn aus der dauerhaft gespeicherten Historie erneut ab und ist auf die Wiederholung von Aktivitäten ausgelegt
- SQLite liefert transaktionsbasierte dauerhafte Zustände in einer lokalen Datei, ganz ohne separaten Datenbankdienst, Netzwerk-Hop oder zusätzliche Control Plane
- Litestream streamt SQLite-Änderungen asynchron in einen S3-kompatiblen Objektspeicher, aber wenn das Volume vor dem Kopieren verloren geht, können die neuesten Schreibvorgänge verloren gehen
- Obelisk unterstützt auch Postgres und eignet sich besser, wenn höhere Verfügbarkeit, gemeinsame Skalierbarkeit oder die Eigenschaften einer Netzwerkdatenbank benötigt werden
Betriebsmodell von SQLite und Litestream
- Litestream kann SQLite-Änderungen asynchron in einen S3-kompatiblen Objektspeicher streamen
- Der Arbeitszustand kann nahe an der Laufzeitumgebung gehalten werden, während die Datenbank dennoch für Backups, Migrationen oder Inspektionen nach außen kopiert werden kann
- Aufgrund der asynchronen Replikation können bei der Wiederherstellung die neuesten lokalen Schreibvorgänge fehlen, wenn das SQLite-Volume vor dem Kopieren verloren geht
- Die Struktur besteht darin, den Obelisk-Server zusammen mit der SQLite-Datenbank auszuführen, mit Litestream zu sichern und bei Bedarf einen Beobachter die benötigte Datenbank abrufen zu lassen
- Dieselbe SQLite-Datei kann für lokale Wiederherstellung, Debugging und zum Verständnis dessen verwendet werden, was ein Agent tatsächlich ausgeführt hat
Geeigneter Einsatzbereich und Kriterien für die Wahl von Postgres
- KI-Agenten und von KI erzeugte Workflows sind oft burstartig und experimentell, sodass eine Konfiguration mit kleinen eigenständigen Zustandseinheiten pro Agent oder Tenant leichter zu verstehen ist
- Viele kleine Server in Micro-VMs oder Containern, die jeweils eine SQLite-Datenbank und ein Backup im Objektspeicher haben, können einfacher, günstiger und besser in der Fehlerisolierung sein als ein einziges großes, gemeinsam genutztes System im Dauerbetrieb
- Wenn asynchrone Replikation in einen Objektspeicher nicht das gewünschte Dauerhaftigkeitsmodell ist oder höhere Verfügbarkeit, breitere gemeinsame Skalierbarkeit oder die Eigenschaften einer Netzwerkdatenbank benötigt werden, ist Postgres besser geeignet
- Viele Workflow-Systeme brauchen ein solches Infrastrukturniveau nicht vom ersten Tag an, und es gibt keinen Grund, mit einer Infrastruktur zu starten, die größer ist als die Zustandsanforderungen
- Schon mit einer lokalen SQLite-Datenbank, Litestream-Backups nach S3 und einer Kombination günstiger Worker lässt sich ein dauerhaftes System mit wenig Infrastruktur aufbauen, was im Bereich der KI-Agenten ein vernünftiger Standard sein kann
1 Kommentare
Hacker-News-Kommentare
Ich habe angefangen, Workflows mit Temporal aufzubauen. Für lokale Apps lässt es sich recht leicht deployen, und bei isolierten lokalen Installationen verwende ich SQLite.
API-Retry-Handling sowie das Aufräumen von Workflows und Tasks werden dadurch wirklich einfach, daher würde ich empfehlen, es einmal auszuprobieren. Philosophisch geht es exakt in die Richtung, die dieser Artikel vorschlägt, ergänzt aber noch eine sehr reichhaltige und flexible Oberfläche, mit der sich Agents gut handhaben lassen. Über die Web-UI kann man Workflows leicht ansehen und Agent-Ausführungen prüfen.
Temporal bringt dem System fast zum Nulltarif eine deutlich höhere Zuverlässigkeit. Verteilte, zuverlässige Systeme sind schwierig, daher finde ich, man sollte das Rad besser nicht neu erfinden.
Wenn man die SQLite-Datenbank leicht inspizieren, verstehen möchte, was im Workflow passiert, einzelne Tasks zusammensetzen und Workflows einfach aufrufbar machen will, dann ist Temporal einen Blick wert.
Gleichzeitig habe ich die Dateinutzung für Agents fast vollständig reduziert. Markdown und JSON sind gut, aber beim Bau kleiner lokaler Apps fühlen sie sich wie eine Falle an. LLMs kommen mit SQLite gut zurecht, und von dort aus kann man in jede gewünschte Form rendern, etwa Markdown oder JSON. Wenn ein Agent statt
jqzu starten oder Markdown mitgrepzu durchsuchen einfach nur bestimmte Zeilen abfragen kann, spart das auch viele Tokens. Man bekommt ein portables, in sich geschlossenes Datenverwaltungssystem, das zu disziplinierteren Datenstrukturen führt als mehrere Dateien. Wenn ein kleines lokales Projekt wächst oder formeller wird, kann man später auch auf MySQL/Postgres gehen und hat dann bereits ein Schema und Datendisziplin.Temporal wird bei größerem Maßstab deutlich komplexer. Cassandra zu betreiben macht keinen Spaß, und Ringpop sowie TChannel sind schwer zu debuggen, wenn Probleme auftreten. Wegen der Konsistenzanforderungen unterstützt das SQL-Backend keine horizontal skalierten Replikate, sondern nur eine einzelne Instanz.
Je nachdem, wie man den Code schreibt, wird es auch kompliziert, im Workflow eingebetteten Code zu ändern. Änderungen, die die Reihenfolge von History-Events verändern, brechen die Deterministik bereits ausgerollter Worker.
Wir nutzen Temporal viel; alle, die mit einfachem Scripting oder Automatisierung angefangen haben, mögen es, aber alle, die darauf echte Produktionssysteme gebaut haben, hassen es. Das kann an unzureichender Betriebsroutine liegen, aber das rosige Bild in diesen Kommentaren passt nicht zu meiner Erfahrung.
Ich habe es selbst nicht ausprobiert, würde aber gern mehr echte Erfahrungsberichte hören.
Ich verstehe die Fixierung darauf nicht, SQLite in echten Produktiv-Apps einzusetzen. SQLite ist eine eingebettete Datenbank und für das Management von Nebenläufigkeit überhaupt nicht geeignet.
Genau dafür gibt es Datenbankserver wie Postgres oder MySQL. Deren gesamte Aufgabe besteht darin, dass mehrere Prozesse auf unterschiedlichen Maschinen gleichzeitig Daten ändern können.
Das ist ein Grundprinzip der Informatik, und die Fraktion „SQLite für alles“ wirkt dabei etwas unerfahren.
SQLite ist für viele reale Workloads eine hervorragende Produktivdatenbank, und das ist gut dokumentiert. Da sie sich stark von Postgres unterscheidet, muss man dafür eine ganz andere Technik lernen.
Eine Sichtweise ist, dass SQLite gut zu Bereichen eines Systems passen kann, die sich auf natürliche Weise stark partitionieren lassen.
net/http-Server in Go, reicht oft aus, um jede Last zu bewältigen, die ein Dienst sich realistischerweise vorstellen kann. Wenn man die Hardware im Lauf der Zeit aufrüsten kann, gilt das umso mehr, und SQLite lässt sich unkompliziert auf mehrere Hunderttausend TPS skalieren.Was man tatsächlich aufgibt, ist vor allem Hochverfügbarkeit/Failover und Disaster Recovery, aber auch dafür gibt es Lösungen. Single-Server-Systeme sind im Allgemeinen überraschend robust, weil die Verfügbarkeit oft sinkt, je mehr das System wächst, wenn keine komplexe Control Plane vorhanden ist.
Ich bewerte gern bestehende „Best Practices“ im Licht technologischer Veränderungen neu, besonders wenn das zu mehr Einfachheit führt. Eine Social-Media-Seite für die Familie auf einem einzelnen SQLite-DB auf einem VPS zu betreiben, ist großartig. Sie hat etwa 15 Nutzer und braucht fast keine Wartung. Ich betreibe auch eine FreshRSS-Instanz und eine „now“-Seite mit SQLite.
Auch bei der Arbeit habe ich SQLite in den letzten Jahrzehnten für alle möglichen Dinge verwendet: als temporäre Job-Queue, um lokal große Mengen an Logs schnell zu laden und abzufragen, und um sie mit simonws großartigem https://github.com/simonw/datasette live anzuzeigen und zu filtern.
Es geht weniger um „SQLite für alles“ als vielmehr um „SQLite an viel mehr Stellen, als man denkt“.
Die Edge-SQLite-Arbeit von kentonv/Cloudflare hat diese Denkweise vielleicht etwas populärer gemacht, aber der Trend war schon vorher da. https://blog.cloudflare.com/sqlite-in-durable-objects/
Solche kleinen, nützlichen Anwendungsfälle zu kennen und einsetzen zu wollen, ist kein Zeichen von Unerfahrenheit, sondern kann im Gegenteil ein Zeichen von Erfahrung sein.
SQLite wird wahrscheinlich häufiger eingesetzt als alle anderen Datenbank-Engines zusammen. In freier Wildbahn existieren Milliarden von SQLite-Kopien. Sie stecken in Android-Geräten, iPhones und iOS-Geräten, Macs, Windows-10/11-Installationen, Firefox/Chrome/Safari, Skype, iTunes, dem Dropbox-Client, TurboTax und QuickBooks, PHP und Python, den meisten Fernsehern und Set-Top-Boxen, den meisten Fahrzeuginfotainmentsystemen und unzähligen Anwendungen.
https://sqlite.org/mostdeployed.html
Dadurch wird Skalierung viel leichter verständlich. Man kopiert, teilt auf und wiederholt das Ganze. Für jeweils N Nutzer fügt man einfach einen weiteren Shard hinzu.
Dafür bekommt man andere Probleme, etwa shardübergreifende Abfragen wie Analysen und die Frage, wie man die Last ausgleicht, wenn Nutzer abwandern oder ein Shard altert.
Aber man vermeidet das gesamte Problem gemeinsam genutzter Indizes, die bei sehr großen Nutzerzahlen durch Inserts und Updates skaliert werden müssen.
Es wird eher eine hierarchische als eine relationale Datenbank.
Ich habe all das durch Go + SQLite ersetzt: Intercom, Zendesk, E-Mail-Marketing, Kanban, Todo, Zahlungs-Stack, Issue-Tracker, Forum, Uptime-Monitoring, PagerDuty-Klon
Ich verkaufe Dutzende Produkte, also dachte ich: Warum nicht alles selbst bauen?
Alles läuft auf demselben Server und braucht sehr wenig Speicher. Ich habe damit alle SaaS-Tools ersetzt, die ich vorher genutzt habe
Beim Umzug auf dedizierte Server sanken die Kosten auf etwa ein Zehntel dessen, was ich zuvor für Managed-Cloud-Lösungen gezahlt hatte, bei gleicher Hochverfügbarkeit und sogar geringerer Latenz. Ein Teil des Grundes war auch, dass die Tail-Latenz durch noisy neighbors auf VPS zunahm
Früher habe ich dafür viel Geld ausgegeben, aber jetzt läuft das seit 4 Monaten in Produktion und es waren nur kleinere Updates nötig
Das Deployment ist wirklich simpel. Kein Docker, kein Kubernetes, nur systemd-Services und auf der Entwicklungsmaschine gebaute und ausgerollte Binärdateien
Ich habe auch für Dienste wie MaxMind oder IPData bezahlt, aber dann selbst einen IP-Geolokalisierungsdienst gebaut, der in Tests besser abschnitt als die meisten bisherigen Lösungen
Angefangen hat es als Ersatz für Uptime Robot, dann hatte ich genug Selbstvertrauen, um PagerDuty zu ersetzen. Danach habe ich Intercom ersetzt
Zuletzt hieß es immer: „Baue deinen Zahlungs-Stack nicht selbst“, aber ich dachte mir YOLO und wollte diesen Fehler selbst machen. Ich habe bestehende Zahlungslösungen studiert, selbst entwickelt und ausgerollt, und bisher gab es überhaupt keine Probleme
Davor läuft Caddy
Mir wurde klar, dass ich von den Funktionen der meisten SaaS-Produkte in Wirklichkeit nur 1–5 % nutzte und dass die Funktionen, die ich tatsächlich brauchte, in diesen „Enterprise“-Plattformen immer tiefer vergraben wurden und dadurch die Workflows schwieriger machten
Kommerzielle Produkte werde ich nicht zeigen, weil Partner und Kunden wohl nicht mögen würden zu sehen, wie billig ich arbeite, aber ich nenne das einfallsreich
Die kostenlose App kann ich zeigen. Sie ist kürzlich gestartet und hat über 20.000 Nutzer: https://macrocodex.app/
Diese App nutzt nur den Zendesk-Klon. E-Mails werden über Cloudflare Routing verarbeitet, daher fallen fast keine Betriebskosten an
Zwischen einer Datei und einer Datenbank mit mehreren Partitionen liegt eine große Lücke. In einer Situation mit echter Produktionsverantwortung ist es nicht mein Stil, die Datenbank in einem Container laufen zu lassen
Meiner Meinung nach lässt sich viel ETL lokal verarbeiten, ohne eine Enterprise-Datenbank hereinzuziehen. In solchen Fällen ist DuckDB 5–10x besser als SQLite und viel einfacher und schneller, als eine dedizierte Postgres-Datenbank hochzufahren
Für allgemeines Scripting ist ein 20-zeiliges awk-Skript mit einem deutlich saubereren, robusteren und wartbareren SQL-Äquivalent auf Basis von DuckDB nicht einmal ansatzweise vergleichbar
Hoffentlich gerät MotherDuck nie in die Lage, für einen IPO Pump-and-Dump betreiben zu müssen. Es wäre traurig, dieses Tool wegen der üblichen Unternehmensgier zu verlieren
Die Bemerkung über das 20-zeilige awk-Skript ist interessant. Gestern habe ich auf dem Ubuntu Summit fast genau dasselbe argumentiert. Ab einem bestimmten Punkt wird Shell-Scripting mit GNU coreutils unrealistisch, und DuckDB-SQL-Skripte skalieren deutlich besser bei Komplexität und Wartbarkeit und oft auch bei der Performance. Die Folien sind hier: https://blobs.duckdb.org/slides/duckdb-ubuntu-summit-2026.pd... Seiten 32–36
MotherDuck entwickelt außerdem ein Closed-Source-DBaaS auf Basis von DuckDB. Es wird auf DuckDB gebaut, und man verbindet sich mit MotherDuck über DuckDB, aber es ist ein separates VC-finanziertes Unternehmen mit Sitz in Seattle
DuckDB wird von DuckLabs entwickelt, einem bootstrapped, also umsatzfinanzierten Unternehmen aus Amsterdam. Das geistige Eigentum des Projekts liegt bei einer dritten Organisation, der niederländischen Non-Profit-Organisation DuckDB Foundation. Mehr dazu hier: https://duckdb.org/faq#how-are-duckdb-the-duckdb-foundation-...
Ich habe eine Bibliothek gebaut, mit der sich SQLite-DBs auf S3 sicher gleichzeitig aktualisieren lassen[0]
Sie funktioniert mithilfe der wenig bekannten SQLite-sessions-Erweiterung und S3 compare-and-swap auf einer kleinen Metadatendatei ziemlich effizient und sicher. Ich nutze sie mit Freude in mehreren kleinen Projekten, bei denen Lambda-Funktionen eine zustandsbehaftete DB brauchen, ich aber nicht die Kosten für eine vollständige Datenbankinstanz zahlen will
[0]: https://github.com/psanford/s3db
SQLite ist in Single-Node-Anwendungen im Vergleich zu Postgres überraschend leistungsfähig
Postgres verbraucht viel mehr Speicher, und I/O muss über Interprozesskommunikation laufen. SQLite dagegen kann über einen gemeinsam genutzten Connection-Pool alles im Prozess halten
Ich teste gerade mehrere Storage-Engines für ein Agent-Harness; mit SQLite waren auf einer einzelnen vCPU bis zu 7,5k gleichzeitige Sessions möglich, während Postgres abstürzte oder keine Verbindungen mehr annehmen konnte
[0] https://github.com/impalasys/talon/pull/23#issuecomment-4577...
Sobald man den aktuellen Thread verlässt, hat man beim Thema Latenz schon verloren. Wenn keine Inter-Thread-Kommunikation erzwungen wird, kann SQLite im Mikrosekundenbereich arbeiten
Im Single-Node-Kontext ist Postgres overkill. Man sollte nicht erwarten, dass es mit SQLite konkurriert
Das ist fast so, als würde man einen In-Memory-HashMap gegen Redis benchmarken und sich dann wundern, dass die HashMap unter idealen Bedingungen besser abschneidet
Ich habe jahrelang über SQLite gelesen und es dann in einem privaten Projekt ausprobiert; nachdem ich von Postgres kam, war ich schockiert, wie schwach das Typsystem ist
Es ist wirklich minderwertig, und ich verstehe nicht, warum es so viel gelobt wird
https://sqlite.org/datatype3.html
https://www.postgresql.org/docs/current/datatype.html
Das Arbeiten mit Datum/Uhrzeit fühlt sich an, als würde man eine 30 Jahre alte Datenbank benutzen, und beim Einfügen wird überhaupt nichts erzwungen. Jemand muss mir erklären, warum so viele Leute das mögen
PRAGMA journal_mode = WAL
PRAGMA foreign_keys = ON
Something non-null
PRAGMA busy_timeout = 1000This is fine for most applications, but see the manual
PRAGMA synchronous = NORMALIf you use it as a file format
PRAGMA trusted_schema = OFFJe nach Binding können zusätzliche Optionen nötig sein. Python-Anwendungen sollten zum Beispiel nicht die Standardeinstellungen des
sqlite3-Moduls verwenden. Diese Defaults sind einfach falsch. Vor 3.12 gab es auch keine Alternative außer einem Binding außerhalb der Standardbibliothek: https://docs.python.org/3/library/sqlite3.html#transaction-c...Man sollte auch strict tables verwenden. https://www.sqlite.org/stricttables.html
Man kann auch
CHECK-Constraints verwenden, auch wenn die Ergonomie schlecht ist. Mit der eingebauten Datumsunterstützung von SQLite geht das zum Beispiel, wirkt aber unbeholfen:CHECK (
date(my_date_col) IS NOT NULL
AND my_date_col = date(my_date_col)
)
IS NOT NULList nötig, weildatebei ungültigen DatumswertenNULLzurückgibt. Die andere Prüfung ist nötig, weil sonst auch julianische Tageszahlen akzeptiert werden;date('2026')wird zu einem Zeitpunkt im Jahr 4707 v. Chr.Ich stimme zu, dass es vor strict tables enttäuschend war
Man sollte sich DuckDB anschauen. Das kommt SQLite mit vernünftigen Typen am nächsten. Es ist allerdings OLAP statt OLTP, also eher Struct-of-Arrays statt Array-of-Structs, und kann bei typischen SQLite-Lasten daher schlechter performen. Wenn eine Anwendung realistisch zwischen beiden wählen kann, dürfte der Unterschied aber nicht groß sein
Ich habe mehrere große Postgres-Cluster betrieben und bin dann zu SQLite gewechselt; ein Dienst mit monatlich aktiven Nutzern im siebenstelligen Bereich läuft komplett auf SQLite durable objects
Man muss die Zugriffsmuster anders denken, aber die Vorteile waren es absolut wert
Gutes Framing. Wenn das Hauptproblem darin besteht, den Workflow-Zustand dauerhaft zu speichern, inspizierbar zu machen und leicht wiederherstellen zu können, reicht SQLite oft aus
Ich hoffe, als nächste Iteration dieser Idee bald „Für dauerhafte Workflows braucht man nur Logs“ zu sehen
Ein Grund, warum „nur Logs reichen“-Lösungen scheitern können, ist, dass nicht vertrauenswürdige Logs zu einem Injection-Angriff werden[1]
Vergiss nicht, das SBOM zu prüfen und auch die CI/CD-Pipeline einzubeziehen[2]
[1] https://news.ycombinator.com/item?id=48315440
[2] https://github.com/jqwik-team/jqwik/issues/708#issuecomment-...
Ernsthaft gesprochen heißt Experte zu sein, das richtige Werkzeug für die jeweilige Aufgabe zu verwenden