1 pontos por GN⁺ 4 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • O ponto central da execução durável não é a infraestrutura em si, mas a preservação do estado do workflow; se o estado de progresso é mantido, reexecução e recuperação se tornam possíveis
  • O Obelisk armazena o andamento do workflow em um log de execução e o reproduz a partir do histórico persistido, com uma estrutura voltada para tentativas repetidas de atividades
  • O SQLite fornece estado durável baseado em transações em um arquivo local, sem serviço de banco de dados separado, saltos de rede ou plano de controle adicional
  • O Litestream transmite alterações do SQLite de forma assíncrona para um armazenamento de objetos compatível com S3, mas se o volume desaparecer antes da cópia, as gravações mais recentes podem ser perdidas
  • O Obelisk também oferece suporte a Postgres, sendo mais adequado quando são necessárias maior disponibilidade, escalabilidade compartilhada e características de banco de dados em rede

Modelo operacional de SQLite e Litestream

  • O Litestream pode transmitir alterações do SQLite de forma assíncrona para um armazenamento de objetos compatível com S3
  • Isso permite manter o estado de trabalho próximo ao ambiente de execução e, ao mesmo tempo, copiar o banco de dados para fora para backup, migração e inspeção
  • Devido à natureza da replicação assíncrona, se o volume do SQLite desaparecer antes da cópia, as gravações locais mais recentes podem ser perdidas na restauração
  • A estrutura é executar o servidor Obelisk junto com o banco de dados SQLite, fazer backup com Litestream e, quando necessário, um observador buscar o banco de dados de que precisa
  • O mesmo arquivo SQLite pode ser usado para reprodução local, depuração e para entender o que o agente realmente executou

Faixa de uso adequada e critérios para escolher Postgres

  • Agentes de IA e workflows gerados por IA costumam ser intermitentes e experimentais, então uma configuração em que cada agente ou tenant tenha sua própria pequena unidade de estado é mais fácil de compreender
  • Uma abordagem em que vários pequenos servidores dentro de micro-VMs ou contêineres tenham, cada um, seu banco de dados SQLite e backup em armazenamento de objetos pode ser mais simples, barata e com melhor isolamento de falhas do que um único sistema grande, compartilhado e sempre em execução
  • Se a replicação assíncrona para armazenamento de objetos não corresponder ao modelo de durabilidade desejado, ou se forem necessárias maior disponibilidade, escalabilidade compartilhada mais ampla e características de banco de dados em rede, Postgres é mais adequado
  • Muitos sistemas de workflow não precisam desse nível de infraestrutura desde o primeiro dia, e não há necessidade de começar com uma infraestrutura maior do que as exigências de estado
  • Apenas com um banco de dados SQLite local, backup via Litestream para S3 e uma combinação de workers de baixo custo já é possível criar um sistema durável com pouca infraestrutura, e isso pode ser um padrão inicial razoável no universo de agentes de IA

1 comentários

 
GN⁺ 4 시간 전
Comentários do Hacker News
  • Comecei a montar workflows com Temporal, mas para apps locais ele é distribuído de forma bem leve e, em instalações locais isoladas, usa SQLite
    O tratamento de retries de API e a organização de workflows e tarefas ficam realmente muito simples, então recomendo experimentar. Filosoficamente, vai exatamente na mesma direção proposta por este texto, mas acrescenta uma interface muito rica e flexível, ótima para agentes. Também é fácil inspecionar workflows pela UI web e revisar execuções de agentes
    O Temporal coloca uma confiabilidade muito maior no sistema quase de graça. Como sistemas distribuídos e confiáveis são difíceis, acho melhor não reinventar a roda
    Se você quer conseguir inspecionar facilmente o banco SQLite, entender o que está acontecendo no workflow, compor tarefas individuais e tornar simples a chamada de workflows, vale a pena olhar o Temporal
    Junto com isso, quase deixei de usar arquivos para agentes. Markdown e JSON são bons, mas ao criar pequenos apps locais acabam parecendo armadilhas. LLMs lidam bem com SQLite e, a partir dele, dá para renderizar em Markdown, JSON ou no formato que você quiser. Se o agente puder consultar apenas linhas específicas, em vez de abrir o jq ou fazer grep em Markdown, também economiza muitos tokens. Você acaba tendo um sistema de gestão de dados portátil e autocontido que força uma estrutura de dados mais disciplinada do que vários arquivos. Se um pequeno projeto local crescer ou se formalizar mais, ele também pode evoluir para MySQL/Postgres, e você já terá esquema e disciplina de dados

    • Parece que você roda isso principalmente em uma única máquina
      O Temporal fica bem mais complexo quando cresce. Operar Cassandra não é divertido, e Ringpop e TChannel são difíceis de depurar quando há problemas. O suporte a backend SQL, por causa das exigências de consistência, não oferece réplicas com escalonamento horizontal e só permite instância única
      Dependendo de como o código é escrito, também fica complicado modificar o código embutido no workflow. Mudanças que alteram a ordem dos eventos no histórico quebram o determinismo dos workers já implantados
      Usamos bastante Temporal, e quem começou com scripts ou automação simples adora, mas quem construiu sistemas reais de produção em cima dele odeia. Pode ser falta de maturidade operacional da nossa parte, mas o quadro cor-de-rosa que aparece nestes comentários não bateu com a minha experiência
    • Pelo que se ouve no HN, ou você paga mais do que esperava pela solução gerenciada do Temporal, ou acaba operando você mesmo um sistema bem pesado e assumindo uma carga operacional considerável
      Nunca fiz isso na prática, mas queria ouvir mais experiências reais
    • Você pode dar um exemplo concreto de usar SQLite em vez de fazer jq ou grep em Markdown?
    • Parece propaganda do Temporal :)
    • A discussão sobre abordagens com arquivos versus banco de dados é interessante. Eu também fiquei indo e voltando até acabar me fixando em banco de dados
  • Não entendo a obsessão em usar SQLite em apps reais de produção. SQLite é um banco de dados embarcado, então não é nada adequado para gerenciamento de concorrência
    É para esse tipo de trabalho que existem servidores de banco de dados como Postgres e MySQL. O papel deles, como um todo, é permitir que vários processos modifiquem dados ao mesmo tempo a partir de máquinas diferentes
    Este é um princípio básico da ciência da computação, e o pessoal que grita “SQLite para tudo” parece ter um pouco de falta de experiência

    • Parece haver uma compreensão bem limitada sobre que tipos de concorrência existem e como atender a essas exigências. Ser um servidor ou não não é tão importante nesta discussão
      SQLite é um excelente banco de dados de produção para muitas cargas de trabalho reais, e isso está amplamente documentado. Como ele é muito diferente do Postgres, é preciso aprender uma tecnologia completamente diferente
      Uma forma de ver isso é que SQLite pode se encaixar bem em partes de um sistema que acabam tendo uma partição forte de forma natural
    • Só a combinação de SQLite com um frontend de concorrência, por exemplo um servidor net/http do Go, muitas vezes já consegue lidar com toda a carga imaginável de algum serviço. Mais ainda se for possível aumentar o hardware com o tempo, e o SQLite pode escalar com facilidade até centenas de milhares de TPS
      O que realmente se abre mão é de alta disponibilidade/failover e recuperação de desastres, mas também existem soluções para isso. Sistemas de servidor único em geral são surpreendentemente robustos. Isso porque, sem um plano de controle complexo, muitas vezes a disponibilidade cai à medida que o sistema cresce
    • SQLite é bom para vários usos, mas você provavelmente passa a maior parte do tempo com grandes webapps que precisam de banco de dados em rede e sharding. Essa também é uma área interessante, mas existem muitas outras
      Gosto de reavaliar as “boas práticas” existentes à luz das mudanças tecnológicas. Ainda mais quando isso vai na direção de aumentar a simplicidade. Rodar um site de mídia social para a família em um único banco SQLite num VPS é ótimo. São cerca de 15 usuários e quase não há manutenção. Também rodo uma instância do FreshRSS e uma página “now” com SQLite
      No trabalho também usei SQLite para todo tipo de coisa nas últimas décadas. Usei para filas de trabalho temporárias, para carregar e consultar rapidamente muitos logs localmente, e para exibir e filtrar em tempo real com o excelente https://github.com/simonw/datasette do simonw
      Em vez de “SQLite para tudo”, diria que é mais algo como “SQLite em muito mais lugares do que você imagina”
      O trabalho de kentonv/Cloudflare com SQLite na edge pode ter popularizado um pouco mais essa ideia, mas isso já era uma tendência. https://blog.cloudflare.com/sqlite-in-durable-objects/
      Conhecer e querer aproveitar esses casos pequenos e úteis não é falta de experiência; pode até ser um sinal de experiência
    • Não é por isso que existem bilhões de bancos de dados SQLite?
      É bem possível que SQLite seja mais usado do que todos os outros mecanismos de banco de dados somados. Existem bilhões de cópias do SQLite em uso no mundo real. Ele está presente em dispositivos Android, iPhones e aparelhos com iOS, Macs, instalações do Windows 10/11, Firefox/Chrome/Safari, Skype, iTunes, cliente do Dropbox, TurboTax e QuickBooks, PHP e Python, na maioria das TVs e set-top boxes, na maioria dos sistemas multimídia automotivos e em incontáveis aplicações
      https://sqlite.org/mostdeployed.html
    • Se os dados forem naturalmente fragmentados e as escritas acontecerem dentro de um único shard, o paralelismo fica fácil. As requisições são roteadas para o shard onde estão os dados do usuário, e as leituras/escritas são feitas localmente
      Isso torna a escalabilidade muito mais fácil de entender. É só dividir e repetir, depois dividir e repetir de novo. Algo como adicionar mais um shard a cada N usuários
      Em troca, você passa a ter outros problemas, como consultas entre shards — por exemplo, para análise — e como nivelar a carga quando usuários abandonam o sistema ou envelhecem
      Mas dá para evitar todo o problema de escalonamento de índices compartilhados causado por inserções/atualizações em uma base massiva de usuários
      Acaba virando mais um banco de dados hierárquico do que um banco de dados relacional
  • Substituí tudo isso por Go + SQLite: Intercom, Zendesk, marketing por e-mail, Kanban, Todo, stack de pagamentos, issue tracker, fórum, monitoramento de uptime, clone do PagerDuty
    Como vendo dezenas de produtos, pensei: e se eu mesmo construísse tudo?
    Tudo roda no mesmo servidor e usa pouquíssima memória. Substituí todas as ferramentas SaaS que usava por isso
    Ao migrar para um servidor dedicado, reduzi para cerca de 1/10 o que pagava por soluções gerenciadas em nuvem, mantive a mesma alta disponibilidade e ainda consegui latência menor. Parte do motivo também foi o aumento da latência de cauda por causa de noisy neighbors em VPS
    Antes eu gastava muito dinheiro com essas coisas, mas agora estou há 4 meses operando assim e só precisei de pequenas atualizações
    O deploy é realmente simples. Nada de Docker nem Kubernetes, só serviços systemd e binários compilados na máquina de desenvolvimento e enviados para deploy
    Eu também pagava por serviços como MaxMind e IPData, mas criei meu próprio serviço de geolocalização por IP, e nos testes ele teve desempenho melhor do que a maioria das soluções existentes
    Começou como um substituto para o Uptime Robot, depois ganhei confiança e substituí o PagerDuty. Depois disso, substituí o Intercom
    Por fim, eu sempre ouvia que “não se deve construir a própria stack de pagamentos”, mas pensei YOLO e decidi cometer esse erro por conta própria. Estudei as soluções de pagamento existentes, desenvolvi e implantei a minha, e até agora não houve nenhum problema
    Na frente uso o Caddy
    Percebi que, das funcionalidades que a maioria dos produtos SaaS oferece, eu de fato só usava 1% a 5%, e que as funções de que eu realmente precisava ficavam cada vez mais enterradas dentro dessas plataformas “enterprise-grade”, dificultando o workflow
    Não vou mostrar os produtos comerciais, porque parceiros e clientes provavelmente não gostariam de ver o quão barato eu sou, mas eu chamo isso de ser engenhoso
    Posso mostrar um app gratuito. Lancei recentemente e ele já tem mais de 20 mil usuários: https://macrocodex.app/
    Esse app usa apenas o clone do Zendesk. O e-mail é tratado com roteamento da Cloudflare, então o custo operacional é quase zero

    • Eu mesmo criei um monitor de uptime, mas no caso de distribuição geográfica ou testes em conexões residenciais, como você lida com isso de forma diferente?
  • Há um grande salto entre um arquivo e um banco de dados com múltiplas partições. Quando há operação real em jogo, rodar o banco em container não faz meu estilo
    Pessoalmente, muita coisa de ETL pode ser processada localmente sem trazer um banco de dados enterprise para a equação. Nesses casos, o DuckDB é de 5 a 10 vezes melhor que o SQLite, e muito mais simples e rápido do que subir um banco Postgres dedicado
    Em scripting geral, não tem nem comparação entre um script awk de 20 linhas e um script SQL equivalente com DuckDB, muito mais limpo, robusto e fácil de manter
    Espero que a MotherDuck não esteja numa situação em que precise inflar e despejar para fazer IPO. Seria triste perder essa ferramenta por causa da ganância corporativa de sempre

    • Sou responsável por relações com desenvolvedores no DuckDB. Primeiro, obrigado pelas palavras gentis :)
      Achei divertida a parte do script awk de 20 linhas. Ontem mesmo no Ubuntu Summit eu defendi quase exatamente esse ponto. A partir de certo momento, escrever shell scripts com GNU coreutils deixa de ser realista, e scripts SQL com DuckDB escalam muito melhor em complexidade e manutenção, e muitas vezes também em desempenho. Os slides estão aqui: https://blobs.duckdb.org/slides/duckdb-ubuntu-summit-2026.pd... páginas 32 a 36
      Além disso, a MotherDuck desenvolve um DBaaS de código fechado sobre o DuckDB. Ela é construída em cima do DuckDB, e você se conecta à MotherDuck usando DuckDB, mas é uma empresa separada, com investimento de VC e sede em Seattle
      O DuckDB é desenvolvido pela DuckLabs, uma empresa bootstrap baseada em receita, de Amsterdã. A propriedade intelectual do projeto pertence a uma terceira organização, a DuckDB Foundation, uma entidade sem fins lucrativos da Holanda. Para mais detalhes, veja https://duckdb.org/faq#how-are-duckdb-the-duckdb-foundation-...
  • Criei uma biblioteca que permite fazer atualizações concorrentes com segurança em um banco SQLite no S3[0]
    Ela usa a pouco conhecida extensão de sessions do SQLite e compare-and-swap do S3 em um pequeno arquivo de metadados para funcionar de forma bem eficiente e segura. Tenho usado isso com satisfação em vários projetos pequenos nos quais preciso de um banco com estado em funções Lambda, mas não quero pagar o custo de uma instância completa de banco de dados
    [0]: https://github.com/psanford/s3db

  • O SQLite tem um desempenho surpreendentemente bom em aplicações de nó único, mesmo comparado ao Postgres
    O Postgres usa muito mais memória, e a E/S precisa passar por comunicação entre processos. Já o SQLite consegue manter tudo dentro do processo por meio de um pool de conexões compartilhado
    Estou testando vários mecanismos de armazenamento para harnesses de agentes, e com SQLite consegui até 7,5 mil sessões simultâneas em uma única vCPU, enquanto o Postgres travava ou esgotava as conexões
    [0] https://github.com/impalasys/talon/pull/23#issuecomment-4577...

    • Se usado corretamente, o SQLite é praticamente uma chamada de método dentro do processo. Se os únicos obstáculos restantes forem o runtime, o kernel, o sistema de arquivos e o armazenamento NVMe local, ele pode ser absurdamente mais rápido do que alternativas hospedadas
      No momento em que você sai da thread atual, já está perdendo em termos de latência. Se não obrigar comunicação entre threads, o SQLite pode operar em escala de microssegundos
    • Partindo do princípio de que o SQLite é um software excelente, não seria esperado que ele tivesse bom desempenho em aplicações de nó único, mesmo comparado ao Postgres?
      No contexto de nó único, o Postgres é excesso. Não se deveria esperar que ele competisse com o SQLite
      É quase como fazer benchmark entre um HashMap em memória e o Redis, e se surpreender porque o HashMap vai bem em condições ideais
  • Depois de ler sobre o SQLite por anos, experimentei usá-lo em um projeto pessoal e, vindo do Postgres, fiquei chocado com o quão fraco é o sistema de tipos
    É realmente inferior, e não entendo por que recebe tantos elogios
    https://sqlite.org/datatype3.html
    https://www.postgresql.org/docs/current/datatype.html
    Lidar com data/hora parece usar um banco de dados de 30 anos atrás, e nada é realmente imposto na inserção. Alguém precisa me explicar por que tanta gente gosta disso

    • Dá para usar tabelas STRICT: https://sqlite.org/stricttables.html
    • Sim, esse é praticamente o único ponto de que realmente sinto falta no SQLite. Um SQLite com sistema de tipos estrito seria excelente
    • Isso é culpa — e custo — da compatibilidade retroativa. A maioria dos usuários de SQLite precisa executar alguns pragmas em cada conexão
      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
      Dependendo do binding, opções adicionais podem ser necessárias. Por exemplo, aplicações Python não deveriam usar os padrões do módulo sqlite3. Esses padrões simplesmente estão errados. Antes do 3.12, nem havia alternativa além de usar um binding fora da biblioteca padrão: https://docs.python.org/3/library/sqlite3.html#transaction-c...
      Você também deveria usar tabelas strict. https://www.sqlite.org/stricttables.html
      Embora a ergonomia seja ruim, também dá para usar restrições CHECK. Por exemplo, é possível com o suporte embutido a datas do SQLite, mas é meio esquisito:
      CHECK (
      date(my_date_col) IS NOT NULL
      AND my_date_col = date(my_date_col)
      )
      O IS NOT NULL é necessário porque date retorna NULL para datas inválidas. A outra verificação é necessária porque ela também aceita dias julianos; por exemplo, date('2026') vira algum momento no ano 4707 a.C.
    • Porque é um arquivo único
    • Ele é elogiado por coisas que não são o sistema de tipos
      Concordo que, especialmente antes das tabelas strict, isso era decepcionante
      Vale a pena olhar para o DuckDB. É próximo de um SQLite com tipagem de verdade. Mas, em vez de OLTP — isto é, array de structs — ele é OLAP, isto é, struct de arrays, então pode ter desempenho pior em cargas típicas de SQLite. Na prática, para uma aplicação que realmente consideraria um ou outro, imagino que a diferença não seja grande
  • Depois de usar vários clusters grandes de Postgres, migrei para SQLite, e um serviço com MAU de 7 dígitos é sustentado inteiramente por durable objects com SQLite
    É preciso pensar o padrão de acesso de outra forma, mas os benefícios valeram muito a pena

  • É um bom enquadramento. Se o problema principal é armazenar o estado do workflow de forma durável, com possibilidade de inspeção e recuperação fácil, então muitas vezes o SQLite já basta

  • Quero muito ver a próxima iteração dessa ideia virar “para workflows duráveis, basta um log

    • Não precisa esperar mais. Isso já está acontecendo
      Um motivo pelo qual soluções no estilo “basta um log” podem falhar é quando logs não confiáveis viram um ataque por injeção[1]
      Confira o SBOM, e não se esqueça de incluir também o pipeline de CI/CD[2]
      [1] https://news.ycombinator.com/item?id=48315440
      [2] https://github.com/jqwik-team/jqwik/issues/708#issuecomment-...
    • Falando sério, eu compraria a ideia de que “para workflows duráveis, basta S3” e usaria isso em aplicações de processamento de dados que movem dados de S3 para S3
    • Um log sozinho é suficiente para um workflow durável? Fiquei confuso. Como você persiste e consulta dados aninhados ou relacionados em cima do log? Aqui, “log” quer dizer algo como Elasticsearch ou Meilisearch?
    • Logo depois vem “para workflows duráveis, basta um socket”, e no fim “para workflows duráveis, bastam primitivas do kernel”
      Falando sério, ser especialista é usar a ferramenta certa para o trabalho