Skip to content

workerd schema preload adds about 10 MiB cold heap in client + server bundle #2865

Open

Description

@zackbart

Observation

The workerd shims in both @modelcontextprotocol/client@2.0.0 and @modelcontextprotocol/server@2.0.0 call preloadSchemas() at module scope. In a Connecta Worker-example bundle, removing only those two calls for measurement reduced cold used V8 heap by about 10.03 MiB on average across three fresh local workerd isolates per variant.

Pair Original used heap Without the two preload calls
1 23,184,168 B 12,485,064 B
2 22,998,196 B 12,478,996 B
3 22,846,584 B 12,503,924 B

Environment: Node 26.9.0, Miniflare 4.20260722.0, workerd 1.20260722.1, compatibility date 2025-01-01, nodejs_compat, both SDK packages 2.0.0.

Reproduction and limits

Measurement script, raw observations, and method and interpretation.

The script bundles the Worker example, retains its imported export to prevent tree shaking, answers one synthetic request, then reads Runtime.getHeapUsage from the exact core:user: inspector target. The comparison plugin removes the two shim calls without modifying installed dependencies. Each sample uses a fresh Miniflare instance, disposed afterward. No provider is called or Worker deployed.

This measures cold allocation without forced GC, not retained heap after GC or production isolate churn. HeapProfiler.collectGarbage did not respond within ten seconds in this runtime. We have not verified that removing preload preserves all SDK schema paths on Workers and are not shipping a patch or recommending that workaround.

Could the workerd entry avoid eagerly materializing unused schemas while preserving its no-runtime-code-generation requirements? An upstream supported approach would let consumers benefit without modifying their dependencies.

Activity

  1. added
    v2Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes
    on Sep 25, 2026
  2. furuchanchan commented on Sep 29, 2026

    @furuchanchan

    Independent reproduction, plus the cost on the other side of the trade-off.

    Setup: Node 26.9.0, Miniflare 4.20260722.0, workerd 1.20260722.1, nodejs_compat, SDK built from main. Five fresh Miniflare instances per variant. Runtime.getHeapUsage is read after one request. "strip" removes only the module-scope preloadSchemas() call from the bundled output.

    Bundle as-is, used heap strip, used heap first preloadSchemas() call in strip
    empty Worker 0.74 MB – –
    server 17.0 MB 8.4 MB 12–16 ms
    client 17.7 MB 8.9 MB 11–14 ms
    server + client 29.6 MB 18.8 MB 11–14 ms

    The heap delta reproduces: about 8.6 to 8.8 MB per package here, and about 10.8 MB with both.

    Context: the eager call is deliberate. #2483 moved construction back to module scope on workerd, because the lazy construction from #2476 landed on the first billed request of every fresh isolate. The milliseconds above are wall-clock time for the deferred construction in the same isolates, so they are roughly what laziness would put back on the first request. They are not CPU time.

    Given that, I see three directions:

    1. Keep the eager call and document the heap cost.
    2. Add an opt-out for memory-constrained Workers.
    3. Build only the era that is actually negotiated.

    I can prototype whichever direction maintainers prefer.

  3. prasad-kavuri commented on Oct 18, 2026

    @prasad-kavuri

    Hi maintainers — I’m interested in contributing to this issue. I have experience with production agent platforms and serverless/edge deployments, and I’d like to investigate a narrowly scoped approach for avoiding unnecessary eager schema materialization in the workerd entry points while preserving the no-runtime-code-generation constraint.

    Before I start coding, could you confirm which direction you would prefer for this issue: an opt-out for memory-constrained Workers, lazy construction after negotiation, or a focused measurement/documentation change? I’ll keep the work local until there is alignment and will add targeted regression/benchmark coverage.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    v2Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions