- 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
Quer continuar recebendo tópicos de tecnologia selecionados?
Siga o canal no Telegram.
@GeekNewsBrasil
1 comentários
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
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
Nunca fiz isso na prática, mas queria ouvir mais experiências reais
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
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
net/httpdo 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 TPSO 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
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
É 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
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
systemde binários compilados na máquina de desenvolvimento e enviados para deployEu 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
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
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...
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
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
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 = OFFDependendo 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
dateretorna 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.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”
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, ser especialista é usar a ferramenta certa para o trabalho