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>
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"puislen(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" → 0xCBF43926testé) — 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).
fsyncconfigurable (Sync::Always/Sync::Never+sync()manuel).- Index en mémoire (offset de chaque record) reconstruit au scan d'ouverture →
iter_from(seq)etreplaydé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 :
Sync::EveryN(n)/ group commit — amortir lefsyncsur n appends ;- index persistant (fichier
.idxside-car, régénérable) — ouverture O(1) sur les très gros journaux au lieu du scan complet ; - segments + compaction — découper en fichiers de taille bornée, archiver ou fusionner les vieux segments (le chemin vers Kafka) ;
- mmap en lecture — itération zéro-copie ;
- CRC vectorisé (slicing-by-8, ou
crc32matériel SSE4.2) — même résultat, ~10× plus vite ; - compression des payloads — brancher
tsoin-codec: appender la coordonnée de Babel au lieu des octets bruts.