Files
bions-rust/bion-tsoinlog
cloudion-labo 2556698dd3 🦀 bions-rust vague 1 : 6 briques build-your-own-x — on ne les rebuild plus jamais
Principe RS-7 : « dès qu'on build un truc, plus personne n'a à le rebuild —
la seule chose à faire est l'optimisation. » (nexus/RepoVerse)
- bion-vc      : horloges vectorielles + MvReg fork-visible (LA spec
                 xion-relativiste-v0 enfin codée — CRDT testé par permutations)
- bion-triplet : l'Adressage Génératif (gen_hash BLAKE3, coords, résidu ;
                 résidu vide quand déjà-su ; align décidable au bit)
- bion-tsoinlog: journal append-only rejouable (CRC32 maison, crash-recovery)
- bion-kv      : magasin clé-valeur bitcask (compaction atomique, tombstones)
- bion-regex   : moteur Thompson NFA linéaire (jamais exponentiel — Russ Cox)
- bion-git     : mini-git content-addressed (SHA-1 maison + vecteurs officiels,
                 branches divergentes = le fork visible)
129 tests verts, clippy 0 warning, doc française = chaque bion est un cours.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 01:07:31 +00:00
..

bion-tsoinlog — le journal append-only rejouable

Quoi

La primitive de la machine à tsoins : un journal d'événements sur disque où l'on ne fait qu'appender (jamais modifier, jamais effacer) et que l'on peut rejouer — en entier ou par tranche [from, to). Chaque événement = un topic (UTF-8) + un payload (octets opaques), et reçoit un numéro de séquence Seq strictement croissant.

  • std-only, zéro dépendance, zéro unsafe.
  • Format binaire v1 minuscule et documenté : magic "TSOINLG1" puis len(u32 LE) · topic_len(u16 LE) · topic · payload · crc32(u32 LE).
  • CRC-32 (IEEE) implémenté maison (table générée à la compilation, vecteur canonique "123456789" → 0xCBF43926 testé) — pas de lib, build-your-own.
  • Récupération après crash : à l'ouverture, la queue tronquée ou au CRC faux est détectée et retaillée ; l'historique valide survit toujours (testé en tronquant et en corrompant le fichier à la main).
  • fsync configurable (Sync::Always / Sync::Never + sync() manuel).
  • Index en mémoire (offset de chaque record) reconstruit au scan d'ouverture → iter_from(seq) et replay démarrent en seek direct, pas de re-scan.

Pourquoi

C'est la structure au cœur de Kafka, des WAL de bases de données, de l'event sourcing, de git — et de la machine à tsoins : enregistrer le réel dans l'ordre, pouvoir le revivre. Le complément exact de tsoin-codec (~/xerboxion-rt/crates/tsoin-codec) : le codec transforme un contenu en coordonnée de Babel, le log mémorise durablement la séquence des événements. On appende volontiers des tsoins-de-fil comme payloads ; le log, lui, ne présuppose rien sur le contenu.

Exemple

use bion_tsoinlog::{TsoinLog, Seq, Sync};

let mut log = TsoinLog::open("journal.tsoinlog")?;      // Sync::Never par défaut
// ou : TsoinLog::open_with("journal.tsoinlog", Sync::Always)?  // durable à chaque append

let s0 = log.append("capteur/temp", b"21.5")?;           // → Seq(0)
let s1 = log.append("bus/emit", b"{\"topic\":\"leds\"}")?; // → Seq(1)

// Tout relire :
for rec in log.iter()? {
    let rec = rec?;
    println!("#{} [{}] {} octets", rec.seq.0, rec.topic, rec.payload.len());
}

// Rejouer une tranche [1, 2) (from inclus, to exclu, comme un Range) :
log.replay(Seq(1), Seq(2), |rec| { /* ré-appliquer l'événement */ })?;
# std::io::Result::Ok(())

API publique (stable — rétrocompatibilité éternelle)

Élément Rôle
TsoinLog::open(path) / open_with(path, Sync) ouvre/crée + scan + réparation
append(topic, payload) -> io::Result<Seq> appende un événement
iter() / iter_from(Seq) itère (instantané cohérent, seek O(1))
replay(from, to, f) -> io::Result<u64> rejoue [from, to), retourne le compte
sync() / set_sync(Sync) contrôle du fsync
len() / is_empty() / next_seq() état du journal
Seq(u64) / Record { seq, topic, payload } / Sync types de données
crc32(&[u8]) -> u32 / MAGIC / MAX_TOPIC_LEN briques exposées

Build-your-own-x correspondants

Dans build-your-own-x :

  • Build your own Database — le log est un write-ahead log (WAL) minimal ;
  • Build your own Git — un historique append-only adressé par position ;
  • le CRC-32 maison est la brique commune à zip/gzip/PNG/Ethernet (« build your own checksum » depuis la division polynomiale dans GF(2)).

Comment l'optimiser (l'invitation au fork)

Le format v1 est volontairement le plus simple qui soit correct. Pistes, dans l'ordre de rentabilité, sans jamais casser la lecture des fichiers v1 :

  1. Sync::EveryN(n) / group commit — amortir le fsync sur n appends ;
  2. index persistant (fichier .idx side-car, régénérable) — ouverture O(1) sur les très gros journaux au lieu du scan complet ;
  3. segments + compaction — découper en fichiers de taille bornée, archiver ou fusionner les vieux segments (le chemin vers Kafka) ;
  4. mmap en lecture — itération zéro-copie ;
  5. CRC vectorisé (slicing-by-8, ou crc32 matériel SSE4.2) — même résultat, ~10× plus vite ;
  6. compression des payloads — brancher tsoin-codec : appender la coordonnée de Babel au lieu des octets bruts.