1 points par GN⁺ 2025-06-07 | 1 commentaires | Partager sur WhatsApp
  • TigerBeetle est une base de données OLTP spécialisée dans la comptabilité en partie double, développée avec pour objectif la sûreté et une vitesse de traitement élevée
  • Elle prend en charge le protocole de consensus Viewstamped Replication et le critère de strong serializability, avec une architecture optimisée pour les charges à forte contention et à haut débit
  • Sa conception et ses procédures de test accordent une attention particulière à la tolérance aux pannes et à la résilience, avec pour objectif un fonctionnement sans perte de données dans des situations de panne variées
  • Les tests Jepsen ont mis en évidence divers bugs et problèmes de performance dans les mises à niveau, les tests, le modèle d’opération et la résilience du cluster, permettant d’améliorer les réponses possibles
  • Les versions récentes apportent plusieurs améliorations et correctifs, notamment sur les performances de réplication en anneau, la gestion des erreurs côté client, ainsi que la précision des logs et des requêtes

Présentation de TigerBeetle

  • TigerBeetle est une base de données de traitement transactionnel en ligne (OLTP) spécialisée dans la comptabilité en partie double (Double-entry bookkeeping)
  • Elle garantit la strong serializability sur la base du protocole de consensus Viewstamped Replication (VR), et ne stocke que les données de comptes et de transferts entre comptes
  • Elle convient aux environnements à fort volume transactionnel et à forte concurrence, comme les switches bancaires internes, le courtage, la billetterie ou le comptage électrique
  • Son architecture confie toutes les opérations d’écriture à un seul nœud (Core), en mettant l’accent sur le scale-up vertical plutôt que le scale-out horizontal
  • Elle vise à maximiser le débit d’un seul nœud grâce à des optimisations proches du matériel comme le traitement par lots, la parallélisation des E/S et un schéma fixe

Résilience et tolérance aux pannes

  • TigerBeetle fournit des modèles explicites et des procédures de reprise pour les défaillances de mémoire, de processus, d’horloge, de stockage et de réseau
  • La durabilité des données garantit l’absence de perte tant qu’une seule réplique reste en vie
    • Si les enregistrements sont corrompus sur toutes les répliques, le système s’arrête de manière sûre
  • Le système suppose divers incidents possibles : pannes matérielles ou logicielles, dérives d’horloge, corruption de disque, latence réseau, pertes ou duplications de paquets
  • Il applique Viewstamped Replication, des techniques de Protocol-Aware Recovery, des checksums sur les blocs de données et un stockage sur plusieurs répliques
  • Il utilise des vérifications à l’exécution (assertions) afin de minimiser l’impact des erreurs et bugs

Méthode de mise à niveau

  • Le binaire contient le code de la version actuelle ainsi que de plusieurs versions précédentes
  • La mise à niveau peut se faire simplement en remplaçant le binaire
  • Tous les nœuds du cluster changent automatiquement de version selon un mode rolling, avec une intervention utilisateur minimale
  • En empêchant qu’une opération validée dans une version soit revalidée en double dans une autre, le système réduit le risque d’incohérence d’état

Modèle temporel

  • Il utilise à la fois une horloge logique fondée sur les vues VR et les numéros d’opération, et une horloge physique hybride
  • Le leader collecte l’heure POSIX de toutes les répliques et les synchronise mutuellement dans les limites d’erreur autorisées
  • Si la synchronisation des horloges échoue pendant plus de 60 secondes, le service est refusé
  • Les horodatages sont exprimés en « nanosecondes depuis l’époque Unix », mais présentent un écart de 27 secondes par rapport au temps POSIX réel
  • En cas de seconde intercalaire ou d’ajustement négatif du temps, l’horloge interne peut ralentir

Modèle de données

  • Le système ne prend en charge que deux types de données : les comptes (account) et les transferts (transfer)
    • Chaque champ a une taille fixe, suit le principe d’immuabilité (immutable) et repose sur des entiers non signés
  • Les comptes sont définis par un identifiant 128 bits personnalisé, un ledger, des flags, une date de création et des champs personnalisés
  • Les transferts incluent notamment l’identifiant du compte débité/crédité, le code, le montant et des champs personnalisés
  • Les transferts peuvent être exécutés immédiatement en une seule étape, ou via un traitement en deux phases (réservation puis exécution/négociation)
    • Les transferts en attente (pending) peuvent être annulés ou expirer
    • Des transferts spéciaux permettent aussi de fermer ou rouvrir un compte

Modèle d’opération et transactions

  • Le client fonctionne à l’unité d’une requête unique (batch) pour mettre à jour ou consulter l’état des données
  • Chaque événement d’une requête est traité séquentiellement comme une transaction atomique
  • Les réexécutions, transactions multi-requêtes et requêtes interactives ne sont pas prises en charge
  • Le système fournit une forte cohérence (Strong Serializability) ainsi qu’une forte cohérence de session
  • Il prend en charge le succès ou l’échec de chaque opération, les codes d’erreur retournés, ainsi que des traitements composés via la fonctionnalité Chain (sous-transactions)

Conception des tests Jepsen

  • Les tests utilisent la bibliothèque Jepsen pour réaliser des tests property-based et de l’injection de pannes
  • Des expériences ont été menées sur des clusters de 3 à 6 nœuds dans divers environnements, dont LXC et EC2
  • En raison des contraintes du modèle de données, il est difficile d’appliquer les vérifications classiques de cohérence de type liste ou ensemble ; la validation s’appuie donc sur l’ordre total des opérations pour vérifier la cohérence d’état et temporelle
  • La détection d’erreurs combine plusieurs approches complémentaires : contrôles de cohérence fondés sur les horodatages, validation de modèles et simulations

Validation du modèle et génération d’opérations

  • Un modèle de machine à états monothread de plus de 1 600 lignes est utilisé pour vérifier finement l’exactitude du comportement de TigerBeetle
  • Il gère le raisonnement et le rollback sur divers cas d’erreur, comme les identifiants dupliqués, les horodatages discontinus, les contraintes de solde ou les chaînes liées
  • Pour améliorer l’efficacité de la validation, diverses techniques sont utilisées : génération d’opérations et d’identifiants, mise à jour d’état et combinaisons probabilistes de requêtes

Injection de pannes

  • Les scénarios de panne de base incluent les crashs de processus (SIGKILL), les pauses (SIGSTOP), les partitions réseau et les modifications d’horloge
  • L’injection couvre aussi des cas détaillés de stockage : mises à niveau de version, simulation de corruption de fichiers, ou corruption partielle limitée à certaines répliques
  • Divers scénarios, dont la corruption de disque en zigzag (helical), sont utilisés pour vérifier que le risque de perte de données est réduit au minimum

Principaux bugs et améliorations

