use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock};
use std::time::SystemTime;
use crate::serve::kv_persist::format::{
self, BlockHash, EnvelopeHeader, ModelFingerprint, ParentBlockHash, CURRENT_FORMAT_VERSION,
};
#[derive(Clone, Debug)]
pub struct BlockMeta {
pub hash: BlockHash,
pub parent: ParentBlockHash,
pub model_fp: ModelFingerprint,
pub payload_kind: String,
pub codec_version: u32,
pub n_tokens: u32,
pub file_path: PathBuf,
pub mtime: SystemTime,
pub bytes_on_disk: u64,
}
impl BlockMeta {
fn from_header_and_path(
header: &EnvelopeHeader,
path: PathBuf,
mtime: SystemTime,
bytes_on_disk: u64,
) -> Self {
Self {
hash: header.block_hash,
parent: header.parent_block_hash,
model_fp: header.model_fingerprint,
payload_kind: header.payload_kind.clone(),
codec_version: header.codec_version,
n_tokens: header.n_tokens,
file_path: path,
mtime,
bytes_on_disk,
}
}
}
#[derive(Debug, PartialEq, Eq)]
enum RecoveryOutcome {
Indexed,
QuarantinedHeaderError,
QuarantinedVersionMismatch,
IgnoredTmpFile,
}
#[derive(Clone)]
pub struct BlockIndex {
inner: Arc<RwLock<HashMap<BlockHash, BlockMeta>>>,
}
impl Default for BlockIndex {
fn default() -> Self {
Self::new()
}
}
impl BlockIndex {
pub fn new() -> Self {
Self {
inner: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn insert(&self, meta: BlockMeta) {
let mut guard = self
.inner
.write()
.expect("BlockIndex inner RwLock poisoned");
guard.insert(meta.hash, meta);
}
pub fn lookup(&self, hash: &BlockHash) -> Option<BlockMeta> {
let guard = self.inner.read().expect("BlockIndex inner RwLock poisoned");
guard.get(hash).cloned()
}
pub fn remove(&self, hash: &BlockHash) -> Option<BlockMeta> {
let mut guard = self
.inner
.write()
.expect("BlockIndex inner RwLock poisoned");
guard.remove(hash)
}
pub fn iter_by_model(&self, model_fp: &ModelFingerprint) -> Vec<BlockMeta> {
let guard = self.inner.read().expect("BlockIndex inner RwLock poisoned");
guard
.values()
.filter(|m| m.model_fp == *model_fp)
.cloned()
.collect()
}
pub fn total_bytes_on_disk(&self) -> u64 {
let guard = self.inner.read().expect("BlockIndex inner RwLock poisoned");
guard.values().map(|m| m.bytes_on_disk).sum()
}
pub fn block_count(&self) -> usize {
let guard = self.inner.read().expect("BlockIndex inner RwLock poisoned");
guard.len()
}
pub fn snapshot_all(&self) -> Vec<BlockMeta> {
let guard = self.inner.read().expect("BlockIndex inner RwLock poisoned");
guard.values().cloned().collect()
}
pub fn rebuild_from_disk(cache_root: &Path) -> std::io::Result<BlockIndex> {
let index = BlockIndex::new();
let models_dir = cache_root.join("models");
if !models_dir.exists() {
return Ok(index);
}
for slug_ent in std::fs::read_dir(&models_dir)? {
let slug_ent = slug_ent?;
let slug_path = slug_ent.path();
if !slug_path.is_dir() {
continue;
}
let kv_dir = slug_path.join("kv");
if !kv_dir.exists() {
continue;
}
for fanout_ent in std::fs::read_dir(&kv_dir)? {
let fanout_ent = fanout_ent?;
let fanout_path = fanout_ent.path();
if !fanout_path.is_dir() {
continue;
}
for blk_ent in std::fs::read_dir(&fanout_path)? {
let blk_ent = blk_ent?;
let blk_path = blk_ent.path();
if !blk_path.is_file() {
continue;
}
let _ = scan_one(&slug_path, &blk_path, &index)?;
}
}
}
Ok(index)
}
}
fn scan_one(
slug_path: &Path,
blk_path: &Path,
index: &BlockIndex,
) -> std::io::Result<RecoveryOutcome> {
let name = blk_path
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("")
.to_string();
if name.contains(".tmp.") {
return Ok(RecoveryOutcome::IgnoredTmpFile);
}
let header = match format::read_envelope_header(blk_path) {
Ok(h) => h,
Err(_) => {
quarantine(slug_path, blk_path)?;
return Ok(RecoveryOutcome::QuarantinedHeaderError);
}
};
if header.format_version != CURRENT_FORMAT_VERSION.0 {
quarantine(slug_path, blk_path)?;
return Ok(RecoveryOutcome::QuarantinedVersionMismatch);
}
let metadata = std::fs::metadata(blk_path)?;
let mtime = metadata.modified().unwrap_or(SystemTime::UNIX_EPOCH);
let bytes = metadata.len();
let meta = BlockMeta::from_header_and_path(&header, blk_path.to_path_buf(), mtime, bytes);
index.insert(meta);
Ok(RecoveryOutcome::Indexed)
}
fn quarantine(slug_path: &Path, blk_path: &Path) -> std::io::Result<()> {
let q_dir = slug_path.join("kv-quarantine");
if !q_dir.exists() {
std::fs::create_dir_all(&q_dir)?;
}
let name = blk_path.file_name().ok_or_else(|| {
std::io::Error::other(format!("blk path has no name: {}", blk_path.display()))
})?;
let dest = q_dir.join(name);
match std::fs::rename(blk_path, &dest) {
Ok(()) => Ok(()),
Err(_) => {
std::fs::copy(blk_path, &dest)?;
std::fs::remove_file(blk_path)?;
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::serve::kv_persist::format::{
compute_block_hash, compute_model_fingerprint, write_envelope, BLOCK_TOKENS,
};
use sha2::{Digest, Sha256};
use std::process;
use std::sync::atomic::{AtomicU32, Ordering};
fn temp_dir(label: &str) -> PathBuf {
static COUNTER: AtomicU32 = AtomicU32::new(0);
let n = COUNTER.fetch_add(1, Ordering::SeqCst);
let pid = process::id();
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
let dir = std::env::temp_dir().join(format!("hf2q-kv-idx-{label}-{pid}-{nanos}-{n}"));
std::fs::create_dir_all(&dir).expect("temp_dir mkdir");
dir
}
fn fixture_fp(seed: &str) -> ModelFingerprint {
compute_model_fingerprint(
seed,
"Q4_0",
"hf2q-test-1.0.0",
"deadbeefcafebabe1122334455667788",
"<|im_start|>...<|im_end|>",
)
}
fn make_block(
fp: ModelFingerprint,
parent: ParentBlockHash,
seed: u32,
) -> (Vec<u8>, EnvelopeHeader) {
let body: Vec<u8> = (0..512u32)
.flat_map(|i| (i.wrapping_add(seed)).to_le_bytes())
.collect();
let mut h = Sha256::new();
h.update(&body);
let bh: [u8; 32] = h.finalize().into();
let header = EnvelopeHeader {
format_version: CURRENT_FORMAT_VERSION.0,
model_fingerprint: fp,
block_hash: BlockHash(bh),
parent_block_hash: parent,
payload_kind: "kv-dense-bf16".into(),
codec_version: 1,
n_tokens: BLOCK_TOKENS,
};
(body, header)
}
fn block_path(root: &Path, fp: &ModelFingerprint, hash: &BlockHash) -> PathBuf {
let hex = hash.to_string();
let fanout = &hex[..1];
root.join("models")
.join(fp.short_hex())
.join("kv")
.join(fanout)
.join(format!("{hex}.safetensors"))
}
fn synthetic_meta(fp: ModelFingerprint, seed: u32, bytes: u64) -> (BlockHash, BlockMeta) {
let mut h = Sha256::new();
h.update(seed.to_le_bytes());
h.update(b"-synthetic-");
h.update(fp.0);
let bh = BlockHash(h.finalize().into());
let meta = BlockMeta {
hash: bh,
parent: ParentBlockHash(None),
model_fp: fp,
payload_kind: "synthetic".into(),
codec_version: 1,
n_tokens: 256,
file_path: PathBuf::from(format!("/dev/null/{seed}")),
mtime: SystemTime::UNIX_EPOCH,
bytes_on_disk: bytes,
};
(bh, meta)
}
#[test]
fn insert_lookup_remove_round_trip() {
let idx = BlockIndex::new();
assert_eq!(idx.block_count(), 0);
assert_eq!(idx.total_bytes_on_disk(), 0);
let fp = fixture_fp("model-A");
let (h1, m1) = synthetic_meta(fp, 1, 1024);
let (h2, m2) = synthetic_meta(fp, 2, 2048);
idx.insert(m1.clone());
idx.insert(m2.clone());
assert_eq!(idx.block_count(), 2);
let got = idx.lookup(&h1).expect("h1 present");
assert_eq!(got.bytes_on_disk, 1024);
assert_eq!(got.hash, h1);
let (missing, _) = synthetic_meta(fp, 999, 0);
assert!(idx.lookup(&missing).is_none());
let mut m1b = m1.clone();
m1b.bytes_on_disk = 4096;
idx.insert(m1b);
assert_eq!(idx.lookup(&h1).expect("h1 present").bytes_on_disk, 4096);
assert_eq!(idx.block_count(), 2, "overwrite, no growth");
let removed = idx.remove(&h2).expect("h2 present");
assert_eq!(removed.hash, h2);
assert!(idx.lookup(&h2).is_none());
assert_eq!(idx.block_count(), 1);
}
#[test]
fn iter_by_model_filters_correctly() {
let idx = BlockIndex::new();
let fp_a = fixture_fp("model-A");
let fp_b = fixture_fp("model-B");
assert_ne!(fp_a, fp_b, "fixtures pick distinct fingerprints");
for s in 0u32..5 {
let (_, m) = synthetic_meta(fp_a, s, 100);
idx.insert(m);
}
for s in 5u32..8 {
let (_, m) = synthetic_meta(fp_b, s, 200);
idx.insert(m);
}
assert_eq!(idx.block_count(), 8);
let only_a = idx.iter_by_model(&fp_a);
let only_b = idx.iter_by_model(&fp_b);
assert_eq!(only_a.len(), 5);
assert_eq!(only_b.len(), 3);
assert!(only_a.iter().all(|m| m.model_fp == fp_a));
assert!(only_b.iter().all(|m| m.model_fp == fp_b));
let fp_c = fixture_fp("model-C");
assert!(idx.iter_by_model(&fp_c).is_empty());
}
#[test]
fn total_bytes_on_disk_sums_correctly() {
let idx = BlockIndex::new();
let fp = fixture_fp("sum");
let sizes = [128u64, 256, 512, 1024, 2048, 4096];
for (s, sz) in sizes.iter().enumerate() {
let (_, m) = synthetic_meta(fp, s as u32, *sz);
idx.insert(m);
}
let expected: u64 = sizes.iter().sum();
assert_eq!(idx.total_bytes_on_disk(), expected);
}
#[test]
fn block_count_increments_decrements_correctly() {
let idx = BlockIndex::new();
let fp = fixture_fp("count");
assert_eq!(idx.block_count(), 0);
let (h0, m0) = synthetic_meta(fp, 0, 1);
let (h1, m1) = synthetic_meta(fp, 1, 1);
let (h2, m2) = synthetic_meta(fp, 2, 1);
idx.insert(m0);
assert_eq!(idx.block_count(), 1);
idx.insert(m1);
idx.insert(m2);
assert_eq!(idx.block_count(), 3);
idx.remove(&h1);
assert_eq!(idx.block_count(), 2);
idx.remove(&h0);
idx.remove(&h2);
assert_eq!(idx.block_count(), 0);
idx.remove(&h0);
assert_eq!(idx.block_count(), 0);
}
#[test]
fn rebuild_from_disk_clean_state_o1_lookup() {
let dir = temp_dir("rebuild50");
let fp = fixture_fp("rebuild50");
let mut parent = ParentBlockHash(None);
let mut written: Vec<BlockHash> = Vec::new();
for s in 0u32..50 {
let (body, header) = make_block(fp, parent, s);
let path = block_path(&dir, &fp, &header.block_hash);
write_envelope(&path, &header, &body).expect("write_envelope");
written.push(header.block_hash);
parent = ParentBlockHash(Some(header.block_hash));
}
let idx = BlockIndex::rebuild_from_disk(&dir).expect("rebuild");
assert_eq!(idx.block_count(), 50);
for h in &written {
let m = idx.lookup(h).expect("lookup hit");
assert_eq!(m.hash, *h);
assert!(m.file_path.exists(), "indexed path actually exists");
assert!(m.bytes_on_disk > 0);
}
let q = dir
.join("models")
.join(fp.short_hex())
.join("kv-quarantine");
assert!(!q.exists(), "no quarantine on clean rebuild");
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn rebuild_from_disk_corrupted_files_quarantine() {
let dir = temp_dir("quarantine");
let fp = fixture_fp("quarantine");
let mut parent = ParentBlockHash(None);
let mut paths: Vec<PathBuf> = Vec::new();
let mut hashes: Vec<BlockHash> = Vec::new();
for s in 0u32..5 {
let (body, header) = make_block(fp, parent, s);
let path = block_path(&dir, &fp, &header.block_hash);
write_envelope(&path, &header, &body).expect("write_envelope");
paths.push(path);
hashes.push(header.block_hash);
parent = ParentBlockHash(Some(header.block_hash));
}
std::fs::write(&paths[1], b"abc").expect("truncate write");
let bad_header = EnvelopeHeader {
format_version: 999,
model_fingerprint: fp,
block_hash: hashes[3],
parent_block_hash: ParentBlockHash(Some(hashes[2])),
payload_kind: "kv-dense-bf16".into(),
codec_version: 1,
n_tokens: BLOCK_TOKENS,
};
let header_json = serde_json::to_vec(&bad_header).expect("serialize bad header");
let pad = (8 - (header_json.len() % 8)) % 8;
let mut header_bytes = header_json;
header_bytes.extend(std::iter::repeat(b' ').take(pad));
let mut blob = Vec::new();
blob.extend_from_slice(&(header_bytes.len() as u64).to_le_bytes());
blob.extend_from_slice(&header_bytes);
blob.extend_from_slice(&[0u8; 64]);
std::fs::write(&paths[3], &blob).expect("write bad-version envelope");
let idx = BlockIndex::rebuild_from_disk(&dir).expect("rebuild");
assert_eq!(idx.block_count(), 3, "3 indexed; 2 quarantined");
let q = dir
.join("models")
.join(fp.short_hex())
.join("kv-quarantine");
assert!(q.exists(), "quarantine dir created");
let q_files: Vec<_> = std::fs::read_dir(&q)
.expect("read q")
.filter_map(|e| e.ok())
.map(|e| e.file_name().to_string_lossy().into_owned())
.collect();
assert_eq!(q_files.len(), 2, "two quarantined files; saw {q_files:?}");
assert!(!paths[1].exists(), "corrupt file 1 moved");
assert!(!paths[3].exists(), "corrupt file 3 moved");
assert!(paths[0].exists());
assert!(paths[2].exists());
assert!(paths[4].exists());
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn rebuild_from_disk_ignores_tmp_files() {
let dir = temp_dir("tmpignore");
let fp = fixture_fp("tmpignore");
let mut parent = ParentBlockHash(None);
let mut hashes: Vec<BlockHash> = Vec::new();
for s in 0u32..3 {
let (body, header) = make_block(fp, parent, s);
let path = block_path(&dir, &fp, &header.block_hash);
write_envelope(&path, &header, &body).expect("write_envelope");
hashes.push(header.block_hash);
parent = ParentBlockHash(Some(header.block_hash));
}
let fanout_dir = dir
.join("models")
.join(fp.short_hex())
.join("kv")
.join(&hashes[0].to_string()[..1]);
let orphan = fanout_dir.join(format!("{}.safetensors.tmp.{}", hashes[0], process::id()));
std::fs::write(&orphan, b"this would be a partial atomic-rename leftover")
.expect("write orphan");
assert!(orphan.exists());
let idx = BlockIndex::rebuild_from_disk(&dir).expect("rebuild");
assert_eq!(idx.block_count(), 3);
let q = dir
.join("models")
.join(fp.short_hex())
.join("kv-quarantine");
if q.exists() {
let q_count = std::fs::read_dir(&q)
.expect("read q")
.filter_map(|e| e.ok())
.count();
assert_eq!(q_count, 0, "no quarantine on tmp-only directory state");
}
assert!(orphan.exists(), "orphan tmp file untouched");
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn rebuild_from_disk_empty_root_returns_empty_index() {
let dir = temp_dir("emptyroot");
let idx = BlockIndex::rebuild_from_disk(&dir).expect("rebuild");
assert_eq!(idx.block_count(), 0);
std::fs::create_dir_all(dir.join("models")).expect("mkdir models");
let idx2 = BlockIndex::rebuild_from_disk(&dir).expect("rebuild2");
assert_eq!(idx2.block_count(), 0);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn block_index_clone_shares_storage() {
let a = BlockIndex::new();
let b = a.clone();
let fp = fixture_fp("clone");
let (h, m) = synthetic_meta(fp, 0, 100);
a.insert(m);
assert_eq!(b.block_count(), 1);
assert!(b.lookup(&h).is_some());
b.remove(&h);
assert_eq!(a.block_count(), 0);
}
#[test]
fn rebuild_from_disk_with_hash_chain_uses_real_compute_block_hash() {
let dir = temp_dir("realchain");
let fp = fixture_fp("realchain");
let tokens_per_block = BLOCK_TOKENS as usize;
let n_blocks = 4;
let mut all_tokens = Vec::with_capacity(tokens_per_block * n_blocks);
for i in 0..(tokens_per_block * n_blocks) {
all_tokens.push(i as u32);
}
let mut parent = ParentBlockHash(None);
let mut chain: Vec<BlockHash> = Vec::with_capacity(n_blocks);
for blk in 0..n_blocks {
let lo = blk * tokens_per_block;
let hi = lo + tokens_per_block;
let bh = compute_block_hash(&fp, &parent, &all_tokens[lo..hi]);
chain.push(bh);
parent = ParentBlockHash(Some(bh));
}
let mut parent_for_writes = ParentBlockHash(None);
for blk in 0..n_blocks {
let body: Vec<u8> = (0..1024u32)
.flat_map(|i| (i.wrapping_add(blk as u32)).to_le_bytes())
.collect();
let mut h = Sha256::new();
h.update(&body);
let body_bh = BlockHash(h.finalize().into());
let header = EnvelopeHeader {
format_version: CURRENT_FORMAT_VERSION.0,
model_fingerprint: fp,
block_hash: body_bh,
parent_block_hash: parent_for_writes,
payload_kind: "kv-dense-bf16".into(),
codec_version: 1,
n_tokens: BLOCK_TOKENS,
};
let path = block_path(&dir, &fp, &header.block_hash);
write_envelope(&path, &header, &body).expect("write_envelope");
parent_for_writes = ParentBlockHash(Some(body_bh));
}
let idx = BlockIndex::rebuild_from_disk(&dir).expect("rebuild");
assert_eq!(idx.block_count(), n_blocks);
for i in 1..chain.len() {
assert_ne!(chain[i - 1], chain[i]);
}
let _ = std::fs::remove_dir_all(&dir);
}
}