Skip to main content

Why Warmup is Needed

After spawning, LSP backends (pyright, ty, pyrefly) need time to build their cross-file index. Requests sent during this warmup period return incomplete results:

During Warmup (Incomplete)

  • findReferences returns same-file only (missing cross-project references)
  • goToDefinition on re-exported symbols returns empty
  • Type errors not detected if symbol defined in another file

After Warmup (Complete)

  • findReferences returns all references across the project
  • goToDefinition follows imports and re-exports correctly
  • Full type checking with cross-file dependencies

Real-World Example

Without warmup queuing:
  1. User opens auth.py → backend spawned (warming)
  2. User immediately clicks “Find References” on get_name
  3. Backend hasn’t indexed user.py yet
  4. Result: Only shows reference in auth.py (incorrect!)
With warmup queuing:
  1. User opens auth.py → backend spawned (warming)
  2. User clicks “Find References” on get_namequeued
  3. Backend finishes indexing (2s later) → transitions to Ready
  4. Queued request forwarded to backend
  5. Result: Shows references in both auth.py and user.py (correct!)

Warming vs Ready States

Each backend instance tracks its warmup state in the pool.

State Definitions

Code reference: src/backend_pool.rs:13-21

State Transition Diagram

Transition Triggers

1

Spawn (→ Warming)

New backend starts in Warming state (unless timeout is 0).Code reference: src/proxy/client_dispatch.rs:55-61
2

Progress Signal (→ Ready)

Backend sends $/progress notification with kind: "end".Code reference: src/proxy/backend_dispatch.rs:110-134
Progress detection:
3

Timeout Expiry (→ Ready, Fail-Open)

If progress signal never arrives, fail-open after timeout.Code reference: src/proxy/pool_management.rs:264-302
Why fail-open? If backend never signals readiness (bug, slow machine, etc.), better to forward requests (potentially incomplete results) than block forever.

Which Requests are Queued

Only index-dependent requests are queued during warmup.

Queued Methods (Index-Dependent)

Code reference: src/proxy/client_dispatch.rs:11-17

Forwarded Immediately (Not Queued)

Queueing Logic

Code reference: src/proxy/client_dispatch.rs:337-359

Timeout Behavior

Default Timeout

2 seconds (configurable via TYPEMUX_CC_WARMUP_TIMEOUT). Code reference: src/backend_pool.rs:24-34

Timeout Event Loop Arm

The main event loop has a dedicated arm for warmup expiry: Code reference: src/proxy/mod.rs (conceptual, see actual source)
Dynamic deadline: The warmup timer uses nearest_warmup_deadline() to sleep until the soonest backend expires. This avoids polling and ensures immediate transition when timeout occurs.

Timeout Scenarios

Outcome: Progress signal wins, timeout never fires.

Configuration

Environment Variable

Configuration Examples

Config File Setup

For persistent configuration:

When to Disable Warmup

Disabling warmup (TYPEMUX_CC_WARMUP_TIMEOUT=0) means index-dependent requests may return incomplete results immediately after backend spawn. Only disable if you understand the trade-off.
Use cases for disabling:
  • Testing/debugging: Simpler behavior, no queuing logic
  • Pre-indexed backends: If you know the backend re-uses a persistent index (some LSP servers cache indexes on disk)
  • Single-file workflows: If you never use cross-file features (references, implementations)

Queue Behavior Details

Queue Structure

Each backend maintains a FIFO queue of pending requests. Code reference: src/backend_pool.rs:54

Draining the Queue

When transitioning to Ready, queued requests are drained in order and forwarded to the backend. Code reference: src/backend_pool.rs:72-83
Draining loop: src/proxy/client_dispatch.rs:491-555

Queue Cancellation

If the user cancels a queued request via $/cancelRequest: Code reference: src/proxy/client_dispatch.rs:463-487
Behavior:
  • If request is in warmup queue → remove from queue (never forwarded to backend)
  • If request already forwarded → forward cancel to backend

Session Guard During Drain

If a backend crashes while draining and is replaced with a new session:
This prevents forwarding old queued requests to a new backend instance.

Monitoring Warmup Activity

Enable Debug Logging

Useful Log Queries

Real-Time Monitoring

Performance Impact

Overhead

  • Latency: +2s max for index-dependent requests (only once per backend spawn)
  • Memory: ~1 KB per queued request (typically 0-5 requests)
  • CPU: Negligible (queue is a simple Vec)

Benefits

  • Correctness: Ensures complete results for cross-file queries
  • UX: Prevents confusing partial results (“why didn’t it find this reference?”)
  • Reliability: Fail-open means no indefinite blocking
In practice, most backends signal readiness via $/progress within 500ms-1.5s. The 2s timeout is a safety net, not a common occurrence.