Files
mcp-memory/agent-rules/rust_concurrency_quirks.md
T

3.9 KiB

Rust Guidelines & Quirks

Concurrency & Async Locking

  • Lock Poisoning & Async Safety: Use tokio::sync::RwLock for state shared across Tokio async tasks (such as active WebSocket clients) to avoid blocking worker threads during broadcast fanouts. For synchronous locks, prefer non-poisoning structures or recover cleanly using .unwrap_or_else(|e| e.into_inner()).
  • Atomic Store Write Lock Minimization: Minimize write lock duration by executing JSON serialization under read lock guards, keeping write guards strictly to in-memory state mutations.
  • Two-Phase Graph Condensation: When performing summarization or condensation across stores (condense_graph_worker), implement a two-phase commit: read non-destructively and synthesize observations first, insert into the knowledge graph, and only prune summarized source records by timestamp/content after successful insertion.
  • Watch-Based Non-Destructive Shutdown Channels: Use tokio::sync::watch rather than tokio::sync::mpsc for cancellation signaling to allow multiple workers to observe shutdown state without consuming or starving sibling workers.
  • Redb Transient Lock Resilience: Implement retry loops with exponential backoff on table or database lock contention before aborting or panicking.
  • Safe RPC Request Tracking: Clean up pending request maps (PENDING_REQUESTS.remove(&msgid)) upon timeouts or channel disconnects to prevent orphan memory leaks.
  • Panic-Free Architecture: Avoid raw .unwrap() in production runtime paths. Use .unwrap_or_default(), or proper Result propagation for runtime operations.
  • Background Worker Task Supervision: Always track tokio::task::JoinHandle handles for background workers (ttl_sweeper_worker, index_committer_worker, condense_graph_worker) and log thread exit or panic events cleanly.
  • Offload Heavy Index Rebuilds: In MemoryState::rebuild_index, offload full graph cloning and Tantivy document re-indexing into tokio::task::spawn_blocking to avoid stalling async worker threads.

Embedding & Memory Optimizations

  • Dynamic Character Batching: In embedding generation (generate_embeddings_async), dynamically chunk batches based on total character size (e.g. 16,384 chars) rather than static item counts to prevent OOM spikes on large files while maximizing SIMD throughput.
  • SIMD Cosine Similarity: Compute dot-product and norm accumulators in single-pass iterator folds to facilitate vector auto-vectorization across CPU instruction sets (AVX2/NEON).
  • Memory Truncation Bounds: Enforce a 4KB ceiling on telemetry detail strings (ActivityRecord, TerminalHistory) before queuing items into ring buffers to bound heap usage.

IDE & Rust-Analyzer Quirks

  • Boolean NOT Operator (E0600): Avoid using the unary ! operator on complex boolean expressions inside closures (e.g., !(a == b && c == d)). Rewrite these expressions using De Morgan's laws (e.g., a != b || c != d).
  • Option::None Shadowing: If rust-analyzer throws a non_snake_case warning for None during pattern matching (often caused by wildcard imports like use crate::models::*;), explicitly namespace as std::option::Option::None.
  • Deep Cloning across Thread Boundaries: Construct target primitive payloads or target structs on the main thread before moving into tokio::task::spawn_blocking closures to avoid cloning massive structs across thread boundaries.

Windows MSVC & Test Concurrency

  • ONNX Runtime / Fastembed Concurrency Resilience: ONNX Runtime (ort.dll via fastembed) model initialization is strictly managed via a thread-safe singleton (OnceLock<Mutex<TextEmbedding>>) behind a process-wide INIT_MUTEX. The historical 0xc0000374 STATUS_HEAP_CORRUPTION crash under uncoordinated C-ABI initializations is fully resolved. Full parallel test execution (cargo test --workspace or cargo nextest run --workspace) across all CPU cores without --test-threads=1 is safe, recommended, and standard across all platforms.