diff --git a/check_ruff_pattern.md b/check_ruff_pattern.md deleted file mode 100644 index 5253b69..0000000 --- a/check_ruff_pattern.md +++ /dev/null @@ -1,94 +0,0 @@ -# OUTDATED - See ARCHITECTURE_INSIGHTS.md for current solution - -## This document is preserved for historical context but is OUTDATED -## We found the StorageHandle solution that solves the Send+Sync issue - -# Key Findings from Ruff's Architecture - -Based on the exploration, here's what we discovered: - -## Current Django LS Architecture - -### What We Have: -1. `Database` struct with `#[derive(Clone)]` and Salsa storage -2. `WorkspaceDatabase` that wraps `Database` and uses `DashMap` for thread-safe file storage -3. `Session` that owns `WorkspaceDatabase` directly (not wrapped in Arc) -4. Tower-LSP server that requires `Send + Sync` for async handlers - -### The Problem: -- `Database` is not `Sync` due to `RefCell` and `UnsafeCell` in Salsa's `ZalsaLocal` -- This prevents `Session` from being `Sync`, which breaks tower-lsp async handlers - -## Ruff's Solution (From Analysis) - -### They Don't Make Database Sync! -The key insight is that Ruff **doesn't actually make the database Send + Sync**. Instead: - -1. **Clone for Background Work**: They clone the database for each background task -2. **Move Not Share**: The cloned database is *moved* into the task (requires Send, not Sync) -3. **Message Passing**: Results are sent back via channels - -### Critical Difference: -- Ruff uses a custom LSP implementation that doesn't require `Sync` on the session -- Tower-LSP *does* require `Sync` because handlers take `&self` - -## The Real Problem - -Tower-LSP's `LanguageServer` trait requires: -```rust -async fn initialize(&self, ...) -> ... -// ^^^^^ This requires self to be Sync! -``` - -But with Salsa's current implementation, the Database can never be Sync. - -## Solution Options - -### Option 1: Wrap Database in Arc (Current Workaround) -```rust -pub struct Session { - database: Arc>, - // ... -} -``` -Downsides: Lock contention, defeats purpose of Salsa's internal optimization - -### Option 2: Move Database Out of Session -```rust -pub struct Session { - // Don't store database here - file_index: Arc>, - settings: Settings, -} - -// Create database on demand for each request -impl LanguageServer for Server { - async fn some_handler(&self) { - let db = create_database_from_index(&self.session.file_index); - // Use db for this request - } -} -``` - -### Option 3: Use Actor Pattern -```rust -pub struct DatabaseActor { - database: WorkspaceDatabase, - rx: mpsc::Receiver, -} - -pub struct Session { - db_tx: mpsc::Sender, -} -``` - -### Option 4: Custom unsafe Send/Sync implementation -This is risky but possible if we ensure single-threaded access patterns. - -## The Salsa Version Mystery - -We're using the exact same Salsa commit as Ruff, with the same features. The issue is NOT the Salsa version, but how tower-lsp forces us to use it. - -Ruff likely either: -1. Doesn't use tower-lsp (has custom implementation) -2. Or structures their server differently to avoid needing Sync on the database