forked from kent/consciousness
Integrate redb into Store::load() with health check
- Add db: Option<Database> field to Store - Store::load() opens redb after replaying capnp logs - Health check compares node count + spot checks keys - Rebuilds automatically if db is missing, corrupt, or stale - Make table definitions public for cross-module access Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
2caccf875d
commit
6104c63890
3 changed files with 78 additions and 7 deletions
|
|
@ -2,7 +2,8 @@
|
|||
//
|
||||
// capnp logs are the source of truth; redb provides indexed access.
|
||||
|
||||
use super::types::*;
|
||||
use super::{db, types::*};
|
||||
use redb::ReadableTableMetadata;
|
||||
|
||||
use crate::memory_capnp;
|
||||
|
||||
|
|
@ -16,7 +17,7 @@ use std::io::{BufReader, Seek};
|
|||
use std::path::Path;
|
||||
|
||||
impl Store {
|
||||
/// Load store by replaying capnp logs.
|
||||
/// Load store by replaying capnp logs, then open/verify redb indices.
|
||||
pub fn load() -> Result<Store> {
|
||||
let nodes_p = nodes_path();
|
||||
let rels_p = relations_path();
|
||||
|
|
@ -48,9 +49,60 @@ impl Store {
|
|||
store.nodes.contains_key(&r.target_key)
|
||||
);
|
||||
|
||||
// Open redb and verify/rebuild indices
|
||||
let db_p = db_path();
|
||||
store.db = Some(store.open_or_rebuild_db(&db_p)?);
|
||||
|
||||
Ok(store)
|
||||
}
|
||||
|
||||
/// Open redb database, rebuilding if unhealthy.
|
||||
fn open_or_rebuild_db(&self, path: &Path) -> Result<redb::Database> {
|
||||
// Try opening existing database
|
||||
if path.exists() {
|
||||
match db::open_db(path) {
|
||||
Ok(database) => {
|
||||
if self.db_is_healthy(&database)? {
|
||||
return Ok(database);
|
||||
}
|
||||
eprintln!("redb index stale, rebuilding...");
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("redb open failed ({}), rebuilding...", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild from in-memory state
|
||||
db::rebuild_from_store(path, self)
|
||||
}
|
||||
|
||||
/// Check if redb indices match in-memory state.
|
||||
fn db_is_healthy(&self, database: &redb::Database) -> Result<bool> {
|
||||
use redb::ReadableDatabase;
|
||||
|
||||
let txn = database.begin_read()?;
|
||||
|
||||
// Quick check: node count should match
|
||||
let nodes_table = txn.open_table(db::NODES)?;
|
||||
let db_count = nodes_table.len()?;
|
||||
|
||||
if db_count != self.nodes.len() as u64 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// Spot check: verify a few random nodes exist with matching keys
|
||||
// (full verification would be too slow)
|
||||
for (i, key) in self.nodes.keys().enumerate() {
|
||||
if i >= 10 { break; } // check first 10
|
||||
if nodes_table.get(key.as_str())?.is_none() {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// Replay node log, keeping latest version per UUID.
|
||||
/// Tracks all UUIDs seen per key to detect duplicates.
|
||||
fn replay_nodes(&mut self, path: &Path) -> Result<()> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue