Skip to content

making language service apis parallel safe#2604

Open
aasimkhan30 wants to merge 8 commits intomainfrom
aasim/fix/parallelLanguageService
Open

making language service apis parallel safe#2604
aasimkhan30 wants to merge 8 commits intomainfrom
aasim/fix/parallelLanguageService

Conversation

@aasimkhan30
Copy link
Contributor

@aasimkhan30 aasimkhan30 commented Mar 7, 2026

Description

Addressing: microsoft/vscode-mssql#21361

While the bug is still not identified, this is best effort solution to make sts not stall.

All Language Service request and event handlers are now registered with isParallelProcessingSupported: true, so they no longer serialize on the single-threaded dispatcher queue. Previously, a slow handler (e.g. a completion request waiting on the binding queue) would stall every other dispatcher message behind it.

Changes

1. Parallel dispatch for all handlers

Every SetRequestHandler / SetEventHandler call in InitializeService now passes isParallelProcessingSupported: true. Before this change, a single comment disabled parallelism for all of them:

-            // Not enabling parallel processing for LanguageService as message order might matter.
-            serviceHost.SetRequestHandler(SignatureHelpRequest.Type, HandleSignatureHelpRequest);
+            serviceHost.SetRequestHandler(SignatureHelpRequest.Type, HandleSignatureHelpRequest, isParallelProcessingSupported: true);

2. URI-serialized rebuild and language-flavor notifications

HandleRebuildIntelliSenseNotification and HandleDidChangeLanguageFlavorNotification both mutate the per-URI intellisense cache (ScriptParseInfo, nonMssqlUriMap, diagnostics). To prevent race conditions where two concurrent rebuilds for the same document overwrite each other, both handlers now go through RunSerializedByUriAsync, which holds a per-URI AsyncLock. Different URIs still run fully in parallel.

3. Signature help and hover

HandleSignatureHelpRequest previously used a fire-and-forget Task.Factory.StartNew + ContinueWith pattern to avoid blocking the dispatcher. With parallel dispatch enabled, the handler can simply await the work directly — the dispatcher is already unblocked. HandleHoverRequest had a similar structure and was simplified the same way.

4. Peek definition thread safety

HandleDefinitionRequest scripts objects to temp files. Two changes make this safe under parallel dispatch:

  • FileUtilities.GetPeekDefinitionTempFolder() is now guarded by a lock so the one-time temp folder creation is not raced by concurrent requests.
  • ScripterCore.CreateFileName appends a per-request GUID to every generated filename, so two concurrent peek-definition requests for the same object don't overwrite each other's script files.

5. Syntax parse and token refresh

HandleSyntaxParseRequest is pure computation (parse input text, return errors) with no shared state mutation — trivially safe. HandleTokenRefreshedNotification updates connection/token state backed by concurrent collections.

6. Completion request cancellation

Completion is registered as parallel-capable but uses a cancel-previous pattern rather than true parallelism. On each new CompletionRequest:

  • CancelPreviousCompletionRequest() atomically swaps in a new CancellationTokenSource via Interlocked.Exchange, cancelling and disposing the old one.
  • The token is passed into GetCompletionItems, which checks cancellationToken.IsCancellationRequested after the expensive ParseAndBind step and bails out early if superseded.
  • CompletionResolveRequest is parallel-capable and only operates on the latest completion result cached in currentCompletionParseInfo.

7. Star-expansion completion improvements (AutoCompleteHelper)

Refactored star-expansion (SELECT *) completion items with improved presentation and multiline formatting.

  1. Moved sql start expansion into the binding queue operation that generated completions.

Code Changes Checklist

  • New or updated unit tests added
  • All existing tests pass (dotnet test)
  • Code follows contributing guidelines
  • Logging/telemetry updated if relevant
  • No protocol or behavioral regressions

Reviewers: Please read our reviewer guidelines

Aasim Khan added 2 commits March 6, 2026 22:02
…d Safety

- Updated CreateFileName method in ScripterCore to append a GUID for unique file names.
- Refactored GetPeekDefinitionTempFolder in FileUtils to ensure thread safety when creating temporary folders.
- Added unit tests for completion resolution to ensure dispatcher does not block during binding operations.
- Introduced tests for unique file name generation and thread safety in peek definition folder creation.
- Enhanced existing tests for autocomplete and language service functionalities.
@aasimkhan30 aasimkhan30 changed the title Converting language service apis to make them parallel. making language service apis parallel safe Mar 7, 2026
Copy link

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

This PR aims to prevent SQL Tools Service LanguageService stalls by enabling parallel dispatch for language-service request/event handlers and adding targeted synchronization for shared per-URI and peek-definition state.

Changes:

  • Enable parallel processing for LanguageService request/event handlers (signature help, hover, completion, definition, syntax parse, rebuild intellisense, language flavor change, token refresh).
  • Add per-URI serialization for rebuild-intellisense and language-flavor change to avoid concurrent cache/diagnostics mutations.
  • Make peek-definition temp folder creation and scripted filename generation safe under parallel execution (lock + GUID-based uniqueness) and add unit tests for these behaviors.

Reviewed changes

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

Show a summary per file
File Description
test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/PeekDefinitionTests.cs Adds concurrency/uniqueness unit tests for peek-definition temp folder and script filename generation.
src/Microsoft.SqlTools.ServiceLayer/Utility/FileUtils.cs Makes peek-definition temp folder creation thread-safe and uses GUID suffix for folder name.
src/Microsoft.SqlTools.ServiceLayer/Scripting/ScripterCore.cs Ensures scripted peek-definition filenames are unique per request via GUID suffix.
src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs Enables parallel dispatch, adds per-URI async locking for rebuild/flavor handlers, and introduces completion cancel-previous behavior.
src/Microsoft.SqlTools.ServiceLayer/LanguageServices/Completion/CompletionService.cs Moves star-expansion into completion creation flow and adds verbose logging.
Comments suppressed due to low confidence (1)

src/Microsoft.SqlTools.ServiceLayer/LanguageServices/Completion/CompletionService.cs:181

  • CreateCompletionsFromSqlParser assumes connInfo is non-null (e.g., connInfo.IntellisenseMetrics.UpdateMetrics(...) below) but connInfo is passed through from LanguageService and may be null in some flows. This can lead to NullReferenceException while building completions, causing the request to stall/fail. Either require non-null connInfo at the API boundary or add null-safe handling (skip metrics + star-expansion, return defaults) when it is null.
            // Expanding star expressions in query only when the script is connected to a database
            // as the parser requires a connection to determine column names
            if (connInfo != null)
            {
                CompletionItem[] starExpansionSuggestion = AutoCompleteHelper.ExpandSqlStarExpression(scriptDocumentInfo);
                if (starExpansionSuggestion != null)
                {
                    completionList = [.. starExpansionSuggestion, .. completionList];
                }
            }

            result.CompleteResult(completionList);

            //The bucket for number of milliseconds will take to send back auto complete list
            connInfo.IntellisenseMetrics.UpdateMetrics(result.Duration, 1, (k2, v2) => v2 + 1);
            return result;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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.

2 participants