1 pontos por GN⁺ 2025-06-07 | 1 comentários | Compartilhar no WhatsApp
  • O banco de dados OLTP de partidas dobradas TigerBeetle prioriza segurança e velocidade, e o Jepsen validou a linha 0.16.11~0.16.30 em clusters Debian de 3 a 6 nós com injeção de falhas
  • Os testes combinaram ordenação explícita por timestamp com um modelo de máquina de estados de thread única baseado na documentação para verificar ao mesmo tempo Strong Serializability e a semântica de contas, transferências e consultas
  • Os principais bugs de segurança foram omissão de resultados em consultas com múltiplos filtros e erro de timestamp no cabeçalho do cliente Java; a partir da 0.16.26, foram observados resultados compatíveis com a alegação de Strong Serializability mesmo sob várias combinações de falhas
  • Em disponibilidade, vieram à tona retry infinito do cliente, crash do processo ao ocorrer eviction de sessão, aumento abrupto de latência com falha de um único nó, panic do servidor durante bitflip em disco e upgrades, e ausência de um caminho de recuperação para perda de disco em nó único
  • O TigerBeetle 0.16.43 incorpora a maior parte dos problemas reportados, incluindo mitigação da latência em falha de nó único e tigerbeetle recover; operadores precisam verificar as notas de versão ao atualizar para 0.16.43 e ao migrar para 0.16.26 ou superior

Projeto do TigerBeetle e escopo dos testes

  • O TigerBeetle é um banco de dados OLTP para partidas dobradas que armazena apenas contas (accounts) e transferências (transfers), em vez de linhas, objetos, grafos ou blobs arbitrários
  • Ele promete consistência Strong Serializable com base em Viewstamped Replication (VR) e foi projetado para modelos como transações financeiras, estoque, emissão de ingressos e medição de utilidades
  • Para workloads de alta contenção e alta taxa de processamento, todas as escritas passam por um único core do nó primary do VR, com foco em scale-up em vez de scale-out
    • Para desempenho, usa processamento em lote, paralelização de I/O, esquema fixo e estruturas de dados de tamanho fixo e alinhadas ao cache
  • O modelo de falhas trata explicitamente memória, processo, relógio, armazenamento e rede
    • Processos podem parar ou sofrer crash
    • Relógios podem saltar para frente e para trás
    • Discos podem sofrer não só falha completa, mas também corrupção por escrita parcial e contaminação de dados
    • A rede pode causar atraso, perda, duplicação, entrega incorreta e corrupção de mensagens
  • O TigerBeetle usa testes de simulação determinística, e os testes VOPR simulam o cluster inteiro e as interfaces de relógio, disco e rede

Modelo de dados e semântica das requisições

  • O modelo de dados é composto por dois tipos de registros: accounts e transfers
    • As contas têm id, ledger, flags, timestamp, code, user_data_32, user_data_64, user_data_128 e outros campos, com id customizado de 128 bits
    • As transferências são registros imutáveis que incluem debit_account_id, credit_account_id, amount, ledger, flags e campos customizados
  • Uma transferência pode ser lançada imediatamente em etapa única, e também há transferências em duas fases, divididas em pending e post/void
    • Uma pending transfer reserva a capacidade das contas de débito e crédito
    • Depois, é possível fazer post ou void de um valor igual ou inferior ao pending amount
    • O campo timeout controla a expiração automática
  • As contas são imutáveis, exceto pelo flag closed e pelos quatro campos de saldo, e as transferências são sempre imutáveis
    • Para alterar ou reverter uma transferência, é preciso criar uma nova transferência de compensação
  • Cada requisição representa uma operação lógica de um único tipo e normalmente pode incluir um lote de até 8190 eventos
    • create_accounts e create_transfers são requisições de escrita
    • lookup_accounts, lookup_transfers, query_accounts, query_transfers, get_account_transfers e get_account_balances são requisições de leitura
  • Cada requisição é uma transação do ponto de vista do banco, mas parte dos eventos dentro de uma requisição já commitada pode falhar logicamente e retornar códigos de erro
    • Quando é necessária atomicidade condicional entre eventos, usa-se chain para que todos os eventos da mesma chain tenham sucesso ou falhem juntos

