Files
bions-rust/bion-vc
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-vc — horloges vectorielles + Multi-Value Register « fork visible »

Quoi

La brique causale du xerboxion, std-only, zéro dépendance, zéro unsafe :

  • VectorClock — l'horloge logique canonique (Fidge/Mattern) : increment(node), merge(&other) (max composante par composante), observe(&other, node) (règle de réception complète), et la comparaison causale causality()Before | After | Concurrent | Equal. PartialOrd est implémenté honnêtement : partial_cmp rend None pour deux horloges concurrentes, parce que l'ordre causal est partiel — c'est toute la leçon.
  • Stamped<T> — une valeur + son estampille causale (l'enveloppe du tsoin, §2 de la spec).
  • MvReg<T> — le Multi-Value Register : set() (écrasement causal uniquement — de ce que l'écrivain a vu), apply()/merge() qui gardent les branches concurrentes, values()/branches()/is_forked() pour les exposer, resolve() pour fermer un fork explicitement avec un VC qui domine toutes les branches.
  • Sérialisation texte stable (to_text/from_text, formats gelés a:2,b:1 et mvr1), sans serde : deux répliques convergées produisent le même texte, octet pour octet.

Pourquoi

C'était l'algo manquant du projet : la spec xion-relativiste-v0 (resources/papers/xion-relativiste-v0.md de my_website2) gèle la décision RS-7 —

Divergence = branche, retrouvaille = merge, conflit = fork visible. Jamais d'écrasement silencieux.

— mais personne ne l'avait codée. La réplication actuelle du labo fait du LWW par horloge murale (StateReplicationController, savedAt/cmpAt()), qui jette silencieusement une des deux écritures concurrentes. Ce bion est l'arbitre de remplacement du chemin de migration §6 de la spec : le merge devient un CRDT (commutatif, associatif, idempotent — testé par permutations), l'essaim converge sans coordinateur, et aucune branche ne meurt sans un acte explicite.

Exemple

use bion_vc::{MvReg, Causality, VectorClock};

// Deux répliques divergent : chacune écrit « sa » valeur du même point.
let mut bord = MvReg::new();
bord.set("cubion-bord", "cap sur Europe".to_string());
let mut sol = MvReg::new();
sol.set("cubion-sol", "cap sur Titan".to_string());

// Retrouvailles : merge. Personne ne perd rien → fork VISIBLE.
bord.merge(&sol);
assert!(bord.is_forked());
assert_eq!(bord.values().len(), 2);

// Fermer le fork est un acte explicite, tracé, qui domine les deux branches.
bord.resolve("cubion-bord", |branches| {
    let mut caps: Vec<&str> = branches.iter().map(|b| b.value.as_str()).collect();
    caps.sort();
    caps.join(" PUIS ")
});
assert!(!bord.is_forked());

// Et l'horloge seule, pour estampiller n'importe quoi d'autre :
let mut vc = VectorClock::new();
vc.increment("moi");
assert_eq!(vc.causality(&VectorClock::new()), Causality::After);

Liens build-your-own-x

Le dépôt build-your-own-x n'a pas (encore) de section « Build your own CRDT » — ce bion en tient lieu, depuis les principes, avec les sources primaires qu'un tel tutoriel citerait :

  • Lamport, Time, Clocks, and the Ordering of Events in a Distributed System, CACM 1978 — le happened-before.
  • Fidge 1988, Mattern 1989 — le vector clock qui capture la causalité complète.
  • Shapiro, Preguiça, Baquero, Zawirski, Conflict-free Replicated Data Types, SSS 2011 — les propriétés CRDT et la spec du MV-Register.
  • DeCandia et al., Dynamo, SOSP 2007 — les « siblings » : le fork visible en production depuis 2007.

Parents dans build-your-own-x : « Build your own Git » (le merge à trois voies et les branches visibles sont le même geste) et « Build your own Database » (la réplication).

Comment l'optimiser (l'invitation au fork)

L'API est gelée ; tout le reste est à toi. Pistes réelles, dans l'ordre du §7 de la spec :

  1. Compaction des VC — un VC croît en O(n) nœuds rencontrés. À grande échelle : deltas de VC, dotted version vectors (Preguiça et al. 2010), ou VC hiérarchiques par agrégat. Le format texte réserve la place (c'est une map : ajoute des nœuds composites).
  2. apply en O(n²) sur le nombre de branches — correct et simple ; si un point accumule des centaines de forks non résolus, indexer les branches par nœud dominant.
  3. Interner les ids de nœuds (Rc<str>/index dans un registre) au lieu de String par entrée — le clone de VC est aujourd'hui la seule allocation chaude.
  4. GC des branches — un MVR n'oublie jamais tout seul (c'est voulu) : une politique de rétention/archivage se construit au-dessus, jamais dedans.

Règle du fork : garde to_text/from_text compatibles (rétrocompatibilité éternelle) — un format v2 porte un nouvel en-tête, il ne mute jamais v1.

cargo test -p bion-vc

Ne pas nuire. Aucune montre n'a raison contre l'autre ; aucune branche ne meurt en silence.