feat: implement draft management system for concurrent editing and committing of data

This commit is contained in:
Tunglies
2025-10-15 08:32:30 +08:00
parent 6113be3b6c
commit e6b7d512fb
9 changed files with 127 additions and 68 deletions

1
src-tauri/Cargo.lock generated
View File

@@ -1470,6 +1470,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"tinytemplate", "tinytemplate",
"tokio",
"walkdir", "walkdir",
] ]

View File

@@ -128,6 +128,11 @@ tauri-dev = []
tokio-trace = ["console-subscriber"] tokio-trace = ["console-subscriber"]
clippy = ["tauri/test"] clippy = ["tauri/test"]
[[bench]]
name = "draft_benchmark"
path = "benches/draft_benchmark.rs"
harness = false
[profile.release] [profile.release]
panic = "abort" panic = "abort"
codegen-units = 16 codegen-units = 16
@@ -162,7 +167,7 @@ name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"] crate-type = ["staticlib", "cdylib", "rlib"]
[dev-dependencies] [dev-dependencies]
criterion = "0.7.0" criterion = { version = "0.7.0", features = ["async_tokio"] }
[lints.clippy] [lints.clippy]
# Core categories - most important for code safety and correctness # Core categories - most important for code safety and correctness

View File

@@ -1,91 +1,138 @@
use criterion::{Criterion, criterion_group, criterion_main}; use criterion::{Criterion, criterion_group, criterion_main};
use std::hint::black_box; use std::hint::black_box;
use tokio::runtime::Runtime;
// 业务模型 & Draft // 引入业务模型 & Draft 实现
use app_lib::config::Draft as DraftNew;
use app_lib::config::IVerge; use app_lib::config::IVerge;
use app_lib::utils::Draft as DraftNew;
// fn bench_apply_old(c: &mut Criterion) { /// 创建测试数据
// c.bench_function("apply_draft_old", |b| { fn make_draft() -> DraftNew<Box<IVerge>> {
// b.iter(|| { let verge = Box::new(IVerge {
// let verge = Box::new(IVerge { enable_auto_launch: Some(true),
// enable_auto_launch: Some(true), enable_tun_mode: Some(false),
// enable_tun_mode: Some(false), ..Default::default()
// ..Default::default() });
// }); DraftNew::from(verge)
}
// let draft = DraftOld::from(black_box(verge)); /// 基准:只读 data_ref正式数据
fn bench_data_ref(c: &mut Criterion) {
// { c.bench_function("draft_data_ref", |b| {
// let mut d = draft.draft_mut();
// d.enable_auto_launch = Some(false);
// }
// let _ = draft.apply();
// });
// });
// }
// fn bench_discard_old(c: &mut Criterion) {
// c.bench_function("discard_draft_old", |b| {
// b.iter(|| {
// let verge = Box::new(IVerge::default());
// let draft = DraftOld::from(black_box(verge));
// {
// let mut d = draft.draft_mut();
// d.enable_auto_launch = Some(false);
// }
// let _ = draft.discard();
// });
// });
// }
/// 基准:修改草稿并 apply()
fn bench_apply_new(c: &mut Criterion) {
c.bench_function("apply_draft_new", |b| {
b.iter(|| { b.iter(|| {
let verge = Box::new(IVerge { let draft = make_draft();
enable_auto_launch: Some(true), let data = draft.data_ref();
enable_tun_mode: Some(false), black_box(data.enable_auto_launch);
..Default::default() });
}); });
}
let draft = DraftNew::from(black_box(verge)); /// 基准:可写 data_mut正式数据
fn bench_data_mut(c: &mut Criterion) {
c.bench_function("draft_data_mut", |b| {
b.iter(|| {
let draft = make_draft();
let mut data = draft.data_mut();
data.enable_tun_mode = Some(true);
black_box(data.enable_tun_mode);
});
});
}
/// 基准:首次创建草稿(会触发 clone
fn bench_draft_mut_first(c: &mut Criterion) {
c.bench_function("draft_draft_mut_first", |b| {
b.iter(|| {
let draft = make_draft();
let mut d = draft.draft_mut();
d.enable_auto_launch = Some(false);
black_box(d.enable_auto_launch);
});
});
}
/// 基准:重复 draft_mut已存在草稿不再 clone
fn bench_draft_mut_existing(c: &mut Criterion) {
c.bench_function("draft_draft_mut_existing", |b| {
b.iter(|| {
let draft = make_draft();
{
let mut first = draft.draft_mut();
first.enable_tun_mode = Some(true);
}
let mut second = draft.draft_mut();
second.enable_tun_mode = Some(false);
black_box(second.enable_tun_mode);
});
});
}
/// 基准零拷贝读取最新视图latest_ref
fn bench_latest_ref(c: &mut Criterion) {
c.bench_function("draft_latest_ref", |b| {
b.iter(|| {
let draft = make_draft();
let latest = draft.latest_ref();
black_box(latest.enable_auto_launch);
});
});
}
/// 基准apply提交草稿
fn bench_apply(c: &mut Criterion) {
c.bench_function("draft_apply", |b| {
b.iter(|| {
let draft = make_draft();
{ {
let mut d = draft.draft_mut(); let mut d = draft.draft_mut();
d.enable_auto_launch = Some(false); d.enable_auto_launch = Some(false);
} }
let _ = draft.apply(); let _ = draft.apply();
}); });
}); });
} }
/// 基准:修改草稿并 discard() /// 基准discard(丢弃草稿)
fn bench_discard_new(c: &mut Criterion) { fn bench_discard(c: &mut Criterion) {
c.bench_function("discard_draft_new", |b| { c.bench_function("draft_discard", |b| {
b.iter(|| { b.iter(|| {
let verge = Box::new(IVerge::default()); let draft = make_draft();
let draft = DraftNew::from(black_box(verge));
{ {
let mut d = draft.draft_mut(); let mut d = draft.draft_mut();
d.enable_auto_launch = Some(false); d.enable_auto_launch = Some(false);
} }
let _ = draft.discard(); let _ = draft.discard();
}); });
}); });
} }
/// 基准:异步 with_data_modify
fn bench_with_data_modify(c: &mut Criterion) {
let rt = Runtime::new().unwrap();
c.bench_function("draft_with_data_modify", |b| {
b.to_async(&rt).iter(|| async {
let draft = make_draft();
let _res: Result<(), anyhow::Error> = draft
.with_data_modify(|mut box_data| async move {
box_data.enable_auto_launch =
Some(!box_data.enable_auto_launch.unwrap_or(false));
Ok((box_data, ()))
})
.await;
});
});
}
criterion_group!( criterion_group!(
benches, benches,
// bench_apply_old, bench_data_ref,
// bench_discard_old, bench_data_mut,
bench_apply_new, bench_draft_mut_first,
bench_discard_new bench_draft_mut_existing,
bench_latest_ref,
bench_apply,
bench_discard,
bench_with_data_modify
); );
criterion_main!(benches); criterion_main!(benches);