Método de teste do Jepsen

  • A suíte de testes do Jepsen combina testes baseados em propriedades e injeção de falhas usando a biblioteca de testes Jepsen
  • O alvo dos testes vai do TigerBeetle 0.16.11 ao 0.16.30, incluindo também várias builds de desenvolvimento
    • Os clusters são compostos por 3 a 6 nós Debian
    • A execução ocorreu tanto em contêineres LXC quanto em VMs EC2
  • Como o cliente oficial do TigerBeetle é um smart client que se conecta a todos os nós, ele pode esconder erros de concorrência
    • O Jepsen também testa o comportamento normal do smart client
    • Ao mesmo tempo, usa uma abordagem que restringe cada cliente a um único nó
  • O verificador opera em duas etapas
    • Lê os timestamps de execução das requisições bem-sucedidas, e para escritas com falha ou timeout infere os timestamps a partir dos efeitos observados depois
    • Em seguida, executa o modelo de máquina de estados do TigerBeetle baseado na documentação na ordem inferida dos timestamps para validar resultados e códigos de erro
  • O modelo de máquina de estados foi escrito em mais de 1.600 linhas de Clojure e inclui mapas de contas e transferências, índices, transient error, estatísticas internas e fluxo de relógio
    • Ele trata IDs duplicados, timestamps não monotônicos, restrições de saldo, flags incompatíveis e execução especulativa com rollback de chain
    • Usa a biblioteca de estruturas de dados persistentes de alto desempenho Bifurcan

Injeção de falhas e testes de corrupção de arquivos

  • O Jepsen injeta SIGKILL e SIGSTOP em processos, várias formas de partição de rede, mudanças de relógio de milissegundos a centenas de segundos e oscilações rápidas de relógio de ida e volta
  • Durante os testes, também realiza upgrades de nós entre várias versões
  • Um novo nemesis de corrupção de arquivos foi usado para criar vários tipos de falhas de armazenamento
    • Bit flips aleatórios simulam corrupção como a causada por interferência cósmica
    • Substituição de chunks de arquivo por outros chunks simula misdirected write
    • Restaurar depois snapshots de chunks de arquivo simula lost write
  • Os nós do TigerBeetle têm um único arquivo de dados, dividido em zones em offsets previsíveis
    • Foram realizados testes que corrompem apenas zones específicas, como o cabeçalho do WAL e cópias redundantes da superblock zone
    • Também foram incluídos testes que corrompem várias zones ou o arquivo inteiro
  • A falha de disco “helical” corrompe os arquivos de todos os nós, mas com chunks diferentes em cada nó
    • O objetivo é evitar situações em que um único registro fique irrecuperavelmente corrompido em todas as replicas, já que o layout recente dos arquivos de replica do TigerBeetle costuma ser idêntico bit a bit
    • O head do WAL é uma exceção, pois sua posição pode variar entre os nós

Problemas de segurança encontrados

  • Na 0.16.13, houve com frequência um problema em que as respostas de query_accounts, query_transfers e get_account_transfers omitiam parte ou a totalidade dos resultados
    • Os resultados ausentes estavam sempre no fim da resposta, e a resposta era um prefixo do resultado correto
    • Isso não aparecia em consultas com um único filtro, e ocorria em combinações de múltiplos filtros, como ledger e code
    • A causa era um bug de verificação de limites no zig-zag merge join entre vários índices
    • Foi rastreado em #2544 e corrigido na 0.16.17
  • A API de cabeçalho do cliente Java, adicionada na 0.16.13 para dar suporte aos testes Jepsen, retornava timestamps de execução incorretos ou duplicados
    • A causa era o objeto de resposta singleton mutável Batch.EMPTY do cliente Java
    • Como respostas bem-sucedidas eram representadas como um batch vazio, várias respostas sobrescreviam o header do mesmo objeto
    • Foi corrigido em #2495 e incluído na 0.16.14
    • Não afetava a integridade real dos dados, apenas o timestamp da requisição na API de header do cliente Java
  • Os resultados observados na 0.16.26 e posteriores são consistentes com a afirmação de Strong Serializability do TigerBeetle
    • Essa propriedade é mantida mesmo em combinações de pause de processo, crash, partition de rede, erro de clock, corrupção de disco e upgrade

Problemas de cliente e processamento de requisições

  • A documentação do TigerBeetle explica que as requisições não expiram por timeout e que o cliente continua fazendo retry até receber uma resposta
    • Os métodos assíncronos em Java retornam CompletableFuture e permitem usar APIs de timeout como .get(timeout, timeUnit) ou .orTimeout(...)
    • O Task do cliente .NET também oferece Wait() baseado em timeout
  • Retry infinito pode ocultar tanto erros definidos quanto erros indefinidos
    • Por exemplo, se uma conexão TCP falha com ECONNREFUSED, isso é uma falha definida: aquela requisição original não pode ser executada
    • Mas, se o cliente não informa isso ao chamador e apenas continua com retries internos, do ponto de vista do chamador isso vira uma falha indefinida, como timeout ou interrupção
  • Esse problema está em discussão em #206 e, segundo o relatório, seguia sem solução
    • O Jepsen recomenda representar erros definidos e indefinidos como conceitos de primeira classe e retorná-los ao chamador
    • O retry automático pode ser mantido, mas deve ser configurável, e recomenda-se oferecer como opção o tempo máximo para iniciar a conexão e para aguardar a resposta
  • O cliente Java 0.16.11 tinha um problema em que a JVM inteira sofria segfault ao interromper a thread de chamada síncrona para tratar timeout ou ao fechar o client após uma chamada assíncrona
    • A causa era um campo não definido na estrutura de dados da requisição
    • Se o cliente fosse fechado entre a criação e o envio da requisição, ele desreferenciava o endereço padrão 0xaaa... do Zig
    • Foi corrigido em #2435 e incluído na 0.16.12
  • Os clientes oficiais derrubavam o processo inteiro quando o servidor notificava session eviction
    • O TigerBeetle limita por padrão a 64 o número de sessões concorrentes
    • Eviction também ocorre ao usar uma versão de cliente mais nova que a do servidor
    • Depois de #2484, a partir da 0.16.13, em vez de crashar o processo no eviction, passou a retornar um erro ao chamador

Aumento drástico de latência em falha de nó único

  • Houve casos repetidos em que a latência do cliente aumentou de 3 a 5 ordens de magnitude durante a falha de um único nó
    • Em um cluster de 5 nós, matar um único nó fazia a latência mínima subir de menos de 1 ms para 10 segundos
    • Em um teste com cluster de 3 nós em que um nó foi derrubado, a latência, que era de 1 a 50 ms, subiu para cerca de 100 segundos por requisição e persistiu por quase 1000 segundos até o reinício do nó
  • A causa está relacionada à forma como o TigerBeetle propaga o prepare
    • No VR tradicional, o primary envia o prepare a todos os secondary e recebe os acks diretamente
    • No TigerBeetle, os nós são organizados em um ring, e o primary envia o prepare ao próximo secondary, que então o encaminha ao próximo nó
    • Essa abordagem reduz a exigência de bandwidth de um único nó, mas, se um dos próximos f réplicas no ring falhar, o commit pode ficar bloqueado
  • Esse problema é rastreado em #2739
  • A 0.16.30 mitigou isso enviando metade das mensagens de prepare na direção oposta do ring
    • Assim, alguns prepares conseguem contornar o nó com falha
    • Nos testes Jepsen, latências na casa dos 100 segundos caíram para algo entre 1 e 30 segundos
  • A 0.16.43 inclui melhorias adicionais de desempenho
    • Os nós replicam nas duas direções do ring
    • A topologia em ring muda dinamicamente, e o cluster ajusta a ordem dos nós conforme as condições de rede e as falhas

