store: index ops take WriteTransaction, mutations batch properly

Index functions now take &WriteTransaction instead of &Database,
allowing callers to batch multiple index operations in a single
transaction. Store mutations (upsert, delete, rename, etc.) now
begin_write/commit their own transactions, ensuring atomicity.

- replay_relations uses single txn for all relation indexing
- Store::db() exposes Database for callers needing txn control
- Convenience wrappers open their own txn for simple cases

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-13 21:44:20 -04:00
commit 4696bb8b7d
5 changed files with 151 additions and 144 deletions

View file

@ -33,6 +33,7 @@ pub use ops::current_provenance;
use crate::graph::{self, Graph};
use anyhow::{bail, Result};
use redb::Database;
/// Strip .md suffix from a key, handling both bare keys and section keys.
/// "identity.md" → "identity", "foo.md#section" → "foo#section", "identity" → "identity"
@ -120,34 +121,44 @@ impl Store {
Ok(neighbors)
}
/// Get the database for transaction management.
pub fn db(&self) -> Result<&Database> {
self.db.as_ref().ok_or_else(|| anyhow::anyhow!("store not loaded"))
}
/// Remove a node from the index (used after appending a tombstone).
pub fn remove_from_index(&self, key: &str, uuid: &[u8; 16]) -> Result<()> {
if let Some(db) = self.db.as_ref() {
index::remove_node(db, key, uuid)?;
}
/// For batched operations, use index::remove_node with a WriteTransaction directly.
pub fn remove_from_index(&self, key: &str) -> Result<()> {
let db = self.db()?;
let txn = db.begin_write()?;
index::remove_node(&txn, key)?;
txn.commit()?;
Ok(())
}
/// Get all edges for a node by UUID. Returns (other_uuid, strength, rel_type, is_outgoing).
pub fn edges_for_uuid(&self, uuid: &[u8; 16]) -> Result<Vec<([u8; 16], f32, u8, bool)>> {
let db = self.db.as_ref()
.ok_or_else(|| anyhow::anyhow!("store not loaded"))?;
let db = self.db()?;
index::edges_for_node(db, uuid)
}
/// Add a relation to the index.
/// Add a relation to the index (opens its own transaction).
/// For batched operations, use index::index_relation with a WriteTransaction directly.
pub fn index_relation(&self, source: &[u8; 16], target: &[u8; 16], strength: f32, rel_type: u8) -> Result<()> {
if let Some(db) = self.db.as_ref() {
index::index_relation(db, source, target, strength, rel_type)?;
}
let db = self.db()?;
let txn = db.begin_write()?;
index::index_relation(&txn, source, target, strength, rel_type)?;
txn.commit()?;
Ok(())
}
/// Remove a relation from the index.
/// Remove a relation from the index (opens its own transaction).
/// For batched operations, use index::remove_relation with a WriteTransaction directly.
pub fn remove_relation_from_index(&self, source: &[u8; 16], target: &[u8; 16], strength: f32, rel_type: u8) -> Result<()> {
if let Some(db) = self.db.as_ref() {
index::remove_relation(db, source, target, strength, rel_type)?;
}
let db = self.db()?;
let txn = db.begin_write()?;
index::remove_relation(&txn, source, target, strength, rel_type)?;
txn.commit()?;
Ok(())
}