Skip to content

[api] Client performance optimizations - #64823

Open
Andrew Branch (andrewbranch) wants to merge 6 commits into
microsoft:mainfrom
andrewbranch:api/perf-experiments
Open

Andrew Branch (andrewbranch) wants to merge 6 commits into
microsoft:mainfrom
andrewbranch:api/perf-experiments

Conversation

@andrewbranch

Copy link
Copy Markdown
Member

Five independent changes, best viewed one commit at a time.

This reduces wall clock time for the recommended ruleset over 1024 files in https://github.com/typescript-eslint/performance by 4.9% on my machine, with 61% fewer allocations from the client. Retained heap falls by 1.4%. Most of the allocation reduction is from 47fddf5.

The batched getSymbolAtLocation benchmark from our own suite is also 28% faster in the async client and 22% faster in the sync client.

createChildren allocated a fresh scanner for every node whose children were
materialized, and the token lookups (getTokenAtPosition, findNextToken,
findPrecedingToken) allocated one per query. Keep a per-source-file pool of
idle scanners (WeakMap<SourceFile, Scanner[]>): check out by popping or
creating, reset text/language variant on checkout, and in a finally block
clear comment directives and token state before returning the scanner.
Scanners are removed from the pool while in use, so nested or reentrant
traversals always get distinct scanners.

Add tests covering reuse across cached/repeated calls and token lookups,
nested child creation, exceptions during child creation, trivia/comment
directives/JSDoc state, exact token boundaries, and independent source files
with different language variants.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit acac55e)
encoding/json/v2 only omits empty JSON values for omitempty, so every
TypeResponse carried "objectFlags":0, "isTupleType":false, "value":null and
"isThisType":false, every SymbolResponse "checkFlags":0, and every
SignatureResponse "flags":0. Use omitzero and default the fields on the
client.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 4551280)
Give projects a server-generated numeric identity preserved across snapshot clones. Resolve request handles directly through a lazily initialized index owned by each snapshot project collection, rather than a process-wide alias-to-path table.

Keep public logical project IDs unchanged. Use typed numeric handles in request parameters, symbol owners, and project-scoped registries; regenerate both API clients and remove the custom JSON alias decoder.

Cover snapshot isolation, cloning, removal and recreation, concurrent lookup, and omission of project identity from file-owned symbol responses.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use the existing source-file node ID and node-table index as node identity.
Client requests send "index.#sourceFileID"; server responses retain kind and
path as "index.kind.#sourceFileID.path".

Resolve request handles through a lazy index owned by the program. Remove
the process-global ID-to-path registry and reject nodes from another AST,
even when the path and text match. Client response handles verify the AST
identity before resolving a node fetched by path.

Cover shared ASTs, stale and separately parsed files, malformed requests,
response metadata, and concurrent source-file identity lookup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Symbols and signatures parsed every declaration handle into a NodeHandle as
soon as they were received, although most are never read. Keep the wire
strings and create the NodeHandles when declarations, valueDeclaration, or
declaration is first accessed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit c5f5be5)
Copilot AI balanced review requested due to automatic review settings October 15, 2026 00:08
@github-project-automation github-project-automation Bot moved this to Not started in PR Backlog Oct 15, 2026
@typescript-automation typescript-automation Bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Oct 15, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Cross-language protocol and AST-identity changes warrant human review and full API validation, which was not run here.

0 open findings

What changed in this PR

Optimizes the TypeScript API clients by reducing allocations and protocol overhead while making project and node identities explicit.

Changes:

  • Introduces numeric project handles and compact, exact-AST node request handles.
  • Lazily creates declaration handles, omits zero-valued response fields, and pools scanners.
  • Adds identity, stale-handle, concurrency, and scanner-reuse tests.
File Description
tsc/​internal/​project/​projecthandle_test.go Tests handle lifetime and concurrent lookup.
tsc/​internal/​project/​projectcollection.go Adds cached project-handle lookup.
tsc/​internal/​project/​project.go Assigns lifetime-stable project handles.
tsc/​internal/​json/​json.go Exposes the JSON options type.
tsc/​internal/​compiler/​program.go Indexes source files by node identity.
tsc/​internal/​api/​session.go Uses project handles and exact-AST node lookup.
tsc/​internal/​api/​session_symbolresponse_test.go Updates symbol ownership serialization tests.
tsc/​internal/​api/​session_requestfilesystem_test.go Uses handles in filesystem tests.
tsc/​internal/​api/​session_module_resolution_test.go Uses handles in resolution tests.
tsc/​internal/​api/​session_createprogram_test.go Uses handles in program tests.
tsc/​internal/​api/​session_completion_test.go Uses handles in completion tests.
tsc/​internal/​api/​session_batch_test.go Updates batched request payloads.
tsc/​internal/​api/​session_apistate_test.go Tests handle stability and snapshot scope.
tsc/​internal/​api/​proto.go Updates handles and omits zero-valued fields.
tsc/​internal/​api/​projecthandle_test.go Tests numeric handles and snapshot isolation.
tsc/​internal/​api/​nodehandle_test.go Tests exact-AST lookup and invalid handles.
packages/​typescript/​test/​sync/​ast.test.ts Tests scanner pooling and state cleanup.
packages/​typescript/​test/​sync/​api.test.ts Tests synchronous exact-AST behavior.
packages/​typescript/​test/​sync/​api-generators.test.ts Updates node-handle equivalence checks.
packages/​typescript/​test/​proto.test.ts Updates numeric ownership fixtures.
packages/​typescript/​test/​path.test.ts Tests the revised node-handle format.
packages/​typescript/​test/​async/​api.test.ts Tests asynchronous exact-AST behavior.
packages/​typescript/​src/​ast/​astnav.ts Pools scanners for AST navigation.
packages/​typescript/​src/​api/​sync/​api.ts Updates generated synchronous client behavior.
packages/​typescript/​src/​api/​proto.ts Uses numeric symbol-owner project handles.
packages/​typescript/​src/​api/​proto.generated.ts Updates generated protocol types.
packages/​typescript/​src/​api/​node/​node.ts Parses exact-AST handles and emits compact requests.
packages/​typescript/​src/​api/​async/​api.ts Updates requests and lazily creates declaration handles.

🧠 Review effort: Balanced


💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Update the circular-extends regression test added on main to pass the project's lifetime handle to the diagnostics request instead of its logical ID. This fixes the merged PR's test-package compilation failure across test and lint jobs.

Validation: npx hereby validate --all; targeted API race tests; noembed API tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
pool.push(scanner);
}

function withScanner<T>(sourceFile: SourceFile, pos: number | undefined, callback: (scanner: Scanner) => T): T {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If you wanted to be extra clever, you could use using

Comment thread tsc/internal/json/json.go
}

type (
Options = json.Options

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this is unused

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

Status: Not started

Development

Successfully merging this pull request may close these issues.

3 participants