refactor(backend): phase 5 - optimize concurrency with RwLock and async IO
Replace Mutex with RwLock for AppState.config to enable concurrent reads, improving performance for tray menu building and query operations that previously blocked each other unnecessarily. Key changes: - Migrate AppState.config from Mutex<MultiAppConfig> to RwLock<MultiAppConfig> - Distinguish read-only operations (read()) from mutations (write()) across all command handlers and service layers - Offload blocking file I/O in import/export commands to spawn_blocking threads, minimizing lock hold time and preventing main thread blocking - Extract load_config_for_import() to separate I/O logic from state updates - Update all integration tests to use RwLock semantics Performance impact: - Concurrent reads: Multiple threads can now query config simultaneously (tray menu, provider list, MCP config) - Reduced contention: Write locks only acquired during actual mutations - Non-blocking I/O: Config import/export no longer freezes UI thread All existing tests pass with new locking semantics.
This commit is contained in:
@@ -58,7 +58,7 @@ pub async fn get_mcp_config(
|
||||
.to_string();
|
||||
let mut cfg = state
|
||||
.config
|
||||
.lock()
|
||||
.write()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
let app_ty = AppType::from(app.as_deref().unwrap_or("claude"));
|
||||
let (servers, normalized) = mcp::get_servers_snapshot_for(&mut cfg, &app_ty);
|
||||
@@ -84,7 +84,7 @@ pub async fn upsert_mcp_server_in_config(
|
||||
) -> Result<bool, String> {
|
||||
let mut cfg = state
|
||||
.config
|
||||
.lock()
|
||||
.write()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
let app_ty = AppType::from(app.as_deref().unwrap_or("claude"));
|
||||
let mut sync_targets: Vec<AppType> = Vec::new();
|
||||
@@ -115,7 +115,7 @@ pub async fn upsert_mcp_server_in_config(
|
||||
|
||||
let cfg2 = state
|
||||
.config
|
||||
.lock()
|
||||
.read()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
for app_ty_to_sync in sync_targets {
|
||||
match app_ty_to_sync {
|
||||
@@ -135,7 +135,7 @@ pub async fn delete_mcp_server_in_config(
|
||||
) -> Result<bool, String> {
|
||||
let mut cfg = state
|
||||
.config
|
||||
.lock()
|
||||
.write()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
let app_ty = AppType::from(app.as_deref().unwrap_or("claude"));
|
||||
let existed = mcp::delete_in_config_for(&mut cfg, &app_ty, &id)?;
|
||||
@@ -143,7 +143,7 @@ pub async fn delete_mcp_server_in_config(
|
||||
state.save()?;
|
||||
let cfg2 = state
|
||||
.config
|
||||
.lock()
|
||||
.read()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
match app_ty {
|
||||
AppType::Claude => mcp::sync_enabled_to_claude(&cfg2)?,
|
||||
@@ -169,7 +169,7 @@ pub async fn set_mcp_enabled(
|
||||
pub async fn sync_enabled_mcp_to_claude(state: State<'_, AppState>) -> Result<bool, String> {
|
||||
let mut cfg = state
|
||||
.config
|
||||
.lock()
|
||||
.write()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
let normalized = mcp::normalize_servers_for(&mut cfg, &AppType::Claude);
|
||||
mcp::sync_enabled_to_claude(&cfg)?;
|
||||
@@ -186,7 +186,7 @@ pub async fn sync_enabled_mcp_to_claude(state: State<'_, AppState>) -> Result<bo
|
||||
pub async fn sync_enabled_mcp_to_codex(state: State<'_, AppState>) -> Result<bool, String> {
|
||||
let mut cfg = state
|
||||
.config
|
||||
.lock()
|
||||
.write()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
let normalized = mcp::normalize_servers_for(&mut cfg, &AppType::Codex);
|
||||
mcp::sync_enabled_to_codex(&cfg)?;
|
||||
@@ -216,7 +216,7 @@ fn set_mcp_enabled_internal(
|
||||
id: &str,
|
||||
enabled: bool,
|
||||
) -> Result<bool, AppError> {
|
||||
let mut cfg = state.config.lock()?;
|
||||
let mut cfg = state.config.write()?;
|
||||
let changed = mcp::set_enabled_and_sync_for(&mut cfg, &app_ty, id, enabled)?;
|
||||
drop(cfg);
|
||||
state.save()?;
|
||||
@@ -234,7 +234,7 @@ pub fn set_mcp_enabled_test_hook(
|
||||
}
|
||||
|
||||
fn import_mcp_from_claude_internal(state: &AppState) -> Result<usize, AppError> {
|
||||
let mut cfg = state.config.lock()?;
|
||||
let mut cfg = state.config.write()?;
|
||||
let changed = mcp::import_from_claude(&mut cfg)?;
|
||||
drop(cfg);
|
||||
if changed > 0 {
|
||||
@@ -249,7 +249,7 @@ pub fn import_mcp_from_claude_test_hook(state: &AppState) -> Result<usize, AppEr
|
||||
}
|
||||
|
||||
fn import_mcp_from_codex_internal(state: &AppState) -> Result<usize, AppError> {
|
||||
let mut cfg = state.config.lock()?;
|
||||
let mut cfg = state.config.write()?;
|
||||
let changed = mcp::import_from_codex(&mut cfg)?;
|
||||
drop(cfg);
|
||||
if changed > 0 {
|
||||
|
||||
@@ -60,7 +60,7 @@ pub async fn get_providers(
|
||||
|
||||
let config = state
|
||||
.config
|
||||
.lock()
|
||||
.read()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
|
||||
let manager = config
|
||||
@@ -85,7 +85,7 @@ pub async fn get_current_provider(
|
||||
|
||||
let config = state
|
||||
.config
|
||||
.lock()
|
||||
.read()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
|
||||
let manager = config
|
||||
@@ -114,7 +114,7 @@ pub async fn add_provider(
|
||||
let is_current = {
|
||||
let config = state
|
||||
.config
|
||||
.lock()
|
||||
.read()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
let manager = config
|
||||
.get_manager(&app_type)
|
||||
@@ -145,7 +145,7 @@ pub async fn add_provider(
|
||||
{
|
||||
let mut config = state
|
||||
.config
|
||||
.lock()
|
||||
.write()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
let manager = config
|
||||
.get_manager_mut(&app_type)
|
||||
@@ -178,7 +178,7 @@ pub async fn update_provider(
|
||||
let (exists, is_current) = {
|
||||
let config = state
|
||||
.config
|
||||
.lock()
|
||||
.read()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
let manager = config
|
||||
.get_manager(&app_type)
|
||||
@@ -215,7 +215,7 @@ pub async fn update_provider(
|
||||
{
|
||||
let mut config = state
|
||||
.config
|
||||
.lock()
|
||||
.write()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
let manager = config
|
||||
.get_manager_mut(&app_type)
|
||||
@@ -274,7 +274,7 @@ pub async fn delete_provider(
|
||||
{
|
||||
let mut config = state
|
||||
.config
|
||||
.lock()
|
||||
.write()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
ProviderService::delete(&mut config, app_type, &id).map_err(|e| e.to_string())?;
|
||||
}
|
||||
@@ -286,7 +286,7 @@ pub async fn delete_provider(
|
||||
|
||||
/// 切换供应商
|
||||
fn switch_provider_internal(state: &AppState, app_type: AppType, id: &str) -> Result<(), AppError> {
|
||||
let mut config = state.config.lock().map_err(AppError::from)?;
|
||||
let mut config = state.config.write().map_err(AppError::from)?;
|
||||
|
||||
ProviderService::switch(&mut config, app_type, id)?;
|
||||
|
||||
@@ -323,7 +323,7 @@ pub async fn switch_provider(
|
||||
|
||||
fn import_default_config_internal(state: &AppState, app_type: AppType) -> Result<(), AppError> {
|
||||
{
|
||||
let config = state.config.lock()?;
|
||||
let config = state.config.read()?;
|
||||
if let Some(manager) = config.get_manager(&app_type) {
|
||||
if !manager.get_all_providers().is_empty() {
|
||||
// 已存在供应商则视为已导入,保持与原逻辑一致
|
||||
@@ -358,7 +358,7 @@ fn import_default_config_internal(state: &AppState, app_type: AppType) -> Result
|
||||
None,
|
||||
);
|
||||
|
||||
let mut config = state.config.lock()?;
|
||||
let mut config = state.config.write()?;
|
||||
let manager = config
|
||||
.get_manager_mut(&app_type)
|
||||
.ok_or_else(|| AppError::Message(format!("应用类型不存在: {:?}", app_type)))?;
|
||||
@@ -420,7 +420,7 @@ pub async fn query_provider_usage(
|
||||
let (api_key, base_url, usage_script_code, timeout) = {
|
||||
let config = state
|
||||
.config
|
||||
.lock()
|
||||
.read()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
|
||||
let manager = config.get_manager(&app_type).ok_or("应用类型不存在")?;
|
||||
@@ -601,7 +601,7 @@ pub async fn get_custom_endpoints(
|
||||
.ok_or_else(|| "缺少 providerId".to_string())?;
|
||||
let mut cfg_guard = state
|
||||
.config
|
||||
.lock()
|
||||
.write()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
|
||||
let manager = cfg_guard
|
||||
@@ -647,7 +647,7 @@ pub async fn add_custom_endpoint(
|
||||
|
||||
let mut cfg_guard = state
|
||||
.config
|
||||
.lock()
|
||||
.write()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
let manager = cfg_guard
|
||||
.get_manager_mut(&app_type)
|
||||
@@ -696,7 +696,7 @@ pub async fn remove_custom_endpoint(
|
||||
|
||||
let mut cfg_guard = state
|
||||
.config
|
||||
.lock()
|
||||
.write()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
let manager = cfg_guard
|
||||
.get_manager_mut(&app_type)
|
||||
@@ -734,7 +734,7 @@ pub async fn update_endpoint_last_used(
|
||||
|
||||
let mut cfg_guard = state
|
||||
.config
|
||||
.lock()
|
||||
.write()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
let manager = cfg_guard
|
||||
.get_manager_mut(&app_type)
|
||||
@@ -779,7 +779,7 @@ pub async fn update_providers_sort_order(
|
||||
|
||||
let mut config = state
|
||||
.config
|
||||
.lock()
|
||||
.write()
|
||||
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||
|
||||
let manager = config
|
||||
|
||||
Reference in New Issue
Block a user