[Fix] Stability

This commit is contained in:
Alex Emmet 2026-08-28 13:22:59 +02:00
commit 8160f8d0cb
No known key found for this signature in database
44 changed files with 796 additions and 1296 deletions

View file

@ -149,10 +149,8 @@ pub fn save_config_to(path: &Path) {
return;
}
}
let temporary = path.with_extension("yaml.tmp");
if let Err(error) = fs::write(&temporary, yaml).and_then(|_| fs::rename(&temporary, path)) {
if let Err(error) = iota_util::atomic_file::replace(path, yaml.as_bytes(), 3) {
eprintln!("Cannot save {}: {error}", path.display());
let _ = fs::remove_file(temporary);
}
}
}

View file

@ -19,7 +19,7 @@ impl ManageConnection for SqliteManager {
fn connect(&self) -> Result<Connection, rusqlite::Error> {
let path = db_file_path(DB_NAME);
let conn = Connection::open(path)?;
conn.execute_batch("PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL;")?;
conn.execute_batch("PRAGMA journal_mode = WAL; PRAGMA synchronous = FULL;")?;
conn.busy_timeout(Duration::from_millis(250))?;
Ok(conn)
}
@ -55,6 +55,52 @@ where
f(&conn)
}
/* Verify the persistent database before the pool is initialized. A corrupt
* database is moved aside rather than opened again, preserving material for
* operator recovery while allowing the daemon to report the failed storage. */
pub fn verify_and_backup_database() -> Result<(), StorageError> {
let storage_dir = iota_util::file_util::storage_directory();
std::fs::create_dir_all(&storage_dir)?;
let path = storage_dir.join(format!("{DB_NAME}.sqlite3"));
if !path.exists() {
return Ok(());
}
let connection = Connection::open(&path)?;
connection.execute_batch("PRAGMA journal_mode = WAL; PRAGMA synchronous = FULL;")?;
connection.execute_batch("PRAGMA wal_checkpoint(FULL);")?;
let integrity: String = connection.query_row("PRAGMA integrity_check", [], |row| row.get(0))?;
drop(connection);
if integrity != "ok" {
let recovery = storage_dir.join("recovery");
std::fs::create_dir_all(&recovery)?;
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
for suffix in ["", "-wal", "-shm"] {
let source = PathBuf::from(format!("{}{}", path.display(), suffix));
if source.exists() {
let destination = recovery.join(format!("{DB_NAME}.sqlite3.{timestamp}{suffix}"));
std::fs::rename(source, destination)?;
}
}
return Err(StorageError::Other(format!(
"database integrity check failed ({integrity}); moved database files to {}",
recovery.display()
)));
}
let backup_dir = storage_dir.join("backups");
std::fs::create_dir_all(&backup_dir)?;
let backup = backup_dir.join(format!("{DB_NAME}.sqlite3"));
let temporary = backup_dir.join(format!(".{DB_NAME}.sqlite3.tmp"));
std::fs::copy(&path, &temporary)?;
std::fs::File::open(&temporary)?.sync_all()?;
std::fs::rename(temporary, backup)?;
Ok(())
}
fn db_file_path(db_name: &str) -> PathBuf {
let storage_dir = iota_util::file_util::storage_directory();
// Creating storage belongs to initialization/connection setup, never to a