From 99ef870077ed88e400cb124f7c592e5f429b0df6 Mon Sep 17 00:00:00 2001 From: Jason Date: Mon, 9 Mar 2026 12:27:32 +0800 Subject: [PATCH] perf: skip backup and atomic write when OpenClaw config is unchanged --- src-tauri/src/openclaw_config.rs | 51 +++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/openclaw_config.rs b/src-tauri/src/openclaw_config.rs index b0338e381..8b0bc0eba 100644 --- a/src-tauri/src/openclaw_config.rs +++ b/src-tauri/src/openclaw_config.rs @@ -339,13 +339,28 @@ impl OpenClawConfigDocument { )); } + let next_source = self.text.to_string(); + if current_source.as_deref() == Some(next_source.as_str()) { + let warnings = scan_openclaw_health_from_value( + &json5::from_str::(&next_source).map_err(|e| { + AppError::Config(format!( + "Failed to parse unchanged OpenClaw config as JSON5: {e}" + )) + })?, + ); + + return Ok(OpenClawWriteOutcome { + backup_path: None, + warnings, + }); + } + let backup_path = current_source .as_ref() .map(|source| create_openclaw_backup(source)) .transpose()? .map(|path| path.display().to_string()); - let next_source = self.text.to_string(); atomic_write(&self.path, next_source.as_bytes())?; let warnings = scan_openclaw_health_from_value( @@ -981,6 +996,40 @@ mod tests { }); } + #[test] + fn default_model_noop_write_skips_backup() { + let source = r#"{ + models: { + mode: 'merge', + providers: {}, + }, +} +"#; + + with_test_paths(source, |_| { + let model = OpenClawDefaultModel { + primary: "provider/model".to_string(), + fallbacks: vec!["provider/fallback".to_string()], + extra: HashMap::new(), + }; + + let first_outcome = set_default_model(&model).unwrap(); + assert!(first_outcome.backup_path.is_some()); + + let first_written = fs::read_to_string(get_openclaw_config_path()).unwrap(); + let backup_dir = get_app_config_dir().join("backups").join("openclaw"); + let backup_count = fs::read_dir(&backup_dir).unwrap().count(); + assert_eq!(backup_count, 1); + + let second_outcome = set_default_model(&model).unwrap(); + assert!(second_outcome.backup_path.is_none()); + + let second_written = fs::read_to_string(get_openclaw_config_path()).unwrap(); + assert_eq!(second_written, first_written); + assert_eq!(fs::read_dir(&backup_dir).unwrap().count(), backup_count); + }); + } + #[test] fn save_detects_external_conflict() { let source = r#"{