Persist backend per database id in admin metadata so restarts and lazy opens always use the correct engine (Sled/Redb)
This commit is contained in:
@@ -69,9 +69,38 @@ pub fn open_admin_storage(
|
||||
if let Some(st) = w.get(base_dir) {
|
||||
return Ok(st.clone());
|
||||
}
|
||||
let st = init_admin_storage(base_dir, backend, admin_secret)?;
|
||||
|
||||
// Detect existing 0.db backend by filesystem, if present.
|
||||
let admin_path = PathBuf::from(base_dir).join("0.db");
|
||||
let detected = if admin_path.exists() {
|
||||
if admin_path.is_file() {
|
||||
Some(options::BackendType::Redb)
|
||||
} else if admin_path.is_dir() {
|
||||
Some(options::BackendType::Sled)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let effective_backend = match detected {
|
||||
Some(d) if d != backend => {
|
||||
eprintln!(
|
||||
"warning: Admin DB 0 at {} appears to be {:?}, but process default is {:?}. Using detected backend.",
|
||||
admin_path.display(),
|
||||
d,
|
||||
backend
|
||||
);
|
||||
d
|
||||
}
|
||||
Some(d) => d,
|
||||
None => backend, // First boot: use requested backend to initialize 0.db
|
||||
};
|
||||
|
||||
let st = init_admin_storage(base_dir, effective_backend, admin_secret)?;
|
||||
w.insert(base_dir.to_string(), st.clone());
|
||||
return Ok(st);
|
||||
Ok(st)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +151,39 @@ pub fn open_data_storage(
|
||||
return Ok(st.clone());
|
||||
}
|
||||
|
||||
// Determine per-db encryption
|
||||
// Resolve effective backend for this db id:
|
||||
// 1) Try admin meta "backend" field
|
||||
// 2) If missing, sniff filesystem (file => Redb, dir => Sled), then persist into admin meta
|
||||
// 3) Fallback to requested 'backend' (startup default) if nothing else is known
|
||||
let meta_backend = get_database_backend(base_dir, backend.clone(), admin_secret, id).ok().flatten();
|
||||
let db_path = PathBuf::from(base_dir).join(format!("{}.db", id));
|
||||
let sniffed_backend = if db_path.exists() {
|
||||
if db_path.is_file() {
|
||||
Some(options::BackendType::Redb)
|
||||
} else if db_path.is_dir() {
|
||||
Some(options::BackendType::Sled)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let effective_backend = meta_backend.clone().or(sniffed_backend).unwrap_or(backend.clone());
|
||||
|
||||
// If we had to sniff (i.e., meta missing), persist it for future robustness
|
||||
if meta_backend.is_none() {
|
||||
let _ = set_database_backend(base_dir, backend.clone(), admin_secret, id, effective_backend.clone());
|
||||
}
|
||||
|
||||
// Warn if caller-provided backend differs from effective
|
||||
if effective_backend != backend {
|
||||
eprintln!(
|
||||
"notice: Database {} backend resolved to {:?} (caller requested {:?}). Using resolved backend.",
|
||||
id, effective_backend, backend
|
||||
);
|
||||
}
|
||||
|
||||
// Determine per-db encryption (from admin meta)
|
||||
let enc = get_enc_key(base_dir, backend.clone(), admin_secret, id)?;
|
||||
let should_encrypt = enc.is_some();
|
||||
|
||||
@@ -134,8 +195,8 @@ pub fn open_data_storage(
|
||||
})?;
|
||||
}
|
||||
|
||||
// Open storage
|
||||
let storage: Arc<dyn StorageBackend> = match backend {
|
||||
// Open storage using the effective backend
|
||||
let storage: Arc<dyn StorageBackend> = match effective_backend {
|
||||
options::BackendType::Redb => Arc::new(Storage::new(&db_file, should_encrypt, enc.as_deref())?),
|
||||
options::BackendType::Sled => Arc::new(SledStorage::new(&db_file, should_encrypt, enc.as_deref())?),
|
||||
};
|
||||
@@ -217,6 +278,39 @@ pub fn set_database_public(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Persist per-db backend type in admin metadata (module-scope)
|
||||
pub fn set_database_backend(
|
||||
base_dir: &str,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
id: u64,
|
||||
db_backend: options::BackendType,
|
||||
) -> Result<(), DBError> {
|
||||
let admin = open_admin_storage(base_dir, backend, admin_secret)?;
|
||||
let mk = k_meta_db(id);
|
||||
let val = match db_backend {
|
||||
options::BackendType::Redb => "Redb",
|
||||
options::BackendType::Sled => "Sled",
|
||||
};
|
||||
let _ = admin.hset(&mk, vec![("backend".to_string(), val.to_string())])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_database_backend(
|
||||
base_dir: &str,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
id: u64,
|
||||
) -> Result<Option<options::BackendType>, DBError> {
|
||||
let admin = open_admin_storage(base_dir, backend, admin_secret)?;
|
||||
let mk = k_meta_db(id);
|
||||
match admin.hget(&mk, "backend")? {
|
||||
Some(s) if s == "Redb" => Ok(Some(options::BackendType::Redb)),
|
||||
Some(s) if s == "Sled" => Ok(Some(options::BackendType::Sled)),
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
// Set database name
|
||||
pub fn set_database_name(
|
||||
base_dir: &str,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum BackendType {
|
||||
Redb,
|
||||
Sled,
|
||||
|
47
src/rpc.rs
47
src/rpc.rs
@@ -158,14 +158,42 @@ impl RpcServerImpl {
|
||||
));
|
||||
}
|
||||
|
||||
// Create server instance with default options
|
||||
// Resolve effective backend for this db from admin meta or filesystem; fallback to default
|
||||
let meta_backend = admin_meta::get_database_backend(&self.base_dir, self.backend.clone(), &self.admin_secret, db_id)
|
||||
.ok()
|
||||
.flatten();
|
||||
let db_path = std::path::PathBuf::from(&self.base_dir).join(format!("{}.db", db_id));
|
||||
let sniffed_backend = if db_path.exists() {
|
||||
if db_path.is_file() {
|
||||
Some(crate::options::BackendType::Redb)
|
||||
} else if db_path.is_dir() {
|
||||
Some(crate::options::BackendType::Sled)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let effective_backend = meta_backend.clone().or(sniffed_backend).unwrap_or(self.backend.clone());
|
||||
if effective_backend != self.backend {
|
||||
eprintln!(
|
||||
"notice: get_or_create_server: db {} backend resolved to {:?} (server default {:?})",
|
||||
db_id, effective_backend, self.backend
|
||||
);
|
||||
}
|
||||
// If we had to sniff (no meta), persist the resolved backend
|
||||
if meta_backend.is_none() {
|
||||
let _ = admin_meta::set_database_backend(&self.base_dir, self.backend.clone(), &self.admin_secret, db_id, effective_backend.clone());
|
||||
}
|
||||
|
||||
// Create server instance with resolved backend
|
||||
let db_option = DBOption {
|
||||
dir: self.base_dir.clone(),
|
||||
port: 0, // Not used for RPC-managed databases
|
||||
debug: false,
|
||||
encryption_key: None,
|
||||
encrypt: false,
|
||||
backend: self.backend.clone(),
|
||||
backend: effective_backend,
|
||||
admin_secret: self.admin_secret.clone(),
|
||||
};
|
||||
|
||||
@@ -308,17 +336,22 @@ impl RpcServer for RpcServerImpl {
|
||||
return Err(jsonrpsee::types::ErrorObjectOwned::owned(-32000, format!("Failed to ensure base dir: {}", e), None::<()>));
|
||||
}
|
||||
|
||||
// Create server instance using base_dir and admin secret
|
||||
// Map RPC backend to options backend and persist it in admin meta for this db id
|
||||
let opt_backend = match backend {
|
||||
BackendType::Redb => crate::options::BackendType::Redb,
|
||||
BackendType::Sled => crate::options::BackendType::Sled,
|
||||
};
|
||||
admin_meta::set_database_backend(&self.base_dir, self.backend.clone(), &self.admin_secret, db_id, opt_backend.clone())
|
||||
.map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned(-32000, e.0, None::<()>))?;
|
||||
|
||||
// Create server instance using base_dir, chosen backend and admin secret
|
||||
let option = DBOption {
|
||||
dir: self.base_dir.clone(),
|
||||
port: 0, // Not used for RPC-managed databases
|
||||
debug: false,
|
||||
encryption_key: None, // per-db key is stored in admin DB 0
|
||||
encrypt: false, // encryption decided per-db at open time
|
||||
backend: match backend {
|
||||
BackendType::Redb => crate::options::BackendType::Redb,
|
||||
BackendType::Sled => crate::options::BackendType::Sled,
|
||||
},
|
||||
backend: opt_backend,
|
||||
admin_secret: self.admin_secret.clone(),
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user