perf: full-stack performance and security hardening

Frontend performance:
- Debounce DataGrid search (150ms) and replace O(n) row wrapping with index array
- Replace QueryStore deep watcher with targeted field-level persistence snapshot
- Extract shared nodeConfig computed in TreeItem (eliminates 15x redundant getConfig calls)
- Clear tab result data on close to free memory
- Add LRU eviction for completion caches (50 entry cap)
- Replace savedSql version string join with numeric counter

Memory leak fixes:
- Store Tauri event unlisten handles and clean up on unmount
- Clean up DataGrid search timer on unmount

Backend security & resource management:
- Add 50MB request body size limit
- Reject file uploads >100MB and SQL files >200MB
- Clean up SSE channels after transfer/import completes
- Recover from poisoned mutex instead of panicking
- Delete temporary upload files after preview
- Validate SQL file paths to prevent directory traversal
This commit is contained in:
t8y2
2026-05-11 00:18:09 +08:00
parent 741783445d
commit f456ddd979
13 changed files with 230 additions and 87 deletions
+4 -4
View File
@@ -10,13 +10,13 @@ pub struct RunningQueries {
impl RunningQueries {
pub fn register(&self, execution_id: String) -> RegisteredQuery {
let token = CancellationToken::new();
self.inner.lock().expect("running query registry poisoned").insert(execution_id.clone(), token.clone());
self.inner.lock().unwrap_or_else(|e| e.into_inner()).insert(execution_id.clone(), token.clone());
RegisteredQuery { execution_id, token, running_queries: self.clone() }
}
pub fn cancel(&self, execution_id: &str) -> bool {
let token = self.inner.lock().expect("running query registry poisoned").get(execution_id).cloned();
let token = self.inner.lock().unwrap_or_else(|e| e.into_inner()).get(execution_id).cloned();
if let Some(token) = token {
token.cancel();
@@ -28,11 +28,11 @@ impl RunningQueries {
#[cfg(test)]
pub fn has(&self, execution_id: &str) -> bool {
self.inner.lock().expect("running query registry poisoned").contains_key(execution_id)
self.inner.lock().unwrap_or_else(|e| e.into_inner()).contains_key(execution_id)
}
fn remove(&self, execution_id: &str) {
self.inner.lock().expect("running query registry poisoned").remove(execution_id);
self.inner.lock().unwrap_or_else(|e| e.into_inner()).remove(execution_id);
}
}