🦀 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>
This commit is contained in:
cloudion-labo
2026-08-16 01:07:31 +00:00
commit 2556698dd3
27 changed files with 6518 additions and 0 deletions

93
bion-kv/README.md Normal file
View File

@@ -0,0 +1,93 @@
# bion-kv 🗃️
Magasin clé-valeur durable, **std-only, zéro dépendance**, style
[Bitcask](https://riak.com/assets/bitcask-intro.pdf) : un log append-only sur
disque + un index en mémoire + une compaction atomique. La brique
*build-your-own-database* du xerboxion — construite une fois, plus jamais
rebâtie, seulement optimisée (principe RS-7).
## Quoi
- `Kv::open(dir)` — ouvre/crée le magasin, reconstruit l'index en scannant le log
- `put(k, v)` — un append séquentiel, O(1)
- `get(k) -> Option<Vec<u8>>` — un lookup mémoire + au plus un seek/read
- `delete(k)` — appose un **tombstone** (on n'efface jamais le passé)
- `compact()` — réécrit uniquement les données vivantes, bascule **atomique par `rename`**
- `sync()` — fsync explicite quand on veut la garantie coupure-de-courant
- CRC32 (IEEE) **implémenté maison** (`bion_kv::crc32`, table `const` calculée à la compilation)
## Pourquoi Bitcask
1. **Rapide en écriture** : tout est un append séquentiel — le motif d'E/S le
plus rapide sur disque comme sur SSD. Pas d'arbre à rééquilibrer, pas de
page à réécrire.
2. **Robuste** : le passé est immuable ; un crash ne peut abîmer que la
*queue* du log. À la réouverture, chaque record est vérifié par CRC32 et
la queue malade est tronquée — les données saines survivent toujours.
3. **Simple** : le format tient en une ligne
(`crc32 | klen | vlen | clé | valeur`, tombstone = `vlen == 0xFFFFFFFF`),
la récupération = relire le log. Tout le moteur tient dans un fichier
source lisible en une soirée : c'est aussi un cours.
Le compromis assumé : la RAM porte l'index (proportionnel au **nombre de
clés**, pas au volume des valeurs), et le log grossit avec les versions
mortes — d'où `compact()`.
## Lien avec le boxion store
Le boxion store (`jOSBoxion`) expose déjà un KV par-utilisateur aux ploxions.
`bion-kv` en est le moteur côté **core Rust** : même contrat
(put/get/delete durable et réouvrable), mais embarquable partout — dans
`xerboxion-rt`, dans l'OS bare-metal, dans un bion WASM. On ne réinvente pas
le KV à chaque étage : on branche ce bloc.
## Exemple
```rust
let mut kv = bion_kv::Kv::open("/tmp/mon-magasin")?;
kv.put(b"xer", b"renderer")?;
assert_eq!(kv.get(b"xer")?.as_deref(), Some(&b"renderer"[..]));
kv.delete(b"xer")?; // tombstone dans le log
assert_eq!(kv.get(b"xer")?, None);
kv.compact()?; // le log ne garde que le vivant
# Ok::<(), std::io::Error>(())
```
## Build-your-own-x
Même famille que ces guides du dépôt
[codecrafters-io/build-your-own-x](https://github.com/codecrafters-io/build-your-own-x)
(section *Build your own Database*) :
- *Build Your Own Fast, Persistent KV Store in Rust* — exactement ce crate
- l'article fondateur : **Bitcask — A Log-Structured Hash Table for Fast
Key/Value Data** (Riak)
- cousins de design : les WAL de SQLite/Postgres, les SSTables de
LevelDB/RocksDB (Bitcask = le cas dégénéré à un seul niveau)
## Comment l'optimiser (l'invitation au fork)
L'API est **stable pour toujours** ; tout ce qui suit se fait dessous, sans
casser un seul appelant :
- **Fichiers multiples + hint files** (le vrai Bitcask) : fermer le segment
actif à N Mo, compacter segment par segment, et écrire un *hint file*
(clé → offset) pour rouvrir sans relire les valeurs.
- **Compaction incrémentale** : aujourd'hui `compact()` copie tout le vivant
d'un coup ; en segments, on ne compacte que les segments les plus « morts ».
- **Batching/`put_many`** : grouper plusieurs records dans un seul `write_all`
+ un seul fsync.
- **CRC matériel** : remplacer la table par l'instruction `crc32c` (SSE4.2)
ou du SIMD — le format ne change pas si on garde le polynôme IEEE.
- **Index plus dense** : `HashMap<Vec<u8>, _>` → arène de clés + table à
adressage ouvert, ou un ART pour les scans par préfixe.
- **`get` sans `&mut`** : `pread`/`read_at` (FileExt) pour des lectures
concurrentes sans déplacer le curseur.
## Tests
`cargo test -p bion-kv` — 17 tests unitaires + 2 doc-tests : put/get/delete,
écrasements, clés/valeurs binaires et vides, persistance après réouverture,
tombstones rejoués, compaction (taille réduite, données intactes, réouverture),
queue corrompue (bruit, record tronqué, bit-flip détecté par CRC), fichier
étranger refusé, brouillon de compaction orphelin ignoré, 500 clés.