1 Punkte von GN⁺ 4 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • 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

 
GN⁺ 4 시간 전
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 jq zu starten oder Markdown mit grep zu 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.

    • Das klingt so, als liefe es hauptsächlich auf einer einzelnen Maschine.
      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.
    • So wie es auf HN klingt, zahlt man für Temporals Managed Solution entweder deutlich mehr als erwartet oder betreibt am Ende selbst ein sehr schwergewichtiges System und trägt dabei eine erhebliche operative Last.
      Ich habe es selbst nicht ausprobiert, würde aber gern mehr echte Erfahrungsberichte hören.
    • Kannst du ein konkretes Beispiel dafür geben, SQLite statt Markdown mit jq oder grep zu verwenden?
    • Liest sich wie Temporal-Werbung :)
    • Die Diskussion über Datei- und Datenbankansätze ist interessant. Ich bin auch hin- und hergewechselt und am Ende bei einer Datenbank gelandet.
  • 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.

    • Das Verständnis dafür, welche Arten von Nebenläufigkeit es gibt und wie man deren Anforderungen erfüllt, scheint ziemlich eingeschränkt zu sein. Ob etwas ein Server ist oder nicht, ist in dieser Diskussion nicht besonders wichtig.
      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.
    • Schon die Kombination aus SQLite und einem Frontend mit Nebenläufigkeit, zum Beispiel einem 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.
    • SQLite ist für viele Einsatzzwecke gut, aber offenbar verbringst du deine Zeit vor allem mit großen Web-Apps, die eine Netzwerkdatenbank und Sharding brauchen. Auch das ist ein spannender Bereich, aber es gibt noch viele andere.
      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.
    • Ist das nicht der Grund, warum es Milliarden von SQLite-Datenbanken gibt?
      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
    • Wenn Daten sich auf natürliche Weise sharden lassen und Schreibvorgänge innerhalb eines einzelnen Shards stattfinden, wird Parallelität einfach. Anfragen werden an den Shard geroutet, in dem sich die Daten des Nutzers befinden, und dort lokal gelesen und geschrieben.
      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

    • Ich habe meinen Uptime-Monitor selbst gebaut; wie gehst du bei geografischer Verteilung oder Tests über private Internetanschlüsse anders vor?
  • 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

    • Ich bin Developer Relations bei DuckDB. Erstmal danke für die netten Worte :)
      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...

    • Wenn man es richtig benutzt, ist SQLite praktisch ein Methodenaufruf innerhalb des Prozesses. Wenn die einzigen verbleibenden Hindernisse Laufzeitumgebung, Kernel, Dateisystem und lokaler NVMe-Speicher sind, kann es Hosting-Alternativen haushoch schlagen
      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
    • Wenn man davon ausgeht, dass SQLite hervorragende Software ist, sollte man dann nicht ohnehin erwarten, dass es in Single-Node-Anwendungen gegenüber Postgres gut abschneidet?
      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

    • Man kann STRICT-Tabellen verwenden: https://sqlite.org/stricttables.html
    • Stimmt, das ist praktisch der einzige Punkt, der mich an SQLite wirklich stört. SQLite mit einem strikten Typsystem wäre großartig
    • Das ist der Fehler und Preis der Abwärtskompatibilität. Die meisten SQLite-Nutzer müssen pro Verbindung einige Pragmas ausführen
      PRAGMA journal_mode = WAL
      PRAGMA foreign_keys = ON

      Something non-null

      PRAGMA busy_timeout = 1000

      This is fine for most applications, but see the manual

      PRAGMA synchronous = NORMAL

      If you use it as a file format

      PRAGMA trusted_schema = OFF
      Je 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 NULL ist nötig, weil date bei ungültigen Datumswerten NULL zurü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.
    • Weil es eine einzelne Datei ist
    • Es wird wegen anderer Dinge als des Typsystems gelobt
      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

    • Darauf musst du nicht warten. Das passiert bereits
      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 gesagt würde ich „Für dauerhafte Workflows braucht man nur S3“ akzeptieren und es für Datenverarbeitungsanwendungen verwenden, die Daten von S3 nach S3 verschieben
    • Reichen Logs allein wirklich für dauerhafte Workflows? Das verwirrt mich. Wie persistiert und fragt man darauf aufbauende oder zusammenhängende Daten über einem Log ab? Sind mit Logs hier Dinge wie Elasticsearch oder Meilisearch gemeint?
    • Kurz danach kommt dann „Für dauerhafte Workflows braucht man nur Sockets“, und am Ende „Für dauerhafte Workflows braucht man nur Kernel-Primitiven“
      Ernsthaft gesprochen heißt Experte zu sein, das richtige Werkzeug für die jeweilige Aufgabe zu verwenden