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>
4.3 KiB
bion-kv 🗃️
Magasin clé-valeur durable, std-only, zéro dépendance, style Bitcask : 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 logput(k, v)— un append séquentiel, O(1)get(k) -> Option<Vec<u8>>— un lookup mémoire + au plus un seek/readdelete(k)— appose un tombstone (on n'efface jamais le passé)compact()— réécrit uniquement les données vivantes, bascule atomique parrenamesync()— fsync explicite quand on veut la garantie coupure-de-courant- CRC32 (IEEE) implémenté maison (
bion_kv::crc32, tableconstcalculée à la compilation)
Pourquoi Bitcask
- 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.
- 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.
- 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
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 (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 seulwrite_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. getsans&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.