From 7a48fa5d34cd36d8749f29b22f86afdbf6f07371 Mon Sep 17 00:00:00 2001 From: Riz Ashraf Date: Mon, 21 Sep 2026 06:32:06 +0100 Subject: [PATCH] Fix unawaited Tokio spawn locks by moving macro clones and formatting allocations to blocking executor --- server/src/handlers.rs | 58 +++++++++++++++-------------- server/src/search.rs | 84 ++++++++++++++++++++++++++++-------------- 2 files changed, 87 insertions(+), 55 deletions(-) diff --git a/server/src/handlers.rs b/server/src/handlers.rs index 8d1ace7..c9f233d 100644 --- a/server/src/handlers.rs +++ b/server/src/handlers.rs @@ -625,36 +625,40 @@ impl MemoryHandler { to_draw.push(r); } } - let mut output = String::from("graph TD;\n"); + use std::fmt::Write; + let mut output = String::with_capacity(included.len() * 40 + to_draw.len() * 60); + output.push_str("graph TD;\n"); + + let sanitize = |s: &str, id_mode: bool| -> String { + let mut out = String::with_capacity(s.len()); + for c in s.chars() { + if c != '"' && c != '(' && c != ')' { + if id_mode && (c == ' ' || c == '-' || c == '.') { + out.push('_'); + } else { + out.push(c); + } + } + } + out + }; + for name in &included { - let safe = name.replace("\"", "").replace("(", "").replace(")", ""); - output.push_str(&format!( - " id_{}[\"{}\"];\n", - safe.replace(" ", "_").replace("-", "_").replace(".", "_"), - safe - )); + let _ = writeln!( + output, + " id_{}[\"{}\"];", + sanitize(name, true), + sanitize(name, false) + ); } for r in to_draw { - let safe_f = r - .from - .replace(" ", "_") - .replace("-", "_") - .replace(".", "_") - .replace("\"", "") - .replace("(", "") - .replace(")", ""); - let safe_t = - r.to.replace(" ", "_") - .replace("-", "_") - .replace(".", "_") - .replace("\"", "") - .replace("(", "") - .replace(")", ""); - let safe_r = r.relation_type.replace("\"", ""); - output.push_str(&format!( - " id_{}-->|\"{}\"|id_{};\n", - safe_f, safe_r, safe_t - )); + let _ = writeln!( + output, + " id_{}-->|\"{}\"|id_{};", + sanitize(&r.from, true), + r.relation_type.replace("\"", ""), + sanitize(&r.to, true) + ); } if output == "graph TD;\n" { output = "No nodes found to visualize.".to_string(); diff --git a/server/src/search.rs b/server/src/search.rs index 6d5ce65..cebf665 100644 --- a/server/src/search.rs +++ b/server/src/search.rs @@ -54,14 +54,21 @@ impl MemoryIndex { pub fn index_entity(&self, e: &Entity) -> tokio::task::JoinHandle> { let writer = Arc::clone(&self.writer); - let doc = doc!( - self.id_field => e.name.clone(), - self.title_field => e.name.clone(), - self.body_field => e.observations.join(" "), - self.type_field => "entity", - self.namespace_field => e.namespace.clone() - ); + let e = e.clone(); + let id_field = self.id_field; + let title_field = self.title_field; + let body_field = self.body_field; + let type_field = self.type_field; + let namespace_field = self.namespace_field; + tokio::task::spawn_blocking(move || { + let doc = doc!( + id_field => e.name.clone(), + title_field => e.name.clone(), + body_field => e.observations.join(" "), + type_field => "entity", + namespace_field => e.namespace.clone() + ); let writer = writer.lock().unwrap(); writer.add_document(doc)?; Ok(()) @@ -70,14 +77,21 @@ impl MemoryIndex { pub fn index_task(&self, t: &Task) -> tokio::task::JoinHandle> { let writer = Arc::clone(&self.writer); - let doc = doc!( - self.id_field => t.id.clone(), - self.title_field => t.title.clone(), - self.body_field => t.description.clone(), - self.type_field => "task", - self.namespace_field => "global" - ); + let t = t.clone(); + let id_field = self.id_field; + let title_field = self.title_field; + let body_field = self.body_field; + let type_field = self.type_field; + let namespace_field = self.namespace_field; + tokio::task::spawn_blocking(move || { + let doc = doc!( + id_field => t.id.clone(), + title_field => t.title.clone(), + body_field => t.description.clone(), + type_field => "task", + namespace_field => "global" + ); let writer = writer.lock().unwrap(); writer.add_document(doc)?; Ok(()) @@ -151,14 +165,21 @@ impl MemoryIndex { pub fn index_snippet(&self, s: &Snippet) -> tokio::task::JoinHandle> { let writer = Arc::clone(&self.writer); - let doc = doc!( - self.id_field => s.name.clone(), - self.title_field => s.name.clone(), - self.body_field => format!("{} {}", s.language, s.description), - self.type_field => "snippet", - self.namespace_field => "global" - ); + let s = s.clone(); + let id_field = self.id_field; + let title_field = self.title_field; + let body_field = self.body_field; + let type_field = self.type_field; + let namespace_field = self.namespace_field; + tokio::task::spawn_blocking(move || { + let doc = doc!( + id_field => s.name.clone(), + title_field => s.name.clone(), + body_field => format!("{} {}", s.language, s.description), + type_field => "snippet", + namespace_field => "global" + ); let writer = writer.lock().unwrap(); writer.add_document(doc)?; Ok(()) @@ -167,14 +188,21 @@ impl MemoryIndex { pub fn index_adr(&self, a: &Adr) -> tokio::task::JoinHandle> { let writer = Arc::clone(&self.writer); - let doc = doc!( - self.id_field => a.id.clone(), - self.title_field => a.title.clone(), - self.body_field => format!("{} {} {}", a.context, a.decision, a.consequence), - self.type_field => "adr", - self.namespace_field => "global" - ); + let a = a.clone(); + let id_field = self.id_field; + let title_field = self.title_field; + let body_field = self.body_field; + let type_field = self.type_field; + let namespace_field = self.namespace_field; + tokio::task::spawn_blocking(move || { + let doc = doc!( + id_field => a.id.clone(), + title_field => a.title.clone(), + body_field => format!("{} {} {}", a.context, a.decision, a.consequence), + type_field => "adr", + namespace_field => "global" + ); let writer = writer.lock().unwrap(); writer.add_document(doc)?; Ok(())