Problèmes dans la gestion des timeouts de requête (#206)

  • Dans la conception de TigerBeetle, les requêtes client n’expirent jamais ; elles sont retentées indéfiniment jusqu’à réception d’une réponse du cluster
  • En pratique, des clients comme Java peuvent lever des exceptions de timeout lors d’opérations asynchrones, et les applications n’ont souvent d’autre choix que d’ajouter leur propre timeout externe
  • Cette conception masque de façon ambiguë les erreurs réseau ou les erreurs certaines, ce qui rend difficile la distinction entre échec certain et erreur incertaine
  • Jepsen recommande d’ajouter des modes de retour selon le type d’échec (certain/incertain) ainsi que des options de retry

Crash JVM causé par une erreur client (#2435)

  • L’encapsulation via threads ou asynchrone utilisée pour contourner les timeouts a provoqué un problème de segfault dans la JVM
  • Le problème venait d’une référence à un champ non correctement initialisé dans le client Java ; il a été corrigé en 0.16.12

Crash client à l’expiration de session (#2484)

  • Un arrêt forcé du client se produisait en raison d’un nombre excessif de sessions
  • À partir de la version 0.16.13, ce comportement a été remplacé par un retour d’erreur

Forte hausse de latence en cas de panne d’un seul nœud (#2739)

  • Une faiblesse de la réplication en anneau faisait que la latence de réponse globale augmentait fortement lorsqu’une partie des nœuds tombait en panne
  • Cause : par défaut, le primaire envoie les messages étape par étape au nœud suivant, ce qui entraîne une attente d’accusé de réception lorsqu’un nœud intermédiaire échoue
  • Depuis la 0.16.30, l’introduction d’une réplication en sens inverse et d’une topologie en anneau dynamique a permis d’améliorer nettement les délais de réponse en situation de panne

Bug de l’API Header du client Java (#2495)

  • L’utilisation d’un objet singleton pour des batchs de réponse vides a entraîné un partage incorrect des headers et horodatages
  • Cela n’affectait pas l’exactitude des données, mais polluait le résultat de l’API Header ; corrigé en 0.16.14

Résultats de requête manquants (#2544)

  • Dans la version 0.16.13, un bug a été signalé sur query_accounts, query_transfers et d’autres requêtes, avec omission d’une partie des résultats, la réponse étant limitée au seul préfixe correct

Conclusion

  • TigerBeetle est spécialisé pour les environnements des domaines financier et comptable exigeant un haut niveau de sûreté et de tolérance aux pannes
  • La série de tests Jepsen a mis en lumière divers problèmes de résilience, de cohérence, de modèle d’opération et de performance
  • Une collaboration active a permis des améliorations concrètes sur la reprise après incident, la gestion des erreurs côté client, la réplication et l’automatisation des mises à niveau
  • Les versions récentes offrent un niveau de fiabilité élevé avec une meilleure réponse aux pannes, de meilleures garanties de connexion et de réponse, ainsi qu’une cohérence accrue des opérations

(Une partie de ce contenu a été rédigée en s’appuyant sur diverses sources open source, dont GitHub, la documentation officielle de TigerBeetle et les rapports de test Jepsen.)

1 commentaires

 
GN⁺ 2025-06-07
Avis Hacker News
  • Voir aussi l’article informatif « Fuzzer Blind Spots (Meet Jepsen!) », avec le lien https://tigerbeetle.com/blog/2025-06-06-fuzzer-blind-spots-meet-jepsen/

  • Partage d’une expérience consistant à toujours valider en dernier les affirmations de fiabilité et de scalabilité de TigerBeetle via un rapport Jepsen ; appréciation du fait que ce rapport ait trouvé plusieurs problèmes, que ceux-ci aient été corrigés rapidement, et que la suite de tests interne ait aussi été renforcée pour éviter la répétition de bugs similaires à l’avenir ; avec une telle posture, espoir qu’à horizon 10 ans, dans le domaine des bases de données spécialisées pour la finance, TigerBeetle atteigne un statut de choix par défaut du niveau de « utilisez simplement Postgres », tout en saluant l’excellent travail d’aphyr dont beaucoup disent avoir énormément appris

    • TigerBeetle contient plus de 6 000 assertions ; certaines étaient trop strictes et ont provoqué quelques crashs, mais ces assertions ont justement joué exactement le rôle d’alerte attendu ; en pratique, Jepsen n’a trouvé qu’un petit bug de correctness dans une fonctionnalité de test interne destinée à faciliter l’audit côté client Java, ainsi qu’un bug de correctness sans impact sur la durability ; les détails sont expliqués dans ce lien ; TigerBeetle est conçu et testé pour tolérer plus de pannes que Postgres, adopte un modèle explicite de défaillance du stockage, intègre des résultats de recherche inexistants à l’époque de la sortie de Postgres, et applique diverses garanties de stabilité comme le Deterministic Simulation Testing et les standards de code critique de la NASA ; concrètement, pour des scénarios documentés de perte de données dans Postgres, TigerBeetle peut détecter et récupérer ; pour plus de détails, recommandation de consulter la section sur le helical fault injection de Kyle ou la vidéo de sa conférence à QCon London
    • Chaque lecture d’un rapport de Kyle donne l’impression de progresser d’un niveau en systèmes distribués
  • Joie de voir TigerBeetle tenir ses promesses après validation par aphyr, avec l’idée encourageante qu’une bonne approche peut produire de vrais bons résultats ; question sur la manière dont sont gérées en pratique la consistency et la reprise entre TigerBeetle et des systèmes externes moins fiables, sachant qu’en production les données autres que Account ou Transfer restent souvent dans des systèmes externes et des bases séparées

    • Réponse de Joran de TigerBeetle expliquant le pattern d’intégration : il recommande généralement une architecture séparant control plane (OLGP généraliste, par ex. Postgres) et data plane (OLTP, par ex. TigerBeetle) ; les informations utilisateur vont dans l’OLGP, sorte de « classeur », tandis que les données transactionnelles (inventaire → panier → paiement, etc.) vont dans l’OLTP, assimilé à un « coffre-fort » ; il est possible d’associer jusqu’à 3 identifiants de données utilisateur à chaque compte ou transfert pour relier les entités OLGP et les événements ; cette séparation apporte des avantages d’évolutivité, d’exploitation et d’administration indépendantes ; l’exemple de la banque, qui doit distinguer cash (coffre-fort) et information (classeur), est jugé parlant ; comme la fréquence des transactions et celle des changements d’information (nom, e-mail, etc.) diffèrent en pratique, cette structure est présentée comme rationnelle ; pour la cohérence des données, il est recommandé dans le write path d’écrire d’abord les données dépendantes dans l’OLGP (et dans les stockages externes nécessaires comme S3), puis de commit dans TigerBeetle en dernier ; dans le read path, il est proposé d’interroger systématiquement TigerBeetle comme source of record, afin de s’appuyer sur sa strict serializability ; un lien vers la documentation d’architecture est aussi donné
  • Avis selon lequel, si l’on a lu le billet de Jepsen sur les blind spots du fuzzer, ce rapport TigerBeetle est encore plus intéressant ; le cas de segfault côté JNI n’aurait peut-être pas été empêché même avec un langage memory-safe comme Rust, mais l’approche Zig/TigerStyle de TigerBeetle est jugée convaincante en matière de sûreté mémoire

    • Un des bugs aurait aussi pu être évité en Rust ; en pratique, la plupart sont déjà bloqués par des assertions, et sans TigerStyle la situation aurait pu être plus risquée
  • Petit golf clap admiratif pour le sens du titre de la section « Panic! At the Disk 0 »

  • Expression d’une profonde impression face à ce rapport Jepsen détaillé et certifiant ; forte attente malgré l’absence encore de version v1.0 ; compliment particulier au fait que les fondateurs partagent activement leurs insights dans le fil

    • Mention de la minutie de Kyle et de la finesse presque artistique du rapport, avec en plus l’attente d’une nouvelle présentation à SD25 Amsterdam
  • Remarque sur le fait qu’en test de systèmes distribués, il est intéressant et même « manifestement évident » que le système doive lui-même rapporter l’ordre/le temps des événements internes afin de pouvoir être comparé précisément à un modèle externe, condition jugée indispensable à une vérification exacte

    • Dans un environnement de strict serializability, cette approche est possible, alors qu’elle ne l’est pas avec des garanties de cohérence plus faibles où une timeline globale unique est impossible ; intérêt pour ce pattern méta dans lequel l’introduction d’un problème plus difficile simplifie paradoxalement le système ; par exemple, le fait d’ajouter par défaut un protocole de panne/récupération disque permet aussi d’obtenir « gratuitement » la synchronisation d’état d’une replica lente
  • Après lecture du rapport Jepsen, du blog associé et du code d’intégration Antithesis, question à visée d’apprentissage sur la portée et l’efficacité des tests ; l’auteur pensait que TigerBeetle effectuait déjà des tests très complets avec Antithesis, et se demande donc comment les bugs trouvés par Jepsen ont pu lui échapper ; demande concrète sur la différence entre les tests Antithesis et Jepsen, et sur ce qui distingue finalement leur couverture de celle des tests internes

    • Dans la réponse complémentaire d’aphyr, il est expliqué que le generative testing des systèmes distribués requiert 1) l’environnement d’exécution du système 2) le générateur de charge 3) l’auditeur/vérificateur ; Antithesis couvre surtout le point 1 en fournissant un environnement de simulation déterministe ; Jepsen injecte des pannes au niveau des vraies machines et de l’OS ; le VOPR de TigerBeetle exécute l’ensemble du cluster dans un seul thread ; ces différentes formes de simulation se complètent avec leurs avantages et limites respectifs ; dans ce bug précis, les éléments décisifs étaient en fin de compte 2) et 3), c’est-à-dire la génération de workload et l’auditeur de vérification ; c’est le code Clojure spécifique à TigerBeetle écrit par aphyr qui a provoqué puis détecté le bug, après quoi leur équivalent interne a aussi été patché ; le problème le plus important concernait davantage VOPR que la base elle-même ; comme toujours en base distribuée, la possibilité de bugs demeure, d’où l’importance fondamentale de concevoir plusieurs générateurs et stratégies de test
    • Le blog de TigerBeetle détaille aussi ce point : en résumé, les tests utilisés dans Antithesis ne couvraient pas la combinaison de requêtes croisées et de valeurs out-of-order nécessaire pour déclencher ce bug, tandis que Jepsen a réussi à le détecter parce qu’il réunissait cette combinaison ; il est également rappelé que le générateur de test de Jepsen a lui aussi certaines limites, ce qui renforce l’idée qu’il faut plusieurs conceptions de générateurs
    • 90 % des tests internes de simulation de latence sont exécutés dans VOPR (leur simulateur maison), qui tourne 24/7 sur 1 000 cœurs CPU ; Antithesis est utilisé comme couche supplémentaire ; pour comprendre pourquoi ce bug du moteur de requêtes est passé au travers, invitation à lire ce billet
  • Quelqu’un dit s’intéresser à TigerBeetle et trouve étrange qu’il n’existe pas, d’après la documentation client, de client C ou Zig ; question sur le fait de savoir si ce client n’existe pas ou est encore en développement

  • Question sur une éventuelle utilisation de TigerBeetle par de grandes banques ou sociétés de courtage

    • Réponse de Joran de TigerBeetle : le produit doit être utilisé, en coopération avec la Gates Foundation, pour construire à Luanda le système national de paiement numérique 2.0 de la banque centrale électronique ; côté entreprise, des clients en production exploitent déjà le service à plus de 100 millions de transactions par mois ; un contrat a récemment été signé avec une licorne fintech européenne valorisée 2 milliards de dollars, et d’autres accords avancent aux États-Unis ; la demande d’adoption de TigerBeetle augmente dans le monde entier avec les besoins en traitement des transactions en temps réel ; les fondateurs de Clear Street, société de courtage importante de Wall Street, font aussi partie des investisseurs ; liens associés : mojaloop.io, blog de TigerBeetle, présentation de l’entreprise
    • Pas une grande banque ni une bourse, mais l’auteur dit l’utiliser déjà pour un nouveau produit dans une grande fintech
    • Avis selon lequel l’absence de grands noms mis en avant sur le site laisse penser qu’il n’y a peut-être pas encore de référence majeure publique ; à ce stade, la plus forte forme d’endorsement semblerait plutôt venir d’un youtubeur influent