P3: `FSharpProjectOptionsReactor` processes requests strictly FIFO — no priority for the active document
- Lingua principale
- F#
- Stelle
- 4.3k
- Fork
- 876
- Merge medio
- 5g 9h
- PR unite (30g)
- 153
Descrizione
## Summary
`FSharpProjectOptionsReactor` (`vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs`) serializes all project-options requests through a single `MailboxProcessor` and processes them with a plain FIFO loop:
```fsharp
let loop (agent: MailboxProcessor) =
async {
while true do
match! agent.Receive() with
| FSharpProjectOptionsMessage.TryGetOptionsByDocument(document, reply, ct, userOpName) -> ...
| FSharpProjectOptionsMessage.TryGetOptionsByProject(project, reply, ct) -> ...
| FSharpProjectOptionsMessage.ClearOptions(projectId) -> ...
...
}
let reactor = new FSharpProjectOptionsReactor(checker)
```
Every consumer of project options — the active editor tab computing diagnostics/classification/completion, and background services (solution crawler passes, Find All References, unused-opens/unused-declarations analyzers, etc.) — posts into the same mailbox and is served in strict arrival order.
## Problem
When background work enqueues a burst of `TryGetOptionsByDocument` / `TryGetOptionsByProject` messages (e.g. a crawler pass over many documents, or `Find All References` across a large project), a request coming from the **active document** (the one the user is currently typing in) can be queued behind dozens of background requests. Since each request may trigger a real F# Compiler Service computation (`tryComputeOptions`, `tryComputeOptionsBySingleScriptOrFile`), this can noticeably delay diagnostics/IntelliSense responsiveness for the file the user is actively looking at, even though the reactor itself isn't overloaded in absolute terms — it's simply working through older, lower-priority requests first.
This mirrors the general theme of the background-activity-minimization effort: background work should not be allowed to starve foreground/interactive work.
## Proposed solution
Introduce a **priority queue** in front of (or instead of) the plain FIFO `MailboxProcessor`, so that requests associated with the active document are dequeued ahead of background requests:
1. **Two-tier queue.** Replace the single `MailboxProcessor` receive loop with two channels/queues — a small-capacity "foreground" queue and a "background" queue (e.g. `System.Threading.Channels.Channel<'T>` with `UnboundedChannel` for background and a bounded/unbounded high-priority channel for foreground), or a single `MailboxProcessor` combined with an internal `PriorityQueue` that the loop drains with priority ordering (using `agent.TryScan`/`Scan` is not ideal for this since it re-scans the whole mailbox on every call; a dedicated processing loop backed by `System.Threading.Channels` is a cleaner fit for prioritized draining).
2. **Priority classification at post time.** When a message is posted (`TryGetOptionsByDocument`, `TryGetOptionsByProject`), classify it using the existing `ActiveDocumentDetection` helper (see #9-related work in `vsintegration/src/FSharp.Editor/Diagnostics/ActiveDocumentDetection.fs`) — if the request's document/project matches the active document, enqueue into the foreground queue; otherwise the background queue.
3. **Draining order.** The processing loop should always prefer to drain the foreground queue when it is non-empty, falling back to the background queue only when the foreground queue is empty, so active-document requests are never blocked behind an arbitrarily long backlog of background requests. To avoid starving background work entirely under sustained foreground activity, consider a simple weighted/round-robin fallback (e.g., service at most N foreground messages before checking background once) if needed in practice.
4. **Cancellation-awareness.** Preserve existing behavior where messages already carrying a canceled `CancellationToken` are replied to immediately with `ValueNone` without doing any work, for both queues.
5. **No change to computation semantics.** `tryComputeOptions`/`tryComputeOptionsBySingleScriptOrFile` and the existing caches (`cache`, `lastSuccessfulCompilations`, `emitCache`) are unaffected — this is purely a scheduling/ordering change on top of the existing reactor, not a change to what gets computed.
## Alternative considered
A lighter-weight alternative would be to keep the single `MailboxProcessor` but call `agent.Scan` at the head of the loop to look for a foreground message first before falling back to `agent.Receive()`. This avoids introducing `System.Threading.Channels` but has less predictable performance characteristics under a large mailbox backlog (each `Scan` call walks the mailbox), and is likely a reasonable first iteration if the full priority queue is judged too invasive for a first pass.
## Impact
Low risk (isolated to `FSharpProjectOptionsReactor`'s message loop), improves perceived editor responsiveness for the active document during heavy background project-options activity (crawler passes, Find All References, etc.), without changing correctness or caching behavior.
## Related
- Continuation of the background-activity-minimization effort tracked in `docs/ide/background-activity-minimization-plan.md`.
- Complements the Find All References parallel-typecheck throttling change (item #10).
Guida per i contributori
Apri la guida per i contributori
Direzione di ricerca
Inizia in vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs, nel ciclo dei messaggi di FSharpProjectOptionsReactor, poi leggi ActiveDocumentDetection.fs e il relativo piano per le attività in background. Traccia come vengono gestiti TryGetOptionsByDocument, TryGetOptionsByProject e l’annullamento. Il lavoro è completo quando le richieste relative al documento attivo hanno la priorità senza modificare il calcolo, la memorizzazione nella cache o il comportamento delle richieste annullate.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- fsharp
- Ambito
- developer-experience, devtools, performance
- Tipo di issue
- Funzionalità
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Stato di attività
- Tranquilla
- Chiarezza
- Abbastanza chiara
- Idoneità per principianti
- 46/100