Catàleg i extracció
El valor és opac per al motor, així que un índex necessita una funció que extregui les claus a indexar. Es registra quan la base de dades s'obre:
db.define_index(IndexDef {
id: 1,
name: "by_endpoint",
version: 3,
unique: false,
extract: |key, value| -> Vec<Vec<u8>> { /* ... */ },
})?;El motor crida extract a cada put i delete, i manté l'índex dins de la mateixa transacció que les dades. Aquesta atomicitat és exactament el que es compra en posar els índexs al motor en lloc de mantenir-los al codi de qui crida.
Emmagatzematge, un espai de claus per índex:
No únic:
[0x80 | id][indexed_key][primary_key] → emptyÚnic:
[0x80 | id][indexed_key] → primary_key, amb la unicitat garantida per col·lisió de clau — no cal cap comprovació extra.
Versionat de l'extractor. El camp version s'emmagatzema al catàleg. Si una base de dades es reobre amb una funció d'extracció diferent i la versió no es puja, l'índex mentiria sense que es pogués detectar. Amb la versió emmagatzemada, el motor detecta el desajust i, segons la configuració, es nega a obrir o reconstrueix l'índex. És l'única defensa possible quan la lògica d'indexació viu en una funció que aporta qui crida.
Esborrat per rang
delete_range sobre un rang de claus contigu desenganxa subarbres sencers. Els números de pàgina de les fulles viuen als seus nodes pare, així que les fulles mai es llegeixen: es recorren els nodes interns, els seus punters es bolquen a la llista lliure en bloc, i el subarbre es desenganxa. El cost és proporcional al nombre de pàgines internes, no al nombre d'entrades esborrades.
Podar 833.000 registres amb pàgines de 4 KB significa llegir unes 25 pàgines internes en lloc de les 8.000 fulles que contenen les dades — dos ordres de magnitud menys d'E/S. Sense aquesta operació, la poda diària de retenció a cada node seria un recorregut complet de l'arbre, exactament el cost de CPU que el projecte existeix per evitar.
Regla derivada. Qualsevol índex definit sobre un espai de claus amb retenció ha de posar el component temporal primer a la seva clau d'índex. Amb [bucket][endpoint][pk], esborrar les entrades d'índex corresponents és un altre rang contigu. Sense el prefix temporal, les entrades d'índex acaben disperses per tot l'índex i la poda torna a ser un escaneig complet.