dotnet / dotnet/fsharp

P3: `FSharpProjectOptionsReactor` processes requests strictly FIFO — no priority for the active document

Aperta
#20,122 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
Needs-Triage
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

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.