perf: replace inefficient memory cloning vectors with fast zero-allocation read iterators

This commit is contained in:
Riz Ashraf committed 2026-09-21 12:51:26 +01:00
1 parent 5da422d23a
commit 3c31aeec1f
6 files changed
+97 -58

No files matched your search

BIN
View File
Binary file not shown.
+6 -3
View File
@@ -155,9 +155,12 @@ impl McpTool for GetEnvironmentDetailsHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: GetEnvironmentDetailsTool = let req: GetEnvironmentDetailsTool =
serde_json::from_value(args).map_err(|e| e.to_string())?; serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut envs = state.environments.read(); let data = state.environments.read_with(|envs| {
envs.retain(|e| e.namespace == req.namespace); let filtered: Vec<_> = envs.iter().filter(|e| e.namespace == req.namespace).collect();
let data = serde_json::to_string(&envs).unwrap_or_default(); serde_json::to_string(&filtered).unwrap_or_default()
});
Ok(data.to_string()) Ok(data.to_string())
} }
} }
+22 -14
View File
@@ -65,16 +65,19 @@ impl McpTool for QueryDecisionsHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: QueryDecisionsTool = serde_json::from_value(args).map_err(|e| e.to_string())?; let req: QueryDecisionsTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut adrs = state.adrs.read(); let data = state.adrs.read_with(|adrs| {
if let Some(q) = req.query { let filtered: Vec<_> = adrs.iter().filter(|a| {
if let Some(q) = &req.query {
let q = q.to_lowercase(); let q = q.to_lowercase();
adrs.retain(|a| {
contains_ignore_ascii_case(&a.title, &q) contains_ignore_ascii_case(&a.title, &q)
|| contains_ignore_ascii_case(&a.context, &q) || contains_ignore_ascii_case(&a.context, &q)
|| contains_ignore_ascii_case(&a.decision, &q) || contains_ignore_ascii_case(&a.decision, &q)
}); } else {
true
} }
let data = serde_json::to_string(&adrs).unwrap_or_default(); }).collect();
serde_json::to_string(&filtered).unwrap_or_default()
});
Ok(data.to_string()) Ok(data.to_string())
} }
} }
@@ -127,12 +130,13 @@ impl McpTool for SearchErrorFixesHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: SearchErrorFixesTool = serde_json::from_value(args).map_err(|e| e.to_string())?; let req: SearchErrorFixesTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let q = req.query.to_lowercase(); let q = req.query.to_lowercase();
let mut fixes = state.error_fixes.read(); let data = state.error_fixes.read_with(|fixes| {
fixes.retain(|f| { let filtered: Vec<_> = fixes.iter().filter(|f| {
contains_ignore_ascii_case(&f.signature, &q) contains_ignore_ascii_case(&f.signature, &q)
|| contains_ignore_ascii_case(&f.solution, &q) || contains_ignore_ascii_case(&f.solution, &q)
}).collect();
serde_json::to_string(&filtered).unwrap_or_default()
}); });
let data = serde_json::to_string(&fixes).unwrap_or_default();
Ok(data.to_string()) Ok(data.to_string())
} }
} }
@@ -183,7 +187,7 @@ impl McpTool for QueryRecentChangesHandler {
} }
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let data = serde_json::to_string(&state.ledger.read()).unwrap_or_else(|_| "[]".to_string()); let data = state.ledger.read_with(|l| serde_json::to_string(l).unwrap_or_else(|_| "[]".to_string()));
Ok(data.to_string()) Ok(data.to_string())
} }
} }
@@ -232,8 +236,7 @@ impl McpTool for ReadPreferencesHandler {
} }
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let prefs = state.prefs.read(); let data = state.prefs.read_with(|prefs| serde_json::to_string(prefs).unwrap_or_default());
let data = serde_json::to_string(&prefs).unwrap_or_default();
Ok(data.to_string()) Ok(data.to_string())
} }
} }
@@ -320,9 +323,12 @@ impl McpTool for ListTechDebtHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: ListTechDebtTool = serde_json::from_value(args).map_err(|e| e.to_string())?; let req: ListTechDebtTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut debts = state.tech_debts.read(); let data = state.tech_debts.read_with(|debts| {
debts.retain(|d| d.namespace == req.namespace && (req.include_resolved || !d.is_resolved)); let filtered: Vec<_> = debts.iter().filter(|d| {
let data = serde_json::to_string(&debts).unwrap_or_default(); d.namespace == req.namespace && (req.include_resolved || !d.is_resolved)
}).collect();
serde_json::to_string(&filtered).unwrap_or_default()
});
Ok(data.to_string()) Ok(data.to_string())
} }
} }
@@ -490,3 +496,5 @@ impl McpTool for GetProjectHealthHandler {
} }
use crate::handlers_v2::utils::*; use crate::handlers_v2::utils::*;
+11 -5
View File
@@ -51,7 +51,7 @@ impl McpTool for ReadStickyNotesHandler {
} }
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let data = serde_json::to_string(&state.sticky.read()).unwrap_or_else(|_| "[]".to_string()); let data = state.sticky.read_with(|s| serde_json::to_string(s).unwrap_or_else(|_| "[]".to_string()));
Ok(data.to_string()) Ok(data.to_string())
} }
} }
@@ -161,11 +161,16 @@ impl McpTool for ReadHandoffMemosHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: ReadHandoffMemosTool = serde_json::from_value(args).map_err(|e| e.to_string())?; let req: ReadHandoffMemosTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut items = state.handoff_memos.read(); let data = state.handoff_memos.read_with(|items| {
if let Some(ns) = req.namespace { let filtered: Vec<_> = items.iter().filter(|i| {
items.retain(|i| i.namespace == ns); if let Some(ns) = &req.namespace {
&i.namespace == ns
} else {
true
} }
let data = serde_json::to_string(&items).unwrap_or_default(); }).collect();
serde_json::to_string(&filtered).unwrap_or_default()
});
Ok(data.to_string()) Ok(data.to_string())
} }
} }
@@ -271,3 +276,4 @@ impl McpTool for GenerateStandupReportHandler {
Ok(report.to_string()) Ok(report.to_string())
} }
} }
+25 -18
View File
@@ -263,15 +263,18 @@ impl McpTool for ListActiveTasksHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: ListActiveTasksTool = serde_json::from_value(args).map_err(|e| e.to_string())?; let req: ListActiveTasksTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut tasks = state.tasks.read(); let data = state.tasks.read_with(|tasks| {
tasks.retain(|t| t.status != "done"); let filtered: Vec<_> = tasks.iter().filter(|t| {
if let Some(branch) = req.git_branch { let status_match = t.status != "done";
tasks.retain(|t| { let branch_match = match &req.git_branch {
t.git_branch.is_none() || t.git_branch.as_deref() == Some(branch.as_str()) Some(branch) => t.git_branch.is_none() || t.git_branch.as_deref() == Some(branch.as_str()),
None => true,
};
status_match && branch_match
}).collect();
serde_json::to_string(&filtered).unwrap_or_default()
}); });
} Ok(data)
let data = serde_json::to_string(&tasks).unwrap_or_default();
Ok(data.to_string())
} }
} }
@@ -313,7 +316,7 @@ impl McpTool for SetAcceptanceCriteriaHandler {
} }
}); });
if success { if success {
Ok(vec!["Acceptance criteria set successfully.".to_string()][0].clone()) Ok("Acceptance criteria set successfully.".to_string())
} else { } else {
Ok("Task not found.".to_string()) Ok("Task not found.".to_string())
} }
@@ -360,15 +363,14 @@ impl McpTool for VerifyAcceptanceCriteriaHandler {
} }
}); });
if success { if success {
Ok(vec![format!( Ok(format!(
"Acceptance criteria verified with proof: {}", "Acceptance criteria verified with proof: {}",
req.proof req.proof
)][0] ))
.clone())
} else if already_met { } else if already_met {
Ok("Acceptance criteria was already met.".to_string()) Ok("Acceptance criteria was already met.".to_string())
} else { } else {
Ok(vec!["Acceptance criteria or task not found.".to_string()][0].clone()) Ok("Acceptance criteria or task not found.".to_string())
} }
} }
} }
@@ -446,11 +448,16 @@ impl McpTool for ListMilestonesHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: ListMilestonesTool = serde_json::from_value(args).map_err(|e| e.to_string())?; let req: ListMilestonesTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut items = state.milestones.read(); let data = state.milestones.read_with(|items| {
if let Some(ns) = req.namespace { let filtered: Vec<_> = items.iter().filter(|i| {
items.retain(|i| i.namespace == ns); if let Some(ns) = &req.namespace {
&i.namespace == ns
} else {
true
} }
let data = serde_json::to_string(&items).unwrap_or_default(); }).collect();
Ok(data.to_string()) serde_json::to_string(&filtered).unwrap_or_default()
});
Ok(data)
} }
} }
+33 -18
View File
@@ -75,16 +75,20 @@ impl McpTool for ListPinnedFilesHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: ListPinnedFilesTool = serde_json::from_value(args).map_err(|e| e.to_string())?; let req: ListPinnedFilesTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut pinned = state.pinned_files.read(); let data = state.pinned_files.read_with(|pinned| {
if let Some(ns) = req.namespace { let filtered: Vec<_> = pinned.iter().filter(|p| {
pinned.retain(|p| p.namespace == ns); let ns_match = match &req.namespace {
} Some(ns) => &p.namespace == ns,
if let Some(branch) = req.git_branch { None => true,
pinned.retain(|p| { };
p.git_branch.is_none() || p.git_branch.as_deref() == Some(branch.as_str()) let branch_match = match &req.git_branch {
Some(branch) => p.git_branch.is_none() || p.git_branch.as_deref() == Some(branch.as_str()),
None => true,
};
ns_match && branch_match
}).collect();
serde_json::to_string(&filtered).unwrap_or_default()
}); });
}
let data = serde_json::to_string(&pinned).unwrap_or_default();
Ok(data.to_string()) Ok(data.to_string())
} }
} }
@@ -239,9 +243,12 @@ impl McpTool for LoadContextWorkspaceHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: LoadContextWorkspaceTool = let req: LoadContextWorkspaceTool =
serde_json::from_value(args).map_err(|e| e.to_string())?; serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut ws = state.context_workspaces.read(); let data = state.context_workspaces.read_with(|ws| {
ws.retain(|w| w.namespace == req.namespace && w.name == req.name); let filtered: Vec<_> = ws.iter().filter(|w| {
let data = serde_json::to_string(&ws.first()).unwrap_or_default(); w.namespace == req.namespace && w.name == req.name
}).collect();
serde_json::to_string(&filtered.first()).unwrap_or_default()
});
Ok(data.to_string()) Ok(data.to_string())
} }
} }
@@ -264,9 +271,12 @@ impl McpTool for ListContextWorkspacesHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: ListContextWorkspacesTool = let req: ListContextWorkspacesTool =
serde_json::from_value(args).map_err(|e| e.to_string())?; serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut ws = state.context_workspaces.read(); let data = state.context_workspaces.read_with(|ws| {
ws.retain(|w| w.namespace == req.namespace); let filtered: Vec<_> = ws.iter().filter(|w| {
let data = serde_json::to_string(&ws).unwrap_or_default(); w.namespace == req.namespace
}).collect();
serde_json::to_string(&filtered).unwrap_or_default()
});
Ok(data.to_string()) Ok(data.to_string())
} }
} }
@@ -314,9 +324,12 @@ impl McpTool for GetPrChecklistHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: GetPrChecklistTool = serde_json::from_value(args).map_err(|e| e.to_string())?; let req: GetPrChecklistTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut items = state.pr_checklists.read(); let data = state.pr_checklists.read_with(|items| {
items.retain(|i| i.namespace == req.namespace); let filtered: Vec<_> = items.iter().filter(|i| {
let data = serde_json::to_string(&items).unwrap_or_default(); i.namespace == req.namespace
}).collect();
serde_json::to_string(&filtered).unwrap_or_default()
});
Ok(data.to_string()) Ok(data.to_string())
} }
} }
@@ -346,3 +359,5 @@ impl McpTool for ClearPrChecklistHandler {
} }
use crate::handlers_v2::utils::*; use crate::handlers_v2::utils::*;