Corrupção de disco e crash do servidor

  • Na 0.16.20, havia casos em que corrupção de um único bit no superblock, no WAL e na grid zone causava crash na inicialização
    • O log imprimia panic: reached unreachable code e encerrava
    • A causa era um bug na verificação do padding de setor
  • O checksum do TigerBeetle cobre os dados do chunk, mas exclui o padding
    • Se um bit 0 do padding mudasse para 1, o checksum passava
    • Depois, uma assertion que verifica se o padding ainda era 0 falhava, fazendo o servidor crashar
    • Corrupção no padding não compromete a safety e pode ser revertida para 0 ou recuperada de outra réplica
  • O VOPR não encontrou esse bug antes porque corrompia o setor inteiro
    • A corrupção do setor acionava falha de checksum e o caminho de reparo, sem chegar à assertion do padding
    • O TigerBeetle adicionou erros de um único byte ao VOPR em #2681
    • A partir da 0.16.26, setores com padding corrompido passaram a ser reparados em vez de causar crash
  • Um bitflip no número de cópia do superblock também podia causar o mesmo panic
    • As quatro cópias do superblock têm, cada uma, um número copy distinto de 2 bytes, e o checksum ignora esse número
    • Quando um número de cópia corrompido era lido do disco para a memória e depois gravado, ele fazia falhar a assertion do intervalo de 0 a 3
    • Na 0.16.26, isso foi resolvido resetando o número de cópia

Problemas relacionados a upgrade

  • Ao fazer upgrade da 0.16.25 ou anterior para a 0.16.26 ou posterior, foi observado repetidamente o crash panic: checkpoint diverged
    • A causa foi a mudança na estrutura CheckpointState na 0.16.26
    • A nova versão incluía o conjunto de released blocks, mas durante o processo de transmissão de estado compatível com versões anteriores essa informação podia ficar vazia
    • Depois, se o nó fosse reiniciado na 0.16.26, ele poderia ficar sem os released blocks que outras réplicas conheciam
    • A assertion detectava a divergência e causava o crash, impedindo que clientes observassem dados inconsistentes
  • Esse problema foi documentado no changelog em #2745
    • O TigerBeetle não lançou uma versão 0.16.26 com patch
    • Operadores precisam parar o client e esperar o catch-up da réplica antes de fazer upgrade para a 0.16.26 ou posterior
  • Ao executar vários upgrades em sequência da 0.16.16 para a 0.16.28 em cerca de 20 segundos, ou quando um nó é pausado/crasha durante o upgrade, ocorre falha de assertion release_transition
    • O nó em execução abre o novo binário com memfd e o substitui com exec(), mas nesse intervalo o binário em disco pode ser trocado por uma versão mais nova
    • O código falha ao fazer assert de que até o version header em disco é igual à versão atualmente em execução
    • #2758 mudou a assertion para warning na 0.16.29
  • Ao fazer upgrade da 0.16.26 para a 0.16.27, ocorre panic: switch on corrupt value por causa de um tipo de mensagem deprecated
    • O switch do novo nó não tinha um case para o tipo de mensagem antigo e causava crash
    • #2763 corrigiu isso na 0.16.29, recolocando o tipo de mensagem deprecated no case e ignorando-o

