forked from kent/consciousness
store: add weight to index, index-only key matching
- KEY_TO_UUID now stores weight (30 bytes: uuid+type+ts+deleted+weight) - UUID_OFFSETS changed to composite key for O(log n) max-offset lookup - Add NODES_BY_TYPE index for efficient type+date range queries - Add for_each_key_weight() to StoreView for index-only iteration - match_seeds uses index-only path when content not needed - Fix transaction consistency in ops (single txn for related updates) - rebuild() now records all uuid→offset mappings for version history - Backwards compatible: old index formats decoded with default weight Co-Authored-By: Proof of Concept <poc@bcachefs.org> Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
fc978e2f2e
commit
ba4e01b6f3
9 changed files with 774 additions and 500 deletions
|
|
@ -4,6 +4,10 @@ use super::store::Store;
|
|||
use crate::graph::Graph;
|
||||
use crate::neuro::{consolidation_priority, ReplayItem};
|
||||
|
||||
// All functions take `provenance: &str` for interface uniformity (MCP tools
|
||||
// pass it to everything), but read-only operations ignore it (_provenance).
|
||||
// Only write operations actually record the provenance string.
|
||||
|
||||
// ── Memory operations ──────────────────────────────────────────
|
||||
|
||||
pub fn memory_render(store: &Store, _provenance: &str, key: &str, raw: Option<bool>) -> Result<String> {
|
||||
|
|
@ -125,30 +129,7 @@ pub fn memory_history(store: &Store, _provenance: &str, key: &str, full: Option<
|
|||
let key = store.resolve_key(key).unwrap_or_else(|_| key.to_string());
|
||||
let full = full.unwrap_or(false);
|
||||
|
||||
let path = crate::store::nodes_path();
|
||||
if !path.exists() {
|
||||
anyhow::bail!("No node log found");
|
||||
}
|
||||
|
||||
use std::io::BufReader;
|
||||
let file = std::fs::File::open(&path)
|
||||
.map_err(|e| anyhow::anyhow!("open {}: {}", path.display(), e))?;
|
||||
let mut reader = BufReader::new(file);
|
||||
|
||||
let mut versions: Vec<crate::store::Node> = Vec::new();
|
||||
while let Ok(msg) = capnp::serialize::read_message(&mut reader, capnp::message::ReaderOptions::new()) {
|
||||
let log = msg.get_root::<crate::memory_capnp::node_log::Reader>()
|
||||
.map_err(|e| anyhow::anyhow!("read log: {}", e))?;
|
||||
for node_reader in log.get_nodes()
|
||||
.map_err(|e| anyhow::anyhow!("get nodes: {}", e))? {
|
||||
let node = crate::store::Node::from_capnp_migrate(node_reader)
|
||||
.map_err(|e| anyhow::anyhow!("{}", e))?;
|
||||
if node.key == key {
|
||||
versions.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let versions = store.get_history(&key)?;
|
||||
if versions.is_empty() {
|
||||
anyhow::bail!("No history found for '{}'", key);
|
||||
}
|
||||
|
|
@ -305,19 +286,23 @@ pub fn journal_tail(store: &Store, _provenance: &str, count: Option<u64>, level:
|
|||
.map(|dt| dt.and_utc().timestamp())
|
||||
});
|
||||
|
||||
let all_keys = store.all_keys()?;
|
||||
let mut entries: Vec<_> = all_keys.iter()
|
||||
.filter_map(|key| store.get_node(key).ok()?)
|
||||
.filter(|n| n.node_type == node_type)
|
||||
.filter(|n| after_ts.map(|ts| n.created_at >= ts).unwrap_or(true))
|
||||
.map(|n| JournalEntry {
|
||||
key: n.key.clone(),
|
||||
content: n.content,
|
||||
created_at: n.created_at,
|
||||
})
|
||||
.collect();
|
||||
entries.sort_by_key(|e| std::cmp::Reverse(e.created_at));
|
||||
entries.truncate(count);
|
||||
// Use NODES_BY_TYPE index: O(log n + k) instead of O(n)
|
||||
let db = store.db()?;
|
||||
let uuids = crate::store::nodes_by_type(db, node_type as u8, count, after_ts)?;
|
||||
|
||||
let mut entries = Vec::with_capacity(uuids.len());
|
||||
for uuid in uuids {
|
||||
if let Ok(Some(node)) = store.get_node_by_uuid(&uuid) {
|
||||
if !node.deleted {
|
||||
entries.push(JournalEntry {
|
||||
key: node.key.clone(),
|
||||
content: node.content.clone(),
|
||||
created_at: node.created_at,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Already sorted by timestamp from index, no need to sort again
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
|
|
@ -366,13 +351,17 @@ pub fn journal_new(store: &Store, provenance: &str, name: &str, title: &str, bod
|
|||
pub fn journal_update(store: &Store, provenance: &str, body: &str, level: Option<i64>) -> Result<String> {
|
||||
let level = level.unwrap_or(0);
|
||||
let node_type = level_to_node_type(level);
|
||||
let all_keys = store.all_keys()?;
|
||||
let latest_key = all_keys.iter()
|
||||
.filter_map(|key| store.get_node(key).ok()?)
|
||||
.filter(|n| n.node_type == node_type)
|
||||
.max_by_key(|n| n.created_at)
|
||||
.map(|n| n.key.clone());
|
||||
let Some(key) = latest_key else {
|
||||
|
||||
// Use NODES_BY_TYPE index to find most recent
|
||||
let db = store.db()?;
|
||||
let uuids = crate::store::nodes_by_type(db, node_type as u8, 1, None)?;
|
||||
let key = match uuids.first() {
|
||||
Some(uuid) => store.get_node_by_uuid(uuid)?
|
||||
.filter(|n| !n.deleted)
|
||||
.map(|n| n.key),
|
||||
None => None,
|
||||
};
|
||||
let Some(key) = key else {
|
||||
anyhow::bail!("no entry at level {} to update — use journal_new first", level);
|
||||
};
|
||||
let existing = store.get_node(&key)?.ok_or_else(|| anyhow::anyhow!("node not found"))?.content;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue