Skip to content

perf: loading 292 ES API Zod schemas allocates 373 MB of heap #171

@Kiryous

Description

@Kiryous

Summary

Importing all 292 ES API schemas (src/es/apis.ts) allocates ~373 MB of heap memory. The CLI currently runs as a short-lived process so this doesn't cause OOM, but it's a significant footprint that will grow as more APIs are added and could become a problem if the CLI is used as a long-running process (daemon, language server, agent loop).

For context, the Kibana workflows team hit OOM at 800-1000 MB when loading ~1000 API schemas through Zod. At 373 MB for 292 APIs, the CLI is already at ~47% of that threshold.

Environment

  • CLI: 0.1.0-alpha.1 (commit 1b91960)
  • Node: v25.6.1
  • OS: Darwin 25.4.0 arm64

Repro

node --expose-gc -e '
gc();
const before = process.memoryUsage().heapUsed;
import("./dist/es/apis.js").then(mod => {
  gc();
  const after = process.memoryUsage().heapUsed;
  console.log("Heap before:", (before / 1024 / 1024).toFixed(1), "MB");
  console.log("Heap after:", (after / 1024 / 1024).toFixed(1), "MB");
  console.log("Delta:", ((after - before) / 1024 / 1024).toFixed(1), "MB");
  console.log("API count:", mod.allApis.length);
})
'

Result

Heap before: 2.8 MB
Heap after:  376.2 MB
Delta:       373.4 MB
API count:   292

Additionally, generating JSON Schema output (--help --json) for all APIs produces 22 MB total:

Total JSON Schemas generated: 292
Total JSON Schema bytes:      21,948,227

Why this matters

  • The docs chat command (added in feat: add elastic docs commands (search, ask, chat, read) #147) keeps the process alive for interactive sessions — 373 MB baseline for every chat session.
  • Zod v4 was found to worsen memory consumption in Kibana (kibana#262948).
  • Agent/LLM workflows may run many CLI invocations in sequence; forking 373 MB per invocation adds up.
  • Adding more APIs (the ES spec has 400+) will push this higher.

Possible approaches

  • Lazy loading: Only instantiate schemas for the command being invoked, not all 292 on startup.
  • Schema splitting: Move each API's schema into a separate chunk that's imported on demand.
  • Codegen optimization: Investigate whether the schema graph has excessive duplication (shared types like QueryDslQueryContainer may be instantiated multiple times via z.lazy()).

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions