feat: report store durability status
This commit is contained in:
parent
2921e5e976
commit
6953e3acde
7 changed files with 124 additions and 18 deletions
|
|
@ -2210,6 +2210,14 @@ fn print_response(response: ControlResponse, json: bool) -> Result<()> {
|
|||
println!("socket: {}", status.socket.display());
|
||||
println!("agent: {}", status.agent_id);
|
||||
println!("node: {}", status.node_id);
|
||||
println!(
|
||||
"store schema: {}/{}",
|
||||
status.store_schema_version, status.store_current_schema_version
|
||||
);
|
||||
println!("store journal: {}", status.store_journal_mode);
|
||||
println!("store synchronous: {}", status.store_synchronous);
|
||||
println!("store status: {}", status.store_status);
|
||||
println!("store note: {}", status.store_note);
|
||||
println!(
|
||||
"endpoint: {}",
|
||||
status.endpoint_id.as_deref().unwrap_or("not started")
|
||||
|
|
|
|||
|
|
@ -1156,6 +1156,12 @@ pub struct StatusResponse {
|
|||
pub socket: PathBuf,
|
||||
pub agent_id: String,
|
||||
pub node_id: String,
|
||||
pub store_schema_version: i64,
|
||||
pub store_current_schema_version: i64,
|
||||
pub store_journal_mode: String,
|
||||
pub store_synchronous: String,
|
||||
pub store_status: String,
|
||||
pub store_note: String,
|
||||
pub iroh_enabled: bool,
|
||||
pub endpoint_id: Option<String>,
|
||||
pub iroh_relay_mode: String,
|
||||
|
|
|
|||
|
|
@ -5868,18 +5868,37 @@ pub fn handle_request(
|
|||
) -> Result<ControlResponse, NodeError> {
|
||||
let store = Store::open(&node.paths.metadata_db())?;
|
||||
match request {
|
||||
ControlRequest::Status => Ok(ControlResponse::Status(StatusResponse {
|
||||
home: node.paths.home().to_path_buf(),
|
||||
socket: node.paths.socket_path(),
|
||||
agent_id: node.agent_id.clone(),
|
||||
node_id: node.node_id.clone(),
|
||||
iroh_enabled: node.iroh_status.enabled,
|
||||
endpoint_id: node.iroh_status.endpoint_id.clone(),
|
||||
iroh_relay_mode: node.iroh_status.relay_mode.clone(),
|
||||
iroh_local_discovery: node.iroh_status.local_discovery,
|
||||
iroh: node.iroh_status.note.clone(),
|
||||
native_backends: native_backend_statuses(),
|
||||
})),
|
||||
ControlRequest::Status => {
|
||||
let store_schema_version = store.schema_version()?;
|
||||
let store_journal_mode = store.journal_mode()?;
|
||||
let store_synchronous = store.synchronous_mode()?;
|
||||
let store_ok = store_schema_version == geth_store::CURRENT_SCHEMA_VERSION
|
||||
&& store_journal_mode == geth_store::FILE_JOURNAL_MODE
|
||||
&& store_synchronous == geth_store::SYNCHRONOUS_MODE;
|
||||
Ok(ControlResponse::Status(StatusResponse {
|
||||
home: node.paths.home().to_path_buf(),
|
||||
socket: node.paths.socket_path(),
|
||||
agent_id: node.agent_id.clone(),
|
||||
node_id: node.node_id.clone(),
|
||||
store_schema_version,
|
||||
store_current_schema_version: geth_store::CURRENT_SCHEMA_VERSION,
|
||||
store_journal_mode,
|
||||
store_synchronous,
|
||||
store_status: if store_ok { "ok" } else { "check" }.to_owned(),
|
||||
store_note: format!(
|
||||
"expected schema {}, journal {}, synchronous {}",
|
||||
geth_store::CURRENT_SCHEMA_VERSION,
|
||||
geth_store::FILE_JOURNAL_MODE,
|
||||
geth_store::SYNCHRONOUS_MODE
|
||||
),
|
||||
iroh_enabled: node.iroh_status.enabled,
|
||||
endpoint_id: node.iroh_status.endpoint_id.clone(),
|
||||
iroh_relay_mode: node.iroh_status.relay_mode.clone(),
|
||||
iroh_local_discovery: node.iroh_status.local_discovery,
|
||||
iroh: node.iroh_status.note.clone(),
|
||||
native_backends: native_backend_statuses(),
|
||||
}))
|
||||
}
|
||||
ControlRequest::NodeId => Ok(ControlResponse::NodeId(NodeIdResponse {
|
||||
agent_id: node.agent_id.clone(),
|
||||
node_id: node.node_id.clone(),
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ use rusqlite::{Connection, OptionalExtension, Transaction, params};
|
|||
use std::path::Path;
|
||||
|
||||
pub const CURRENT_SCHEMA_VERSION: i64 = 2;
|
||||
pub const FILE_JOURNAL_MODE: &str = "wal";
|
||||
pub const SYNCHRONOUS_MODE: &str = "normal";
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum StoreError {
|
||||
|
|
@ -17,9 +19,31 @@ pub struct Store {
|
|||
conn: Connection,
|
||||
}
|
||||
|
||||
fn configure_file_connection(conn: &Connection) -> Result<(), StoreError> {
|
||||
conn.execute_batch(
|
||||
r#"
|
||||
PRAGMA journal_mode = WAL;
|
||||
PRAGMA synchronous = NORMAL;
|
||||
PRAGMA foreign_keys = ON;
|
||||
"#,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn configure_memory_connection(conn: &Connection) -> Result<(), StoreError> {
|
||||
conn.execute_batch(
|
||||
r#"
|
||||
PRAGMA synchronous = NORMAL;
|
||||
PRAGMA foreign_keys = ON;
|
||||
"#,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Store {
|
||||
pub fn open(path: &Path) -> Result<Self, StoreError> {
|
||||
let conn = Connection::open(path)?;
|
||||
configure_file_connection(&conn)?;
|
||||
let store = Self { conn };
|
||||
store.migrate()?;
|
||||
Ok(store)
|
||||
|
|
@ -27,6 +51,7 @@ impl Store {
|
|||
|
||||
pub fn open_memory() -> Result<Self, StoreError> {
|
||||
let conn = Connection::open_in_memory()?;
|
||||
configure_memory_connection(&conn)?;
|
||||
let store = Self { conn };
|
||||
store.migrate()?;
|
||||
Ok(store)
|
||||
|
|
@ -58,7 +83,7 @@ impl Store {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn schema_version(&self) -> Result<i64, StoreError> {
|
||||
pub fn schema_version(&self) -> Result<i64, StoreError> {
|
||||
let version = self
|
||||
.conn
|
||||
.query_row(
|
||||
|
|
@ -75,6 +100,26 @@ impl Store {
|
|||
.map_err(|_| StoreError::InvalidSchemaVersion(version))
|
||||
}
|
||||
|
||||
pub fn journal_mode(&self) -> Result<String, StoreError> {
|
||||
self.conn
|
||||
.query_row("PRAGMA journal_mode", [], |row| row.get::<_, String>(0))
|
||||
.map(|mode| mode.to_ascii_lowercase())
|
||||
.map_err(StoreError::from)
|
||||
}
|
||||
|
||||
pub fn synchronous_mode(&self) -> Result<String, StoreError> {
|
||||
let mode = self
|
||||
.conn
|
||||
.query_row("PRAGMA synchronous", [], |row| row.get::<_, i64>(0))?;
|
||||
Ok(match mode {
|
||||
0 => "off".to_owned(),
|
||||
1 => "normal".to_owned(),
|
||||
2 => "full".to_owned(),
|
||||
3 => "extra".to_owned(),
|
||||
other => format!("unknown-{other}"),
|
||||
})
|
||||
}
|
||||
|
||||
fn apply_schema_v1(&self) -> Result<(), StoreError> {
|
||||
let tx = self.conn.unchecked_transaction()?;
|
||||
tx.execute_batch(
|
||||
|
|
@ -1817,6 +1862,25 @@ mod tests {
|
|||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_store_uses_documented_durability_pragmas() {
|
||||
let path = std::env::temp_dir().join(format!("geth-store-durability-{}.sqlite", now_ms()));
|
||||
let store = Store::open(&path).expect("open file store");
|
||||
assert_eq!(
|
||||
store.journal_mode().expect("journal mode"),
|
||||
FILE_JOURNAL_MODE
|
||||
);
|
||||
assert_eq!(
|
||||
store.synchronous_mode().expect("synchronous mode"),
|
||||
SYNCHRONOUS_MODE
|
||||
);
|
||||
|
||||
drop(store);
|
||||
let _ = std::fs::remove_file(&path);
|
||||
let _ = std::fs::remove_file(path.with_extension("sqlite-wal"));
|
||||
let _ = std::fs::remove_file(path.with_extension("sqlite-shm"));
|
||||
}
|
||||
|
||||
fn column_exists(store: &Store, table: &str, column: &str) -> bool {
|
||||
let mut stmt = store
|
||||
.conn
|
||||
|
|
|
|||
|
|
@ -268,6 +268,10 @@ fn geth_status_against_running_daemon() {
|
|||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
assert!(stdout.contains("geth daemon: running"));
|
||||
assert!(stdout.contains("agent:"));
|
||||
assert!(stdout.contains("store schema:"));
|
||||
assert!(stdout.contains("store journal: wal"));
|
||||
assert!(stdout.contains("store synchronous: normal"));
|
||||
assert!(stdout.contains("store status: ok"));
|
||||
assert!(stdout.contains("endpoint:"));
|
||||
assert!(stdout.contains("iroh relay: disabled"));
|
||||
assert!(stdout.contains("iroh discovery: local-network disabled"));
|
||||
|
|
|
|||
Loading…
Reference in a new issue