8 points par GN⁺ 2026-05-30 | 1 commentaires | Partager sur WhatsApp
  • Il n’est pas forcément nécessaire de disposer d’une infrastructure complexe dédiée pour mettre en œuvre la durable execution ; l’essentiel est de préserver en toute sécurité l’état du workflow
  • SQLite fournit un état persistant transactionnel sans service de base de données séparé, et conserve en toute sécurité l’état d’avancement du workflow sans saut réseau ni control plane supplémentaire
  • En utilisant Litestream pour diffuser de façon asynchrone les changements SQLite vers un stockage objet compatible S3, il est possible de garder l’état au plus près du runtime tout en le copiant à des fins de sauvegarde, migration ou inspection
  • Pour des workflows bursty et expérimentaux comme les agents IA, une configuration en micro-VM ou en conteneurs, où chaque agent possède sa propre petite unité d’état SQLite indépendante, est plus simple, moins coûteuse et meilleure pour l’isolation des pannes qu’un grand système partagé unique
  • Si l’on a besoin de haute disponibilité ou d’une forte extensibilité partagée, Postgres est plus adapté, mais de nombreux systèmes de workflow n’ont pas besoin dès le départ d’une infrastructure de ce niveau

Le cœur de l’exécution persistante

  • On parle souvent de l’exécution persistante comme si elle exigeait une infrastructure persistante, mais en pratique ce qui compte réellement, c’est le workflow state ; le calcul reste peu coûteux et peut demeurer jetable
  • Dans Obelisk, l’état d’avancement du workflow est stocké dans un execution log, rejoué à partir de l’historique persisté, et les activités peuvent être relancées

Pourquoi SQLite est adapté

  • SQLite fournit un état persistant transactionnel sans ajouter de service de base de données séparé
  • Il permet de conserver en toute sécurité l’état d’avancement du workflow sans saut réseau, sans control plane supplémentaire et sans nouvelle charge opérationnelle
  • Dans de nombreux systèmes, un fichier de base de données local correspond exactement au bon niveau d’infrastructure

Assurer la portabilité avec Litestream

  • Litestream diffuse de façon asynchrone les changements SQLite vers un stockage objet compatible S3, ce qui permet de garder l’état au plus près du runtime tout en prenant en charge des copies pour la sauvegarde, la migration et l’inspection
  • Point d’attention : la réplication Litestream étant asynchrone, les écritures locales les plus récentes qui n’ont pas encore été copiées avant la disparition du volume SQLite peuvent manquer lors de la restauration
    • C’est acceptable pour de nombreux workflows IA et expérimentaux, mais cela correspond à un modèle différent d’une base de données partagée hautement disponible
  • Modèle opérationnel pratique : exécuter le serveur Obelisk avec la base de données SQLite, effectuer la sauvegarde avec Litestream, puis laisser un observateur récupérer la base si nécessaire — le même fichier peut être réutilisé pour le replay local, le débogage et l’analyse du comportement des agents

Pourquoi c’est particulièrement avantageux pour les agents IA

  • Les agents IA et les workflows générés par l’IA ont des caractéristiques bursty et expérimentales, et il est avantageux pour le raisonnement de disposer de petites unités d’état auto-contenues par agent ou par tenant
  • Une architecture reposant sur une petite flotte de serveurs à base de micro-VM ou de conteneurs, chacun exploitant sa propre base SQLite indépendante avec sauvegarde vers un stockage objet, est plus simple, moins coûteuse et meilleure en termes d’isolation des pannes qu’un grand système partagé permanent

Quand il faut utiliser Postgres

  • Obelisk prend aussi en charge Postgres, à choisir lorsqu’il faut de la haute disponibilité, une extensibilité partagée à grande échelle, ou des caractéristiques de déploiement pour lesquelles une base de données réseau est plus adaptée
  • Postgres est également plus approprié si la réplication asynchrone vers un stockage objet ne correspond pas au modèle de durabilité recherché
  • De nombreux systèmes de workflow n’ont pas besoin, au démarrage, d’une infrastructure supérieure à ce que leur état exige réellement
  • La combinaison SQLite local + sauvegarde S3 via Litestream + workers peu coûteux suffit à construire un système persistant à infrastructure minimale, et peut constituer le choix par défaut le plus raisonnable dans un environnement d’agents IA

1 commentaires

 
GN⁺ 2026-05-30
Commentaires sur Hacker News
  • J’ai commencé à construire des workflows avec Temporal ; pour une appli locale, le déploiement reste assez léger, et dans une installation locale isolée on utilise SQLite
    La gestion des retries d’API ainsi que l’organisation des workflows et des tâches deviennent vraiment simples, donc je recommande d’essayer. Philosophiquement, c’est exactement dans la même direction que ce que propose cet article, mais avec en plus une interface très riche et flexible, bien adaptée aux agents. Il est aussi facile d’examiner les workflows via l’interface web et de revoir l’exécution des agents
    Temporal apporte au système une fiabilité bien plus élevée, presque gratuitement. Les systèmes distribués et fiables sont difficiles, donc je pense qu’il vaut mieux éviter de réinventer la roue
    Si vous voulez pouvoir inspecter facilement une base SQLite, comprendre ce qui se passe dans un workflow, composer des tâches individuelles et rendre les workflows simples à invoquer, Temporal vaut le détour
    Avec ça, j’ai presque cessé d’utiliser des fichiers pour les agents. Markdown et JSON sont très bien, mais pour de petites applis locales, ça ressemble à un piège. Les LLM se débrouillent bien avec SQLite, et on peut ensuite en faire le rendu dans le format voulu, comme Markdown ou JSON. Si l’agent peut interroger uniquement certaines lignes au lieu de lancer jq ou de faire du grep sur du Markdown, on économise aussi beaucoup de tokens. On obtient un système de gestion de données portable et auto-contenu, qui impose plus de discipline à la structure des données que plusieurs fichiers séparés. Et si un petit projet local grossit ou se formalise davantage, on peut ensuite passer à MySQL/Postgres, avec déjà un schéma et une discipline de données en place

    • On dirait que ça tourne surtout sur une seule machine
      Temporal devient bien plus complexe à grande échelle. Exploiter Cassandra n’a rien d’agréable, et Ringpop comme TChannel sont difficiles à déboguer quand il y a un problème. Le support des backends SQL, à cause des exigences de cohérence, ne permet pas de réplicas à scalabilité horizontale et se limite à une seule instance
      Selon la manière dont le code est écrit, modifier du code déjà embarqué dans les workflows devient aussi compliqué. Les changements qui modifient l’ordre des événements dans l’historique cassent le déterminisme des workers déjà déployés
      Nous utilisons beaucoup Temporal : tous ceux qui ont commencé avec pour du scripting simple ou de l’automatisation l’adorent, et tous ceux qui ont construit dessus de vrais systèmes de production le détestent. C’est peut-être un manque de maîtrise opérationnelle, mais l’image très rose qu’on voit dans ces commentaires ne correspond pas à mon expérience
    • D’après ce qu’on entend sur HN, soit on paie bien plus que prévu pour la solution managée de Temporal, soit on finit par exploiter soi-même un système très lourd, en assumant une charge opérationnelle importante
      Je ne l’ai pas fait moi-même, mais j’aimerais entendre plus de retours d’expérience concrets
    • Peux-tu donner un exemple concret de cas où SQLite est utilisé à la place de jq ou grep sur du Markdown ?
    • On dirait une pub pour Temporal :)
    • La discussion entre approche par fichiers et approche base de données est intéressante. Moi aussi j’ai fait des allers-retours avant de finir par me fixer sur une base de données
  • Je ne comprends pas cette obsession pour l’idée d’utiliser SQLite dans des applications réellement en production. SQLite est une base de données embarquée, donc pas du tout adaptée à la gestion de la concurrence
    C’est précisément pour ce genre de chose qu’il existe des serveurs de bases de données comme Postgres ou MySQL. Leur rôle global est de permettre à plusieurs processus, sur des machines différentes, de modifier des données simultanément
    C’est un principe de base de l’informatique, et le camp du « SQLite pour tout » semble manquer un peu d’expérience

    • La compréhension des différents types de concurrence et de la manière de répondre à ces exigences semble assez limitée. Le fait qu’il s’agisse ou non d’un serveur n’a pas tant d’importance dans cette discussion
      SQLite est une excellente base de données de production pour de nombreuses charges de travail réelles, et cela est largement documenté. Comme c’est très différent de Postgres, il faut apprendre un ensemble de techniques complètement différent
      Un angle de vue consiste à dire que SQLite peut très bien convenir aux parties d’un système qui se prêtent naturellement à un partitionnement fort
    • Il arrive souvent qu’une combinaison de SQLite et d’un frontend gérant la concurrence, par exemple un serveur net/http de Go, suffise à absorber toute la charge qu’un service donné peut raisonnablement imaginer. Encore plus si l’on peut faire évoluer le matériel avec le temps, et SQLite peut monter simplement jusqu’à plusieurs centaines de milliers de TPS
      Ce à quoi on renonce vraiment, c’est surtout la haute disponibilité / le basculement et la reprise après sinistre, mais il existe aussi des solutions à cela. Les systèmes à serveur unique sont en général étonnamment robustes, notamment parce qu’en l’absence de plan de contrôle complexe, la disponibilité tend souvent à diminuer à mesure que le système grossit
    • SQLite est très bien pour de nombreux usages, mais tu sembles sans doute passer l’essentiel de ton temps sur de grosses applications web qui ont besoin d’une base de données en réseau et de sharding. C’est un domaine intéressant lui aussi, mais il y en a beaucoup d’autres
      J’aime réévaluer les « bonnes pratiques » établies à la lumière de l’évolution technique, surtout quand cela va dans le sens de plus de simplicité. Faire tourner un site de réseau social familial sur une seule base SQLite sur un VPS, c’est excellent. Il y a environ 15 utilisateurs et pratiquement aucune maintenance. Mon instance FreshRSS et ma page « now » tournent aussi sur SQLite
      Au travail aussi, j’ai utilisé SQLite pour toutes sortes de choses au fil des dernières décennies : comme file de tâches temporaire, pour ingérer et interroger rapidement beaucoup de logs en local, et pour les afficher/filtrer en temps réel avec l’excellent https://github.com/simonw/datasette de simonw
      Plutôt que « SQLite pour tout », je dirais plutôt « SQLite dans bien plus d’endroits qu’on ne l’imagine »
      Le travail de kentonv/Cloudflare sur SQLite à l’edge a peut-être un peu popularisé cette idée, mais c’était déjà une tendance existante. https://blog.cloudflare.com/sqlite-in-durable-objects/
      Vouloir connaître et exploiter ces petits cas d’usage utiles n’est pas un signe de manque d’expérience, c’est peut-être au contraire un signe d’expérience
    • N’est-ce pas justement pour cela qu’il existe des milliards de bases de données SQLite ?
      SQLite est probablement plus utilisé que tous les autres moteurs de base de données réunis. Il existe des milliards de copies de SQLite dans la nature. On en trouve dans les appareils Android, les iPhone et appareils iOS, les Mac, les installations Windows 10/11, Firefox/Chrome/Safari, Skype, iTunes, le client Dropbox, TurboTax et QuickBooks, PHP et Python, la plupart des TV et décodeurs, la plupart des systèmes multimédias embarqués des voitures, et d’innombrables applications
      https://sqlite.org/mostdeployed.html
    • Si les données sont naturellement shardées et que les écritures ont lieu à l’intérieur d’un seul shard, alors le parallélisme devient facile. Les requêtes sont routées vers le shard où se trouvent les données de l’utilisateur, et on y effectue localement les lectures/écritures
      Cela rend la scalabilité beaucoup plus facile à comprendre. Il suffit de découper, répliquer le schéma, puis recommencer. En pratique, on ajoute un shard supplémentaire tous les N utilisateurs
      En échange, on récupère d’autres problèmes, comme les requêtes inter-shards — par exemple pour l’analytique — et la manière d’équilibrer la charge quand des utilisateurs partent ou vieillissent
      Mais on évite tout le problème de montée en charge des index partagés causé par les insertions/mises à jour à grande échelle
      Cela devient moins une base de données relationnelle qu’une base de données hiérarchique
  • J’ai remplacé tout ce qui suit par du Go + SQLite : Intercom, Zendesk, email marketing, Kanban, Todo, la stack de paiement, l’issue tracker, le forum, le monitoring d’uptime, un clone de PagerDuty
    Comme je vends des dizaines de produits, je me suis dit : pourquoi ne pas tout faire moi-même ?
    Tout tourne sur le même serveur et consomme très peu de mémoire. J’ai remplacé tous les outils SaaS que j’utilisais par ces solutions
    En migrant vers un serveur dédié, j’ai réduit la facture à environ 1/10 de ce que je payais pour des solutions cloud managées, tout en conservant la même haute disponibilité et avec une latence plus faible. L’augmentation de la latence de queue due aux noisy neighbors sur les VPS faisait aussi partie des raisons
    Avant, je dépensais énormément pour ce genre d’outils, mais après 4 mois en production, seules de petites mises à jour ont été nécessaires
    Le déploiement est vraiment simple. Ni Docker ni Kubernetes, juste des services systemd et des binaires compilés sur la machine de dev puis déployés
    Je payais aussi pour des services comme MaxMind ou IPData, mais j’ai créé mon propre service de géolocalisation IP, et dans les tests il a mieux performé que la plupart des solutions existantes
    Ça a commencé comme un remplacement d’Uptime Robot, puis j’ai pris confiance et j’ai remplacé PagerDuty. Ensuite, j’ai remplacé Intercom
    Enfin, on entend toujours dire qu’il ne faut pas développer soi-même la « stack de paiement », mais je me suis dit YOLO et j’ai décidé de faire moi-même cette erreur. J’ai étudié les solutions de paiement existantes, puis j’ai développé et déployé la mienne, et jusqu’ici il n’y a eu absolument aucun problème
    J’utilise Caddy en frontal
    Je me suis rendu compte que, parmi les fonctions fournies par la plupart des produits SaaS, je n’en utilisais en réalité que 1 à 5 %, et que les fonctions dont j’avais vraiment besoin étaient de plus en plus enfouies dans ces plateformes « enterprise-grade », ce qui compliquait les workflows
    Je ne montrerai pas les produits commerciaux, parce que mes partenaires et clients n’aimeraient sans doute pas voir à quel point je fais ça à bas coût, mais moi j’appelle ça être débrouillard
    Je peux montrer l’app gratuite. Elle a été lancée récemment et compte plus de 20 000 utilisateurs : https://macrocodex.app/
    Cette app n’utilise que le clone de Zendesk. L’email est géré via le routage Cloudflare, donc le coût d’exploitation est quasiment nul

    • J’ai créé moi-même un moniteur d’uptime ; comment gérez-vous différemment la distribution géographique ou les tests sur des connexions résidentielles ?
  • Il y a un vrai fossé entre un fichier et une base de données multi-partitions. Faire tourner une base de données dans un conteneur pour de la vraie prod, ce n’est pas mon style
    À mon avis, beaucoup d’ETL peuvent être traités en local sans faire entrer une base de données enterprise dans l’équation. Dans ces cas-là, DuckDB est 5 à 10 fois meilleur que SQLite, et bien plus simple et rapide à mettre en place qu’une base Postgres dédiée
    Pour du scripting généraliste, il n’y a pas photo entre un script awk de 20 lignes et un équivalent SQL basé sur DuckDB, bien plus propre, robuste et maintenable
    J’espère que MotherDuck n’aura pas à faire du pump-and-dump en vue d’une IPO. Ce serait triste de perdre cet outil à cause d’une cupidité d’entreprise trop banale

    • Je m’occupe des relations développeurs chez DuckDB. Merci d’abord pour les mots sympathiques :)
      L’histoire du script awk de 20 lignes est amusante. Hier, à l’Ubuntu Summit, j’ai défendu pratiquement le même point de vue. À partir d’un certain niveau, écrire des scripts shell avec GNU coreutils devient irréaliste, et les scripts SQL DuckDB passent bien mieux à l’échelle en termes de complexité, de maintenabilité et, souvent, de performance aussi. Les slides sont ici : https://blobs.duckdb.org/slides/duckdb-ubuntu-summit-2026.pd... pages 32 à 36
      Par ailleurs, MotherDuck développe un DBaaS propriétaire construit au-dessus de DuckDB. Ils construisent sur DuckDB, et on se connecte à MotherDuck avec DuckDB, mais c’est une entreprise distincte financée par des VC et basée à Seattle
      DuckDB est développé par DuckLabs, une société bootstrapée d’Amsterdam, donc financée par ses revenus. La propriété intellectuelle du projet appartient à une troisième entité, la fondation néerlandaise à but non lucratif DuckDB Foundation. Pour plus de détails, voir https://duckdb.org/faq#how-are-duckdb-the-duckdb-foundation-...
  • J’ai créé une bibliothèque qui permet de mettre à jour simultanément en toute sécurité une base SQLite stockée sur S3[0]
    Elle fonctionne de manière assez efficace et sûre grâce à l’extension SQLite sessions, peu connue, et à un compare-and-swap S3 sur un petit fichier de métadonnées. Je l’utilise avec plaisir dans plusieurs petits projets où j’ai besoin d’une base persistante pour des fonctions Lambda, sans vouloir payer le coût d’une instance de base de données complète
    [0]: https://github.com/psanford/s3db

  • SQLite offre des performances étonnamment bonnes, même comparé à Postgres, dans une application mono-nœud
    Postgres consomme beaucoup plus de mémoire, et les entrées/sorties doivent passer par de la communication inter-processus. SQLite, lui, permet de tout garder dans le processus via un pool de connexions partagé
    Je teste plusieurs moteurs de stockage pour un harness d’agents ; avec SQLite, on pouvait monter jusqu’à 7 500 sessions concurrentes sur un seul vCPU, alors que Postgres plantait ou épuisait les connexions
    [0] https://github.com/impalasys/talon/pull/23#issuecomment-4577...

    • Quand on l’utilise correctement, SQLite revient en pratique à un appel de méthode intra-processus. S’il ne reste comme obstacles que le runtime, le noyau, le système de fichiers et le stockage NVMe local, ça peut être écrasant de rapidité par rapport aux alternatives hébergées
      Dès qu’on sort du thread courant, c’est une partie perdue en termes de latence. Si on n’impose pas de communication inter-threads, SQLite peut fonctionner à l’échelle de la microseconde
    • Si l’on part du principe que SQLite est un logiciel excellent, ne devrait-on pas justement s’attendre à ce qu’il soit performant face à Postgres dans une application mono-nœud ?
      Dans un contexte mono-nœud, Postgres est excessif. On ne devrait pas s’attendre à ce qu’il rivalise avec SQLite
      C’est un peu comme benchmarker un HashMap presque en mémoire vive contre Redis, puis s’étonner que le HashMap donne de bons résultats dans des conditions idéales
  • Après avoir lu parler de SQLite pendant des années, je l’ai essayé sur un projet perso, et venant de Postgres, j’ai été choqué par la pauvreté du système de types
    C’est vraiment inférieur, et je ne comprends pas pourquoi il reçoit autant d’éloges
    https://sqlite.org/datatype3.html
    https://www.postgresql.org/docs/current/datatype.html
    La gestion des dates/heures donne l’impression d’utiliser une base de données vieille de 30 ans, et rien n’est réellement imposé à l’insertion. Il faut que quelqu’un m’explique pourquoi autant de gens aiment ça

    • On peut utiliser les tables STRICT : https://sqlite.org/stricttables.html
    • Oui, c’est pratiquement mon seul vrai reproche à SQLite. Un SQLite avec un système de types strict serait excellent
    • C’est la faute, et le prix, de la rétrocompatibilité. La plupart des utilisateurs de SQLite doivent exécuter quelques pragma à chaque connexion
      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
      Selon le binding, d’autres options peuvent être nécessaires. Par exemple, une application Python ne devrait pas utiliser les valeurs par défaut du module sqlite3. Ces valeurs par défaut sont tout simplement mauvaises. Avant la 3.12, il n’y avait même pas d’alternative autre que d’utiliser un binding hors de la bibliothèque standard : https://docs.python.org/3/library/sqlite3.html#transaction-c...
      Il faut aussi utiliser les strict tables. https://www.sqlite.org/stricttables.html
      On peut aussi utiliser des contraintes CHECK, même si l’ergonomie laisse à désirer. Par exemple, c’est possible avec la gestion intégrée des dates de SQLite, mais c’est maladroit :
      CHECK (
      date(my_date_col) IS NOT NULL
      AND my_date_col = date(my_date_col)
      )
      Le IS NOT NULL est nécessaire parce que date renvoie NULL pour les dates invalides. L’autre vérification est nécessaire parce qu’elle accepte aussi les jours juliens, donc date('2026') devient un moment quelconque en 4707 av. J.-C.
    • Parce que c’est un fichier unique
    • Il est encensé pour des choses autres que le système de types
      Je suis d’accord pour dire que c’était décevant, surtout avant les strict tables
      Il faut regarder DuckDB. C’est proche d’un SQLite avec de vrais types. En revanche, c’est de l’OLAP, c’est-à-dire array of structs, et non de l’OLTP, c’est-à-dire struct of arrays, donc ses performances peuvent être moins bonnes que SQLite sur les charges classiques de SQLite. En pratique, pour une application qui hésite entre les deux, il n’y aura probablement pas une énorme différence
  • Après avoir utilisé plusieurs gros clusters Postgres, je suis passé à SQLite, et un service avec un nombre d’utilisateurs actifs mensuels à 7 chiffres repose entièrement sur des durable objects SQLite
    Il faut penser les schémas d’accès différemment, mais les avantages en valaient largement la peine

  • C’est une bonne manière de poser le problème. Si le principal enjeu est de stocker durablement l’état d’un workflow, de pouvoir l’inspecter et de le restaurer facilement, alors SQLite suffit souvent

  • J’ai hâte de voir la prochaine itération de cette idée : « un workflow durable n’a besoin que d’un log »

    • Pas besoin d’attendre, c’est déjà en train d’arriver
      Une raison pour laquelle les solutions du type « un log suffit » peuvent échouer, c’est quand des logs non fiables deviennent un vecteur d’injection[1]
      Il faut vérifier le SBOM, sans oublier d’y inclure le pipeline CI/CD[2]
      [1] https://news.ycombinator.com/item?id=48315440
      [2] https://github.com/jqwik-team/jqwik/issues/708#issuecomment-...
    • Plus sérieusement, j’accepterais volontiers l’idée que « un workflow durable n’a besoin que de S3 », et je l’utiliserais pour une application de traitement de données qui déplace des données de S3 vers S3
    • Un log seul suffit-il vraiment pour un workflow durable ? Je suis perdu. Comment persister et interroger des données imbriquées ou liées au-dessus d’un log ? Ici, par log, on parle d’Elasticsearch ou de quelque chose comme Meilisearch ?
    • Ensuite viendra « un workflow durable n’a besoin que de sockets », et à la fin « un workflow durable n’a besoin que de primitives du noyau »
      Plus sérieusement, être expert, c’est utiliser l’outil adapté au travail