Repository navigation
Conversation
ParseIterator::next recursed once for every statement the filter skipped, so hover, code actions and completions overflowed the 2 MiB stack of the tokio workers on files with a few thousand statements and aborted the server. It is a loop now. Hover and code actions only look at the top-level statement at the cursor and its SQL function body instead of filtering every statement of the file.
…nce for hover The parse and tree-sitter caches of a document held 1,000 statements, so on bigger files every walk over the document parsed every statement again. They now keep the statements of the document and drop the ones that are gone when the content changes. Hover walked the statements before the cursor twice: once to resolve the references, and once more in snapshot_before, which also deep-cloned every AST. It now builds the snapshot from the catalog of its own walk, skips it when the hovered references are all to objects the file didn't change, and caches it by the text before the statement.
psteinroe
force-pushed
the
perf/cache-code-action-lints
branch
from
October 24, 2026 07:32
e5c9257 to
6c8ab2e
Compare
psteinroe
changed the base branch from
main
to
perf/document-statement-caches
October 24, 2026 07:32
Editors request code actions on every cursor move, and each request ran the whole analysis of the file again. The analysis that the diagnostics and the code actions ask for (every rule category, no rule selection) is now kept on the document until its content, the settings or the database snapshot change, and both share it. The LSP handler also filters the actions by the kinds in context.only.
psteinroe
force-pushed
the
perf/cache-code-action-lints
branch
from
October 24, 2026 07:32
6c8ab2e to
c922f66
Compare
This was referenced Oct 24, 2026
psteinroe
commented
Oct 26, 2026
psteinroe
left a comment
Collaborator
Author
There was a problem hiding this comment.
This is too messy
| /// The migration sections that are not linted. | ||
| skipped_sections: Vec<TextRange>, | ||
| } | ||
|
|
Collaborator
Author
There was a problem hiding this comment.
Should be more encapsulated
psteinroe
changed the base branch from
perf/document-statement-caches
to
main
October 26, 2026 11:14
This was referenced Oct 26, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #913, which changes the same parts of
DocumentandWorkspaceServer.Editors request code actions on every cursor move, and each request ran the whole analysis of the file: every lint and typecheck rule, over every statement. Right after an edit, the diagnostics had already run the same analysis. On a file with 25,000 statements that took seconds per cursor move.
What changed
analyse_documentreturns aDocumentAnalysisstruct instead of a tuple. The analysis that the editor asks for (every rule category, noonly/skiprule selection) is kept on theDocument, throughanalyse_document_cached.pull_file_diagnosticsand the code actions use it, so after the diagnostics of an edit the code actions find it ready. Requests with a rule selection, such ascheck --onlyorfix_file(which analyses its own intermediate documents), are not cached.Document::update_content). It is not used when the settings changed, tracked by a version counter that is bumped while the settings are write-locked, or when the database snapshot changed (compared byArcidentity, which also covers a database that becomes reachable).LinterDiagnosticand its advices deriveClone, so thatpull_file_diagnosticscan convert diagnostics out of the shared analysis.RuleCategoriesderivesPartialEqto recognise the "all categories" request.context.only. Kinds are matched hierarchically, soquickfixincludesquickfix.pgls.banDropTable, andsource.fixAlldoesn't get the quick fixes or theExecute Statementcommand. The filter runs on the final list, so actions added later (refactors, fix-all) go through it without changes here.EXPLAIN and plpgsql_check diagnostics are not part of the cached analysis and are not affected.
Timings
Release build on a shared, loaded machine, measured with the probe from the release analysis. The "before" row is #892 alone, which stops code actions from walking the whole file to find the statement at the cursor; on
mainwithout it, that walk alone took about 0.9 s on the 20,000-line file.Tests
code_actions_reuse_the_analysis: the code actions use the analysis that the diagnostics cached (the sameArc), an edit drops it, and turning a rule off in the settings removes its actions.test_lint_fix_code_actions(LSP): withonly: [quickfix]only the fix and the suppression come back, and withonly: [source.fixAll]nothing does.requested_kinds_are_hierarchical: the kind matching, including"", a prefix that isn't a whole segment, and the empty kind of the commands.Risks
workspaces_mut), the database snapshot and the document itself.pull_code_actionsand the LSPget_actionsmapping. This PR changeslint_code_actions' loop and adds a filter at the end ofget_actions, so I expect no overlap beyond context lines. (perf(workspace): keep every statement of a document cached and walk once for hover #913 below renamesreferences_at, which feat: code action to expand * into the column list #887 uses; that conflict is with perf(workspace): keep every statement of a document cached and walk once for hover #913, not this PR.)Follow-ups: The memory of the cached analysis is bounded in #951.