Recuperação de perda de disco em nó único

  • O TigerBeetle é resiliente a corrupção de arquivos, mas falha de disco, incêndio, erro de volume EBS ou erro operacional podem fazer com que todos os arquivos de dados de um nó desapareçam ou fiquem danificados de forma irrecuperável
  • Na documentação da época do relatório não havia método para substituir um nó com falha, e existia um procedimento de recuperação não documentado que executava tigerbeetle format para inicializar com arquivos de dados vazios e então esperar que o repair resolvesse
  • O Jepsen confirmou que reformatar funciona na maioria dos casos, mas pode não ser seguro
    • Se, em 3 nós, 2 tiverem uma operação committed op e um deles for reformatado, uma maioria de 2/3 que não observou op pode realizar um view change e a operação pode ser perdida
    • Em testes reais, houve uma execução em que 5 transfers acknowledged foram perdidos
    • Também houve casos em que um nó formatado com um binário mais novo durante o upgrade sofreu crash na inicialização antes de concluir a transição de versão do cluster
  • Esse problema é rastreado em #2767
  • Depois disso, o TigerBeetle 0.16.43 passou a incluir o comando tigerbeetle recover para recuperar nós que sofreram catastrophic data loss

Conclusões e recomendações do Jepsen

  • Houve dois problemas de segurança identificados
    • Omissão de resultados em consultas com múltiplos filtros antes da 0.16.17
    • Timestamps incorretos e duplicados na API de depuração do client Java usada nos testes do Jepsen
  • No total, houve 7 problemas de crash
    • 2 no client Java: acesso a memória não inicializada, crash do processo durante eviction
    • 5 no servidor: 2 panics relacionados a corrupção de disco, 3 panics relacionados a upgrade
    • #2745 foi documentado, e os demais crashes foram resolvidos até a 0.16.29
  • A 0.16.43 resolve todos os problemas do relatório, exceto um
    • O item ainda não resolvido é o problema de requests do client continuarem em retry por design
  • As recomendações para usuários são claras
    • Fazer upgrade para a 0.16.43
    • Ao migrar para a 0.16.26 ou para versões posteriores, verificar as release notes
    • Simular falha de nó único em ambiente de teste e medir como a aplicação reage ao aumento de latência
  • A arquitetura do TigerBeetle parece sólida, e foi observado que a integração de VR, flexible quorum e protocol-aware recovery não comprometeu os invariantes centrais de Strong Serializability
  • Ainda assim, a validação do Jepsen é uma abordagem experimental: ela pode provar a existência de bugs, mas não sua ausência

