Skip to content

Make LS checkers time out after being idle, segment based on use#3363

Merged
jakebailey merged 12 commits intomainfrom
jabaile/redo-checker-pool
Apr 16, 2026
Merged

Make LS checkers time out after being idle, segment based on use#3363
jakebailey merged 12 commits intomainfrom
jabaile/redo-checker-pool

Conversation

@jakebailey
Copy link
Copy Markdown
Member

@jakebailey jakebailey commented Apr 8, 2026

Fixes #2115

This is a redo of the project CheckerPool.

  • There are now three categories of checkers: "diagnostic", "temporary", and "API". For a given program, there are N+1 checkers, one of which is for diagnostics, one for the API, the rest are for temporary queries. It still defaults to 4 (so, one diagnostic checker, 3 query checkers, one API checker), but is now configurable directly on SessionOptions. That'll mean tsgolint doesn't have to patch us, side benefit.
  • Checkers (including the diagnostic-providing ones) are now entirely ephemeral. They'll be reused, but after they sit idle for 30 seconds (also configurable), they get cleaned up. If a Program hits refcount=0, then we'll throw away all idle checkers and stop the cleanup task, moving the pool into a mode where it instantly cleans up any new checker it creates (keeping old snapshots working, albeit less efficiently).
  • Rather than a condition variable, we use channels to manage things; this is easier to manage for the new code as a more explicit semaphore.
    • My intent is that a future change can make these also context.Context aware and stop waiting for a checker on cancelled requests, it's just a bigger refactor to plumb errors through.
  • The PR was updated after to add an "API" checker, which is persistent and won't get cleaned up. This is just for the API packages, where we need to keep things around for identity reasons.

I'll note that while it's "configurable", I just mean code-wise; I am not actually exposing editor settings. Maybe that's a good idea.

It may be that going down to just one diagnostic checker is too restrictive; perhaps that is part of the speed boost in the new LSP. I could expand this out so we have more than one, potentially. Or make it configurable.


if c.WasCanceled() {
// Canceled checkers must be disposed.
p.log(fmt.Sprintf("checkerpool: Checker %d for request %s was canceled, disposing", index, holdTag(requestID)))
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW we have these logs, but the log func we had passed into the pool is actually a noop. I have not fixed that in this PR.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Refactors the project checker pooling mechanism to split diagnostics vs query usage and add idle-time disposal, with configuration wired through session options.

Changes:

  • Introduces CheckerPoolOptions on SessionOptions and routes diagnostic requests to a dedicated checker via core.CheckerPurpose.
  • Replaces the old CheckerPool implementation with a channel-semaphore-based checkerPool supporting idle cleanup and program/pool discard.
  • Adds extensive tests covering routing, affinity, contention, discard, and cleanup behavior.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
internal/project/snapshot.go Discards a program’s checker pool when the program is no longer referenced by any snapshot.
internal/project/session.go Adds CheckerPoolOptions to session configuration.
internal/project/projectcollectionbuilder.go Discards old pools on program updates and rebinds pool from Program via GetCheckerPool().
internal/project/project.go Switches projects to the new checkerPool and wires in options on program creation.
internal/project/checkerpool.go Major rewrite: diagnostics vs query checkers, per-request/file affinity, idle cleanup timer, discard behavior, channel semaphores.
internal/project/checkerpool_test.go New test suite validating behavior of the redesigned pool.
internal/lsp/server.go Marks document diagnostics requests as CheckerPurposeDiagnostics.
internal/core/context.go Adds checker-purpose context plumbing (WithCheckerPurpose / GetCheckerPurpose).
internal/compiler/program.go Exposes program checker pool via GetCheckerPool().

Comment thread internal/project/checkerpool.go
Comment thread internal/project/checkerpool.go Outdated
Comment thread internal/project/checkerpool.go Outdated
Comment thread internal/project/checkerpool_test.go Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.

Comment thread internal/project/checkerpool.go
Comment thread internal/project/checkerpool_test.go Outdated
Comment thread internal/project/checkerpool_test.go Outdated
Comment thread internal/project/checkerpool.go
Comment thread internal/project/checkerpool_test.go
Comment thread internal/project/checkerpool_test.go
Comment thread internal/lsp/server.go
@andrewbranch
Copy link
Copy Markdown
Member

Since diagnostics requests typically come in big waves (one for each open file on startup or any change), I was skeptical of this approach, but I can't see any negative affect after opening a dozen or so files in the VS Code repo. I think it's fine.

Is any of the Copilot feedback important?

@jakebailey
Copy link
Copy Markdown
Member Author

Yeah, it all seems plausible; I will look at it (was just preoccupied with other stuff and accidentally archived the email with these comments).

@jakebailey
Copy link
Copy Markdown
Member Author

Fixed the comments, but, part of it is that the API changes.

Though I think the approach here is fine, I'm not entirely certain how this should work for API users, or even tsgolint, where they might want more of these checkers?

@jakebailey
Copy link
Copy Markdown
Member Author

@camc314 Do you know how this might affect your usage of these checkers? Is there some sort of setting that would make this not break you, if so?

@camc314
Copy link
Copy Markdown
Contributor

camc314 commented Apr 11, 2026

@jakebailey thanks for the ping

I tried this branch on tsgolint, and it all seemed to work ok - thanks for checking.

@jakebailey jakebailey added this pull request to the merge queue Apr 16, 2026
Merged via the queue into main with commit 18ce0c0 Apr 16, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor checker pool to limit checker creation for foreground tasks, segment diagnostic checkers

4 participants