capnp_store: remove dead has_node trait method, fix fix_categories bulk write

- has_node: defined on StoreView trait but never called externally
- fix_categories: was appending ALL nodes when only changed ones needed
  persisting; now collects changed nodes and appends only those
- save_snapshot: pass log sizes from caller instead of re-statting files
- params: use Copy instead of .clone() in snapshot construction
This commit is contained in:
ProofOfConcept 2026-03-03 12:42:16 -05:00
parent 70a5f05ce0
commit a2ec8657d2

View file

@ -440,9 +440,6 @@ pub trait StoreView {
/// Node content by key.
fn node_content(&self, key: &str) -> Option<&str>;
/// Check if a node exists.
fn has_node(&self, key: &str) -> bool;
/// Search/graph parameters.
fn params(&self) -> Params;
}
@ -469,10 +466,6 @@ impl StoreView for Store {
self.nodes.get(key).map(|n| n.content.as_str())
}
fn has_node(&self, key: &str) -> bool {
self.nodes.contains_key(key)
}
fn params(&self) -> Params {
self.params
}
@ -554,10 +547,6 @@ impl StoreView for MmapView {
snap.nodes.get(key).map(|n| &*n.content)
}
fn has_node(&self, key: &str) -> bool {
self.snapshot().nodes.get(key).is_some()
}
fn params(&self) -> Params {
let p = &self.snapshot().params;
Params {
@ -608,9 +597,6 @@ impl StoreView for AnyView {
fn node_content(&self, key: &str) -> Option<&str> {
match self { AnyView::Mmap(v) => v.node_content(key), AnyView::Owned(s) => s.node_content(key) }
}
fn has_node(&self, key: &str) -> bool {
match self { AnyView::Mmap(v) => v.has_node(key), AnyView::Owned(s) => s.has_node(key) }
}
fn params(&self) -> Params {
match self { AnyView::Mmap(v) => v.params(), AnyView::Owned(s) => s.params() }
}
@ -652,7 +638,7 @@ impl Store {
}
// Bootstrap: write rkyv snapshot if missing
if !snapshot_path().exists() {
if let Err(e) = store.save_snapshot_inner() {
if let Err(e) = store.save_snapshot(cached_nodes, cached_rels) {
eprintln!("rkyv bootstrap: {}", e);
}
}
@ -819,7 +805,7 @@ impl Store {
.map_err(|e| format!("rename {}{}: {}", tmp_path.display(), path.display(), e))?;
// Also write rkyv snapshot (mmap-friendly)
if let Err(e) = self.save_snapshot_inner() {
if let Err(e) = self.save_snapshot(nodes_size, rels_size) {
eprintln!("rkyv snapshot save: {}", e);
}
@ -828,20 +814,17 @@ impl Store {
/// Serialize store as rkyv snapshot with staleness header.
/// Assumes StoreLock is already held by caller.
fn save_snapshot_inner(&self) -> Result<(), String> {
fn save_snapshot(&self, nodes_size: u64, rels_size: u64) -> Result<(), String> {
let snap = Snapshot {
nodes: self.nodes.clone(),
relations: self.relations.iter().filter(|r| !r.deleted).cloned().collect(),
gaps: self.gaps.clone(),
params: self.params.clone(),
params: self.params,
};
let rkyv_data = rkyv::to_bytes::<_, 256>(&snap)
.map_err(|e| format!("rkyv serialize: {}", e))?;
let nodes_size = fs::metadata(nodes_path()).map(|m| m.len()).unwrap_or(0);
let rels_size = fs::metadata(relations_path()).map(|m| m.len()).unwrap_or(0);
let mut data = Vec::with_capacity(RKYV_HEADER_LEN + rkyv_data.len());
data.extend_from_slice(&RKYV_MAGIC);
data.extend_from_slice(&1u32.to_le_bytes()); // format version
@ -1288,7 +1271,7 @@ impl Store {
];
let obs_prefixes = ["skill-", "worked-example-"];
let mut changed = 0;
let mut changed_nodes = Vec::new();
let mut unchanged = 0;
let keys: Vec<String> = self.nodes.keys().cloned().collect();
@ -1321,18 +1304,17 @@ impl Store {
let node = self.nodes.get_mut(key).unwrap();
node.category = cat;
node.version += 1;
changed += 1;
changed_nodes.push(node.clone());
} else {
unchanged += 1;
}
}
if changed > 0 {
let updated: Vec<Node> = self.nodes.values().cloned().collect();
self.append_nodes(&updated)?;
if !changed_nodes.is_empty() {
self.append_nodes(&changed_nodes)?;
}
Ok((changed, unchanged))
Ok((changed_nodes.len(), unchanged))
}
/// Cap node degree by soft-deleting edges from mega-hubs.