1 comentários

 
GN⁺ 2025-06-07
Comentários do Hacker News
  • Leitura relacionada: Fuzzer Blind Spots (Meet Jepsen!) – https://tigerbeetle.com/blog/2025-06-06-fuzzer-blind-spots-m...

  • Este relatório é realmente impressionante. Toda vez que eu via as alegações de confiabilidade e escalabilidade do TigerBeetle, pensava: “ok, vamos esperar pelo relatório do Jepsen”
    O relatório trouxe várias questões, e dá para se preocupar com isso, mas é positivo que eles não tenham simplesmente corrigido os problemas: também ampliaram o conjunto interno de testes para pegar bugs semelhantes no futuro. Com essa abordagem de engenharia, daqui a 10 anos o TigerBeetle pode virar, no nicho de aplicações financeiras, um banco de dados padrão no nível de “é só usar Postgres”
    O trabalho do aphyr também foi excelente, e senti que aprendi bastante lendo o relatório

    • O TigerBeetle tem mais de 6.000 assertions, e algumas eram tão rígidas que causaram crashes, mas essas assertions cumpriram seu papel: sinalizaram que era preciso ajustar o mental model, e ele foi de fato ajustado
      Fora isso, excluindo um pequeno bug de correção em um recurso interno de teste colocado apenas no cliente Java para ajudar na auditoria do Jepsen, o Jepsen encontrou apenas um bug de correção, e ele não afetava a durabilidade. O post relacionado está aqui: https://tigerbeetle.com/blog/2025-06-06-fuzzer-blind-spots-m...
      Para ser justo, o TigerBeetle foi projetado e testado para tolerar mais falhas do que o Postgres. Isso porque ele tem um modelo explícito de falhas de armazenamento e usa pesquisas que não existiam quando o Postgres surgiu, em 1996. O modelo de falhas do TB é validado adicionalmente por testes de simulação determinística, e também usa técnicas como alocação estática de memória seguindo as Power of Ten Rules da NASA para código safety-critical. Há cenários conhecidos na literatura em que o Postgres perde dados, mas o TigerBeetle consegue detectá-los e se recuperar
      Para ver mais, basta consultar a seção helical fault injection do relatório do Kyle. A maioria das implementações de Raft e Paxos não foi projetada para tolerar isso, e há também uma apresentação da QCon London: https://m.youtube.com/watch?v=_jfOk4L7CiY
    • Sempre fico ansioso pelos textos do Kyle. A cada novo texto, parece que meu conhecimento de sistemas distribuídos sobe um nível
  • É muito bom ver que, segundo a validação do aphyr, o TigerBeetle corresponde ao que afirma. É bom ver que escolher a abordagem correta leva aos resultados corretos
    Fico curioso sobre como o TigerBeetle acaba sendo usado na prática. Imagino que haverá muitos sistemas externos e outros bancos de dados ao redor de uma instalação do TigerBeetle para tudo que não seja Account ou Transfer; quais são os padrões típicos para esses sistemas menos confiáveis se manterem alinhados com o TigerBeetle, especialmente quando surgem problemas de consistência entre eles?

    • O padrão típico ao integrar o TigerBeetle é separar plano de controle e plano de dados. Usa-se Postgres para uso geral ou OLGP, e TigerBeetle para processamento de transações ou OLTP
      Informações de usuários (nome, endereço, senha etc.) e informações de produtos (descrição, preço etc.) vão para o OLGP como um “arquivo”
      Já todas as transações da Black Friday, em que usuários movem produtos da conta de estoque para a conta do carrinho e depois para as contas de pagamento e envio, vão para o OLTP como um “cofre”. O TigerBeetle permite armazenar até 3 identificadores de dados do usuário por conta ou transferência, de modo que eventos entre entidades possam ser vinculados ao banco de dados OLGP que descreve essas entidades
      Essa arquitetura [1] oferece uma separação de responsabilidades limpa, permitindo escalar e gerenciar cargas de trabalho diferentes de forma independente. Se fosse um banco, faria sentido manter o dinheiro vivo no cofre, com características diferentes de desempenho, conformidade regulatória e retenção, em vez de guardar todo o dinheiro nos arquivos que contêm os registros dos clientes
      Esse padrão faz sentido porque a frequência com que usuários mudam nome ou endereço de e-mail (OLGP) é muito menor do que a frequência com que fazem transações (OLTP)
      Para preservar a consistência, no caminho de escrita o TigerBeetle é tratado como o plano de dados OLTP e a “fonte da verdade”. Quando entra uma transação de “mover para o carrinho” ou “pagamento”, primeiro você grava as dependências de dados necessárias no OLGP e, se houver dados blob relacionados, também em algo como S3; por fim, grava no TigerBeetle para fazer o commit da transação. No caminho de leitura, consulta-se primeiro a fonte da verdade para preservar serializabilidade estrita
      [1] https://docs.tigerbeetle.com/coding/system-architecture/
  • Depois de ler o post do TigerBeetle sobre pontos cegos de fuzzers, este relatório do Jepsen fica especialmente interessante
    O segfault no lado JNI parece algo que nem Rust nem outra linguagem com segurança de memória teriam evitado. O fato de haver pouquíssimos bugs de segurança de memória parece uma evidência de que a abordagem de programação em Zig do TigerBeetle, se bem me lembro o TigerStyle, cumpre muito bem o papel pretendido

    • Veja https://news.ycombinator.com/item?id=44201189. Havia, sim, um bug que Rust teria salvado. Em vez disso, uma assertion salvou a situação: o bacon ficou só um pouco crocante, não queimado
      Ainda assim, é isso mesmo. Sem o TigerStyle, teríamos sido pegos pelos nasal demons
  • Gostei do relatório, excelente e detalhado. O fato de a Jepsen ter testado e assinado embaixo é uma enorme garantia para o TigerBeetle. Ele ainda nem chegou à v1.0, e estou ansioso pelos próximos novos marcos
    Um aplauso especial também aos fundadores que compartilham bons insights nesta thread

    • Kyle fez um trabalho incrível, e o nível de detalhe do relatório também é realmente muito bom. Enquanto lia, cheguei a pensar “isto parece uma obra de arte”, tamanhos eram o artesanato e a precisão
      Em breve também vou compartilhar novidades na apresentação da SD25 em Amsterdam, então estou animado
  • Gostei, ainda que discretamente, do título da seção “Panic! At the Disk 0”

  • É interessante, embora pareça óbvio em retrospecto, que o sistema distribuído em teste precise informar o tempo e a ordem em que as coisas realmente aconteceram para que seja possível verificá-lo corretamente contra o modelo externo do sistema, em vez de usar o relógio de parede

    • Isso funciona por causa da serializabilidade estrita. Com garantias de consistência mais fracas, não existe necessariamente uma única linha do tempo global consistente
      É um metapadrão interessante: quando você resolve algo mais difícil, o sistema acaba ficando mais simples
      Outro exemplo: ao assumir que discos podem falhar e, portanto, precisar incluir um protocolo de recuperação, você acaba recebendo praticamente “de graça” a sincronização de estado de réplicas atrasadas. Afinal, é exatamente o mesmo problema de um disco inteiro corrompido
    • Vejo isso como uma abordagem clássica. Ex.: https://lamport.azurewebsites.net/pubs/time-clocks.pdf
  • O link no texto para o artigo “Viewstamped Replication” infelizmente está quebrado. https://pmg.csail.mit.edu/papers/vr-revisited.pdf tem a conexão recusada
    Talvez o esquema devesse ser http, não https, como em http://pmg.csail.mit.edu/papers/vr-revisited.pdf
    Agora tenho leitura para a noite de sexta-feira

    • Será corrigido em breve
      O artigo VSR de 2012 é um dos meus favoritos, e “Protocol-Aware Recovery for Consensus-Based Storage” também é realmente muito forte
      Boa leitura
  • Pergunta feita puramente com vontade de aprender, e espero que não seja mal interpretada. Estou começando a estudar sistemas distribuídos e estou fascinado por testes de simulação determinística
    Depois de dar uma olhada rápida no relatório da Jepsen sobre o TigerBeetle, nos posts de blog relacionados e no código de integração do Antithesis no workflow do GitHub, fiquei querendo entender melhor a cobertura dos testes
    A pergunta central é se a integração com o Antithesis também poderia ter encontrado esses bugs que a suíte de testes da Jepsen encontrou
    Minha pergunta parte de algumas premissas que podem estar erradas. Eu achava que o TigerBeetle já era testado de forma abrangente pela suíte interna de testes e pelo produto Antithesis, e entendia que a suíte do Antithesis era mais poderosa que a da Jepsen, então me surpreendeu que a Jepsen tenha encontrado problemas que o Antithesis não encontrou
    Gostaria de saber se meu entendimento está errado. Por exemplo, quero entender se 1) a suíte do Antithesis não conseguia detectar essa classe específica de bugs, 2) essa parte do sistema ainda não estava coberta pelos testes do Antithesis, ou 3) estou comparando maçãs com laranjas por não entender as diferentes forças e objetivos das suítes de teste da Jepsen e do Antithesis

    • O post do blog do TigerBeetle explica isso em mais detalhes, mas, resumindo, embora os testes rodando no Antithesis fossem bastante minuciosos, eles não conseguiram produzir a combinação exata de consultas sobrepostas e valores fora de ordem; o gerador da Jepsen acertou essa combinação
      O gerador de testes da Jepsen quase certamente também tem pontos cegos. É por isso que projetar geradores diferentes ajuda
    • Testes generativos de sistemas distribuídos normalmente exigem três componentes. Primeiro, é preciso um ambiente para executar o sistema. Na forma mais simples, isso significa subir um cluster de máquinas reais, mas, para aumentar desempenho, controle sobre respostas de APIs externas, determinismo e reprodutibilidade, algo mais sofisticado é melhor. Segundo, é preciso um gerador de carga que faça o sistema dentro do ambiente executar coisas interessantes. Terceiro, é preciso um auditor que observe o comportamento do sistema sob carga e decida se ele está de acordo com a especificação
      O Antithesis lida principalmente com o problema 1, oferecendo um ambiente de simulação determinística com máquinas virtuais. A Jepsen trata o mesmo problema usando máquinas reais, mas injetando falhas no nível do sistema operacional; já o VOPR próprio do TigerBeetle foi projetado junto com o banco de dados e consegue executar o cluster inteiro em uma única thread. Essas três abordagens são complementares e cada uma se destaca em áreas diferentes
      As partes decisivas neste bug foram os itens 2 e 3: escrever um validador de workload e um auditor capazes de realmente disparar o bug. Aqui, 1.600 linhas de código Clojure específico para o TigerBeetle, escritas por aphyr, dispararam e detectaram o bug; depois, os testes equivalentes do lado do TigerBeetle também foram corrigidos para dispará-lo. Na verdade, o bug aqui está mais no VOPR do que no banco de dados. É natural que bancos de dados tenham bugs, e não dá para evitá-los só com força de vontade. Por isso é necessária uma estratégia de testes capaz de disparar a maioria dos bugs, e os bugs que escapam apontam para falhas no gerador de workloads
    • 90% dos testes de simulação determinística são feitos principalmente pelo VOPR, o simulador determinístico criado pelo próprio TigerBeetle. Ele roda 24/7 em cerca de 1.000 núcleos de CPU dedicados
      Também usamos o Antithesis, mas como uma segunda camada de testes de simulação determinística
      Para saber por que o bug do mecanismo de consultas escapou, veja aqui: https://tigerbeetle.com/blog/2025-06-06-fuzzer-blind-spots-m...
  • Fico curioso se grandes bancos ou bolsas de valores usam o TigerBeetle

    • Em nível nacional, em parceria com a Gates Foundation, o TigerBeetle está sendo integrado a um switch de banco central sem fins lucrativos, e esse sistema deve operar o National Digital Payments System 2.0 de Rwanda no fim deste ano [1]
      No âmbito corporativo, o TigerBeetle já é usado em produção por clientes que processam mais de 100 milhões de transações por mês; recentemente fechou seu primeiro contrato com uma unicórnio fintech europeia avaliada em US$ 2 bilhões, e alguns contratos nos EUA também devem ser concluídos em breve. Por causa da tendência global de migração para processamento de transações em tempo real [2], há bastante interesse de empresas em migrar para o TigerBeetle em busca de maior desempenho
      Respondendo à pergunta, alguns fundadores da Clear Street, uma corretora bem grande de Wall Street, investiram [3] no TigerBeetle
      [1] https://mojaloop.io/how-mojaloop-enables-rndps-2-0-ekash/
      [2] https://tigerbeetle.com/blog/2024-07-23-rediscovering-transa...
      [3] https://tigerbeetle.com/company
    • Não é banco nem bolsa, mas trabalho em uma fintech bem grande e estamos usando o TigerBeetle em um novo produto
    • Se tivessem um cliente desses, acho que estariam se gabando disso na página inicial. Até agora, o maior endosso na página inicial veio de um YouTuber. É verdade que é um YouTuber popular, mas ainda assim é um YouTuber