View File

@@ -1,9 +1,9 @@
use super::{Draft, IClashTemp, IProfiles, IRuntime, IVerge}; use super::{IClashTemp, IProfiles, IRuntime, IVerge};
use crate::{ use crate::{
config::{PrfItem, profiles_append_item_safe}, config::{PrfItem, profiles_append_item_safe},
core::{CoreManager, handle}, core::{CoreManager, handle},
enhance, logging, enhance, logging,
utils::{dirs, help, logging::Type}, utils::{Draft, dirs, help, logging::Type},
}; };
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use backoff::{Error as BackoffError, ExponentialBackoff}; use backoff::{Error as BackoffError, ExponentialBackoff};

View File

@@ -1,16 +1,13 @@
mod clash; mod clash;
#[allow(clippy::module_inception)] #[allow(clippy::module_inception)]
mod config; mod config;
mod draft;
mod encrypt; mod encrypt;
mod prfitem; mod prfitem;
pub mod profiles; pub mod profiles;
mod runtime; mod runtime;
mod verge; mod verge;
pub use self::{ pub use self::{clash::*, config::*, encrypt::*, prfitem::*, profiles::*, runtime::*, verge::*};
clash::*, config::*, draft::*, encrypt::*, prfitem::*, profiles::*, runtime::*, verge::*,
};
pub const DEFAULT_PAC: &str = r#"function FindProxyForURL(url, host) { pub const DEFAULT_PAC: &str = r#"function FindProxyForURL(url, host) {
return "PROXY 127.0.0.1:%mixed-port%; SOCKS5 127.0.0.1:%mixed-port%; DIRECT;"; return "PROXY 127.0.0.1:%mixed-port%; SOCKS5 127.0.0.1:%mixed-port%; DIRECT;";

View File

@@ -8,7 +8,7 @@ mod enhance;
mod feat; mod feat;
mod module; mod module;
mod process; mod process;
mod utils; pub mod utils;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use crate::utils::window_manager::WindowManager; use crate::utils::window_manager::WindowManager;
use crate::{ use crate::{

View File

@@ -109,7 +109,7 @@ impl<T: Clone + ToOwned> Draft<Box<T>> {
#[test] #[test]
fn test_draft_box() { fn test_draft_box() {
use super::IVerge; use crate::config::IVerge;
// 1. 创建 Draft<Box<IVerge>> // 1. 创建 Draft<Box<IVerge>>
let verge = Box::new(IVerge { let verge = Box::new(IVerge {

View File

@@ -1,5 +1,6 @@
pub mod autostart; pub mod autostart;
pub mod dirs; pub mod dirs;
pub mod draft;
pub mod format; pub mod format;
pub mod help; pub mod help;
pub mod i18n; pub mod i18n;
@@ -12,3 +13,5 @@ pub mod server;
pub mod singleton; pub mod singleton;
pub mod tmpl; pub mod tmpl;
pub mod window_manager; pub mod window_manager;
pub use draft::Draft;

View File

@@ -61,6 +61,12 @@ pub struct NetworkManager {
connection_error_count: Mutex<usize>, connection_error_count: Mutex<usize>,
} }
impl Default for NetworkManager {
fn default() -> Self {
Self::new()
}
}
impl NetworkManager { impl NetworkManager {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {