[Security Solution] Make kbn-openapi-generator producing lazy loaded Zod schemas#264125
[Security Solution] Make kbn-openapi-generator producing lazy loaded Zod schemas#264125jbudz merged 13 commits intoelastic:mainfrom
Conversation
5133728 to
1061bdc
Compare
Local testing: Does this improve heap memory.Seems like there is a visible improvement by lazy loading in my local Note that idle memory consumption after installing prebuilt rules with kibana-puppetter-scripts is almost 100mb lower than in
Two datasets × three runs. Metric:
Method
Dataset:
|
| Run folder | Peak heap_used (after 30 s) |
Avg heap_used (after 30 s) |
Samples (included / total) |
|---|---|---|---|
main-1400-round1 |
1235.66 MiB | 1121.35 MiB | 555 / 600 |
main-1400-round2 |
1257.13 MiB | 1100.33 MiB | 555 / 600 |
main-1400-round3 |
1242.56 MiB | 1120.50 MiB | 560 / 600 |
Across 3 runs (mean of per-run values): peak ≈ 1245.1 MiB, average ≈ 1114.1 MiB.
Dataset: lazy-loading-1400-round* (current work)
| Run folder | Peak heap_used (after 30 s) |
Avg heap_used (after 30 s) |
Samples (included / total) |
|---|---|---|---|
lazy-loading-1400-round1 |
1219.63 MiB | 1060.34 MiB | 554 / 600 |
lazy-loading-1400-round2 |
1218.85 MiB | 1068.25 MiB | 555 / 600 |
lazy-loading-1400-round3 |
1219.34 MiB | 1070.83 MiB | 492 / 537 |
Across 3 runs (mean of per-run values): peak ≈ 1219.3 MiB, average ≈ 1066.5 MiB.
Which set is more memory efficient?
lazy-loading-1400-round* (current work) is more memory efficient on heap_used after the 30 s cutoff:
- Peak: ~25.8 MiB lower mean peak than
main-1400-round*(~2.1% lower). - Average: ~47.6 MiB lower mean of per-run averages than
main(~4.3% lower).
All six runs show the same ordering: lazy-loading peaks are clustered ~1219 MiB vs main ~1236–1257 MiB; lazy-loading averages are lower in every run.
5847d2c to
b2b20dc
Compare
443e01a to
0b9c23c
Compare
cf5b634 to
70bf6c0
Compare
|
Pinging @elastic/security-detections-response (Team:Detections and Resp) |
e0aebd7 to
4f8db49
Compare
a78fc75 to
e8cdbef
Compare
💔 Build Failed
Failed CI StepsMetrics [docs]
History
cc @maximpn |
rudolf
left a comment
There was a problem hiding this comment.
We should remove the unused readme but can be done in a follow-up PR too
|
Starting backport for target branches: 9.4 https://github.com/elastic/kibana/actions/runs/24797125575 |
💔 All backports failed
Manual backportTo create the backport manually run: Questions ?Please refer to the Backport tool documentation |
💚 All backports created successfully
Note: Successful backport PRs will be merged automatically after passing CI. Questions ?Please refer to the Backport tool documentation |
…oaded Zod schemas (#264125) (#265139) # Backport This will backport the following commits from `main` to `9.4`: - [[Security Solution] Make kbn-openapi-generator producing lazy loaded Zod schemas (#264125)](#264125) <!--- Backport version: 11.0.1 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Maxim Palenov","email":"maxim.palenov@elastic.co"},"sourceCommit":{"committedDate":"2026-04-22T19:02:58Z","message":"[Security Solution] Make kbn-openapi-generator producing lazy loaded Zod schemas (#264125)\n\n## Summary\n\nReduces the baseline heap footprint of the many generated Zod schemas\n(OpenAPI `*.gen.ts` files) by deferring their construction until first\nuse, and letting the GC reclaim materialized schemas once no consumer is\nholding on to them. Generated schemas are declared at module-load time\nbut only a subset is actually exercised at runtime, so materializing all\nof them eagerly wastes memory — and pinning them for the process\nlifetime wastes memory even for schemas used once.\n\n## Changes\n\n- Add `lazySchema(factory)` in `@kbn/zod/v4` — a Zod-typed wrapper\naround `lazyGCableObject` used by generated schemas. Unused schemas stay\nas a single Proxy + closure; transiently-used schemas are collectible\nafter their last reference is dropped.\n- Update the `zod_operation_schema.handlebars` template in\n`@kbn/openapi-generator` so generated request/response/params/body\nschemas and named component schemas are wrapped in `lazySchema(() =>\n…)`.\n\nCaveat documented on the helper: `instanceof z.ZodType` on a lazy schema\nis `false` because the Proxy target is an empty object. Zod internals\nand typical consumers use structural `_zod` / `.def` checks rather than\n`instanceof`, so this is safe in practice.\n\n## Feature flag\n\nLazy loaded Zod schemas have been smoke tested and there aren't\nnoticeable performance degradations revealed. Anyway\n`disableLazyZodSchemas` feature flag has been added to have an ability\nto disable this optimization. After applying the changes **Kibana\nrestart is required**. To make the optimization disabled add the\nfollowing to the Kibana config\n\n```yaml\nfeature_flags:\n overrides:\n disableLazyZodSchemas: true\n```\n\n### Identify risks\n\n- Consumers that rely on `instanceof z.ZodType` / `instanceof\nz.ZodObject` against a generated schema will see `false`. Mitigation:\nZod and common helpers use structural checks (`_zod`, `.def`) rather\nthan `instanceof`, and this is called out in the helper JSDoc. Reviewers\nshould flag any `instanceof` usage against generated schemas.\n- First-access and rebuild cost: the first property access on a\npreviously unused schema materializes it. Because the cache is a\n`WeakRef`, if the materialized schema is reclaimed between uses (only\npossible once the caller has dropped its reference), the next access\nrebuilds it from the factory — so repeatedly re-materializing the same\nschema across GC cycles adds overhead. Hot paths should retain a\nreference (e.g. `const s = Schema; s.parse(...)`) for the duration of\ntheir work.\n- Large surface change in regenerated `*.gen.ts` files — regeneration is\nmechanical from the updated handlebars template.\n\nPart of https://github.com/elastic/kibana/issues/264170","sha":"c9fbbf4ab88b6ffb4f2fcd00ca1c6ee442e5f827","branchLabelMapping":{"^v9.5.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["performance","release_note:skip","Team:Detections and Resp","Team: SecuritySolution","Team:Detection Rule Management","ci:build-cloud-image","backport:version","v9.4.0","v9.5.0"],"title":"[Security Solution] Make kbn-openapi-generator producing lazy loaded Zod schemas","number":264125,"url":"https://github.com/elastic/kibana/pull/264125","mergeCommit":{"message":"[Security Solution] Make kbn-openapi-generator producing lazy loaded Zod schemas (#264125)\n\n## Summary\n\nReduces the baseline heap footprint of the many generated Zod schemas\n(OpenAPI `*.gen.ts` files) by deferring their construction until first\nuse, and letting the GC reclaim materialized schemas once no consumer is\nholding on to them. Generated schemas are declared at module-load time\nbut only a subset is actually exercised at runtime, so materializing all\nof them eagerly wastes memory — and pinning them for the process\nlifetime wastes memory even for schemas used once.\n\n## Changes\n\n- Add `lazySchema(factory)` in `@kbn/zod/v4` — a Zod-typed wrapper\naround `lazyGCableObject` used by generated schemas. Unused schemas stay\nas a single Proxy + closure; transiently-used schemas are collectible\nafter their last reference is dropped.\n- Update the `zod_operation_schema.handlebars` template in\n`@kbn/openapi-generator` so generated request/response/params/body\nschemas and named component schemas are wrapped in `lazySchema(() =>\n…)`.\n\nCaveat documented on the helper: `instanceof z.ZodType` on a lazy schema\nis `false` because the Proxy target is an empty object. Zod internals\nand typical consumers use structural `_zod` / `.def` checks rather than\n`instanceof`, so this is safe in practice.\n\n## Feature flag\n\nLazy loaded Zod schemas have been smoke tested and there aren't\nnoticeable performance degradations revealed. Anyway\n`disableLazyZodSchemas` feature flag has been added to have an ability\nto disable this optimization. After applying the changes **Kibana\nrestart is required**. To make the optimization disabled add the\nfollowing to the Kibana config\n\n```yaml\nfeature_flags:\n overrides:\n disableLazyZodSchemas: true\n```\n\n### Identify risks\n\n- Consumers that rely on `instanceof z.ZodType` / `instanceof\nz.ZodObject` against a generated schema will see `false`. Mitigation:\nZod and common helpers use structural checks (`_zod`, `.def`) rather\nthan `instanceof`, and this is called out in the helper JSDoc. Reviewers\nshould flag any `instanceof` usage against generated schemas.\n- First-access and rebuild cost: the first property access on a\npreviously unused schema materializes it. Because the cache is a\n`WeakRef`, if the materialized schema is reclaimed between uses (only\npossible once the caller has dropped its reference), the next access\nrebuilds it from the factory — so repeatedly re-materializing the same\nschema across GC cycles adds overhead. Hot paths should retain a\nreference (e.g. `const s = Schema; s.parse(...)`) for the duration of\ntheir work.\n- Large surface change in regenerated `*.gen.ts` files — regeneration is\nmechanical from the updated handlebars template.\n\nPart of https://github.com/elastic/kibana/issues/264170","sha":"c9fbbf4ab88b6ffb4f2fcd00ca1c6ee442e5f827"}},"sourceBranch":"main","suggestedTargetBranches":["9.4"],"targetPullRequestStates":[{"branch":"9.4","label":"v9.4.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.5.0","branchLabelMappingKey":"^v9.5.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/264125","number":264125,"mergeCommit":{"message":"[Security Solution] Make kbn-openapi-generator producing lazy loaded Zod schemas (#264125)\n\n## Summary\n\nReduces the baseline heap footprint of the many generated Zod schemas\n(OpenAPI `*.gen.ts` files) by deferring their construction until first\nuse, and letting the GC reclaim materialized schemas once no consumer is\nholding on to them. Generated schemas are declared at module-load time\nbut only a subset is actually exercised at runtime, so materializing all\nof them eagerly wastes memory — and pinning them for the process\nlifetime wastes memory even for schemas used once.\n\n## Changes\n\n- Add `lazySchema(factory)` in `@kbn/zod/v4` — a Zod-typed wrapper\naround `lazyGCableObject` used by generated schemas. Unused schemas stay\nas a single Proxy + closure; transiently-used schemas are collectible\nafter their last reference is dropped.\n- Update the `zod_operation_schema.handlebars` template in\n`@kbn/openapi-generator` so generated request/response/params/body\nschemas and named component schemas are wrapped in `lazySchema(() =>\n…)`.\n\nCaveat documented on the helper: `instanceof z.ZodType` on a lazy schema\nis `false` because the Proxy target is an empty object. Zod internals\nand typical consumers use structural `_zod` / `.def` checks rather than\n`instanceof`, so this is safe in practice.\n\n## Feature flag\n\nLazy loaded Zod schemas have been smoke tested and there aren't\nnoticeable performance degradations revealed. Anyway\n`disableLazyZodSchemas` feature flag has been added to have an ability\nto disable this optimization. After applying the changes **Kibana\nrestart is required**. To make the optimization disabled add the\nfollowing to the Kibana config\n\n```yaml\nfeature_flags:\n overrides:\n disableLazyZodSchemas: true\n```\n\n### Identify risks\n\n- Consumers that rely on `instanceof z.ZodType` / `instanceof\nz.ZodObject` against a generated schema will see `false`. Mitigation:\nZod and common helpers use structural checks (`_zod`, `.def`) rather\nthan `instanceof`, and this is called out in the helper JSDoc. Reviewers\nshould flag any `instanceof` usage against generated schemas.\n- First-access and rebuild cost: the first property access on a\npreviously unused schema materializes it. Because the cache is a\n`WeakRef`, if the materialized schema is reclaimed between uses (only\npossible once the caller has dropped its reference), the next access\nrebuilds it from the factory — so repeatedly re-materializing the same\nschema across GC cycles adds overhead. Hot paths should retain a\nreference (e.g. `const s = Schema; s.parse(...)`) for the duration of\ntheir work.\n- Large surface change in regenerated `*.gen.ts` files — regeneration is\nmechanical from the updated handlebars template.\n\nPart of https://github.com/elastic/kibana/issues/264170","sha":"c9fbbf4ab88b6ffb4f2fcd00ca1c6ee442e5f827"}}]}] BACKPORT-->
rylnd
left a comment
There was a problem hiding this comment.
Looks like this was force-merged for expedience. I was mid-review, so: here are the outstanding comments for if/when we do some followup.
| expect(Outer.parse({}).inner).toBeUndefined(); | ||
| expect(Outer.parse({ inner: { value: 1 } }).inner).toEqual({ value: 1 }); | ||
| }); | ||
|
|
There was a problem hiding this comment.
I noticed that we don't have coverage for .merge() (which is used by allOf), nor for the actual materialization mechanism for lazySchema that would catch regressions to its implementation. Here are some examples of what I'm thinking:
| it('forwards .merge() on the underlying object schema', () => { | |
| const Base = lazySchema(() => z.object({ id: z.string() })); | |
| const Merged = Base.merge(z.object({ name: z.string() })); | |
| expect(Merged.parse({ id: 'a', name: 'b' })).toEqual({ id: 'a', name: 'b' }); | |
| expect(Merged.safeParse({ id: 'a' }).success).toBe(false); | |
| }); | |
| describe('materialization', () => { | |
| it('does not invoke factories for unused schemas', () => { | |
| const factories = Array.from({ length: 20 }, (_, i) => | |
| jest.fn(() => z.object({ id: z.literal(i) })) | |
| ); | |
| const schemas = factories.map((f) => lazySchema(f)); | |
| for (const f of factories) { | |
| expect(f).not.toHaveBeenCalled(); | |
| } | |
| schemas[3].parse({ id: 3 }); | |
| schemas[7].parse({ id: 7 }); | |
| expect(factories[3]).toHaveBeenCalledTimes(1); | |
| expect(factories[7]).toHaveBeenCalledTimes(1); | |
| const untouched = factories.filter((_, i) => i !== 3 && i !== 7); | |
| for (const f of untouched) { | |
| expect(f).not.toHaveBeenCalled(); | |
| } | |
| }); | |
| it('does not materialize a lazy schema used only as a nested reference until the outer schema parses', () => { | |
| const innerFactory = jest.fn(() => z.object({ value: z.number() })); | |
| const Inner = lazySchema(innerFactory); | |
| const Outer = lazySchema(() => z.object({ inner: Inner, tag: z.string() })); | |
| expect(innerFactory).not.toHaveBeenCalled(); | |
| Outer.parse({ inner: { value: 1 }, tag: 't' }); | |
| expect(innerFactory).toHaveBeenCalledTimes(1); | |
| }); | |
| it('does not materialize a lazy schema used in .merge() until the merged schema parses', () => { | |
| const baseFactory = jest.fn(() => z.object({ id: z.string() })); | |
| const Base = lazySchema(baseFactory); | |
| const Merged = lazySchema(() => Base.merge(z.object({ name: z.string() }))); | |
| expect(baseFactory).not.toHaveBeenCalled(); | |
| Merged.parse({ id: 'a', name: 'b' }); | |
| expect(baseFactory).toHaveBeenCalledTimes(1); | |
| }); | |
| }); |
| searchSynonyms: 6371 | ||
| security: 79627 | ||
| securitySolution: 157027 | ||
| securitySolution: 172947 |
There was a problem hiding this comment.
This new limit far exceeds the current count as per the latest build, which is 153.5KB. Should we choose a more conservative value here, or perhaps even keep the current limit as the change doesn't seem needed?
…Zod schemas (elastic#264125) ## Summary Reduces the baseline heap footprint of the many generated Zod schemas (OpenAPI `*.gen.ts` files) by deferring their construction until first use, and letting the GC reclaim materialized schemas once no consumer is holding on to them. Generated schemas are declared at module-load time but only a subset is actually exercised at runtime, so materializing all of them eagerly wastes memory — and pinning them for the process lifetime wastes memory even for schemas used once. ## Changes - Add `lazySchema(factory)` in `@kbn/zod/v4` — a Zod-typed wrapper around `lazyGCableObject` used by generated schemas. Unused schemas stay as a single Proxy + closure; transiently-used schemas are collectible after their last reference is dropped. - Update the `zod_operation_schema.handlebars` template in `@kbn/openapi-generator` so generated request/response/params/body schemas and named component schemas are wrapped in `lazySchema(() => …)`. Caveat documented on the helper: `instanceof z.ZodType` on a lazy schema is `false` because the Proxy target is an empty object. Zod internals and typical consumers use structural `_zod` / `.def` checks rather than `instanceof`, so this is safe in practice. ## Feature flag Lazy loaded Zod schemas have been smoke tested and there aren't noticeable performance degradations revealed. Anyway `disableLazyZodSchemas` feature flag has been added to have an ability to disable this optimization. After applying the changes **Kibana restart is required**. To make the optimization disabled add the following to the Kibana config ```yaml feature_flags: overrides: disableLazyZodSchemas: true ``` ### Identify risks - Consumers that rely on `instanceof z.ZodType` / `instanceof z.ZodObject` against a generated schema will see `false`. Mitigation: Zod and common helpers use structural checks (`_zod`, `.def`) rather than `instanceof`, and this is called out in the helper JSDoc. Reviewers should flag any `instanceof` usage against generated schemas. - First-access and rebuild cost: the first property access on a previously unused schema materializes it. Because the cache is a `WeakRef`, if the materialized schema is reclaimed between uses (only possible once the caller has dropped its reference), the next access rebuilds it from the factory — so repeatedly re-materializing the same schema across GC cycles adds overhead. Hot paths should retain a reference (e.g. `const s = Schema; s.parse(...)`) for the duration of their work. - Large surface change in regenerated `*.gen.ts` files — regeneration is mechanical from the updated handlebars template. Part of elastic#264170
…Zod schemas (elastic#264125) ## Summary Reduces the baseline heap footprint of the many generated Zod schemas (OpenAPI `*.gen.ts` files) by deferring their construction until first use, and letting the GC reclaim materialized schemas once no consumer is holding on to them. Generated schemas are declared at module-load time but only a subset is actually exercised at runtime, so materializing all of them eagerly wastes memory — and pinning them for the process lifetime wastes memory even for schemas used once. ## Changes - Add `lazySchema(factory)` in `@kbn/zod/v4` — a Zod-typed wrapper around `lazyGCableObject` used by generated schemas. Unused schemas stay as a single Proxy + closure; transiently-used schemas are collectible after their last reference is dropped. - Update the `zod_operation_schema.handlebars` template in `@kbn/openapi-generator` so generated request/response/params/body schemas and named component schemas are wrapped in `lazySchema(() => …)`. Caveat documented on the helper: `instanceof z.ZodType` on a lazy schema is `false` because the Proxy target is an empty object. Zod internals and typical consumers use structural `_zod` / `.def` checks rather than `instanceof`, so this is safe in practice. ## Feature flag Lazy loaded Zod schemas have been smoke tested and there aren't noticeable performance degradations revealed. Anyway `disableLazyZodSchemas` feature flag has been added to have an ability to disable this optimization. After applying the changes **Kibana restart is required**. To make the optimization disabled add the following to the Kibana config ```yaml feature_flags: overrides: disableLazyZodSchemas: true ``` ### Identify risks - Consumers that rely on `instanceof z.ZodType` / `instanceof z.ZodObject` against a generated schema will see `false`. Mitigation: Zod and common helpers use structural checks (`_zod`, `.def`) rather than `instanceof`, and this is called out in the helper JSDoc. Reviewers should flag any `instanceof` usage against generated schemas. - First-access and rebuild cost: the first property access on a previously unused schema materializes it. Because the cache is a `WeakRef`, if the materialized schema is reclaimed between uses (only possible once the caller has dropped its reference), the next access rebuilds it from the factory — so repeatedly re-materializing the same schema across GC cycles adds overhead. Hot paths should retain a reference (e.g. `const s = Schema; s.parse(...)`) for the duration of their work. - Large surface change in regenerated `*.gen.ts` files — regeneration is mechanical from the updated handlebars template. Part of elastic#264170
…Zod schemas (elastic#264125) ## Summary Reduces the baseline heap footprint of the many generated Zod schemas (OpenAPI `*.gen.ts` files) by deferring their construction until first use, and letting the GC reclaim materialized schemas once no consumer is holding on to them. Generated schemas are declared at module-load time but only a subset is actually exercised at runtime, so materializing all of them eagerly wastes memory — and pinning them for the process lifetime wastes memory even for schemas used once. ## Changes - Add `lazySchema(factory)` in `@kbn/zod/v4` — a Zod-typed wrapper around `lazyGCableObject` used by generated schemas. Unused schemas stay as a single Proxy + closure; transiently-used schemas are collectible after their last reference is dropped. - Update the `zod_operation_schema.handlebars` template in `@kbn/openapi-generator` so generated request/response/params/body schemas and named component schemas are wrapped in `lazySchema(() => …)`. Caveat documented on the helper: `instanceof z.ZodType` on a lazy schema is `false` because the Proxy target is an empty object. Zod internals and typical consumers use structural `_zod` / `.def` checks rather than `instanceof`, so this is safe in practice. ## Feature flag Lazy loaded Zod schemas have been smoke tested and there aren't noticeable performance degradations revealed. Anyway `disableLazyZodSchemas` feature flag has been added to have an ability to disable this optimization. After applying the changes **Kibana restart is required**. To make the optimization disabled add the following to the Kibana config ```yaml feature_flags: overrides: disableLazyZodSchemas: true ``` ### Identify risks - Consumers that rely on `instanceof z.ZodType` / `instanceof z.ZodObject` against a generated schema will see `false`. Mitigation: Zod and common helpers use structural checks (`_zod`, `.def`) rather than `instanceof`, and this is called out in the helper JSDoc. Reviewers should flag any `instanceof` usage against generated schemas. - First-access and rebuild cost: the first property access on a previously unused schema materializes it. Because the cache is a `WeakRef`, if the materialized schema is reclaimed between uses (only possible once the caller has dropped its reference), the next access rebuilds it from the factory — so repeatedly re-materializing the same schema across GC cycles adds overhead. Hot paths should retain a reference (e.g. `const s = Schema; s.parse(...)`) for the duration of their work. - Large surface change in regenerated `*.gen.ts` files — regeneration is mechanical from the updated handlebars template. Part of elastic#264170
…types (#267326) ### Summary Cleanup pass on `@kbn/evals-common` and the `evals` plugin, mirroring what was done for `@kbn/inbox-common` in #265634. No functional changes for end users, but the OAS docs generated from these routes are now correct, and the in-memory cost of the Zod schemas drops via the upstream lazy-bind optimization. ### Changes - **Switch to canonical `buildRouteValidationWithZod` from `@kbn/zod-helpers/v4`** - Deleted the local copy in `kbn-evals-common/impl/schemas/common.ts` and its re-export from `kbn-evals-common/index.ts`. - Updated all 18 evals route files to import the helper from `@kbn/zod-helpers/v4`. - Pruned `@kbn/zod-helpers` and `@kbn/core` from `kbn-evals-common`'s `moon.yml` / `tsconfig.json`; added `@kbn/zod-helpers` to the `evals` plugin's instead. - The canonical helper attaches `_sourceSchema` to the returned validator (added in #263354), which `kbn-router-to-openapispec` unwraps so route `params` / `query` / `body` actually appear in generated OAS docs. The local copy did not, so any OAS doc generation for these routes was silently dropping schema info. - **Regenerated OAS types** (`yarn openapi:generate` in `kbn-evals-common`) - Picks up the `lazySchema(() => …)` wrappers introduced by #264125 and tuned in #266343 (upstream `colinhacks/zod#5897` lazy-bind memory optimization). 19 `.gen.ts` files updated; pattern matches the prior inbox regen exactly. - **README cleanup** — pointed at the new helper location and dropped the obsolete "after regenerating, you may need to fix unused imports" workaround (no longer needed thanks to the OAS-generator `fix_eslint.ts` fix from #265634, which forces the non-editor branch when invoked from agent/IDE terminals). _PR developed with Cursor + Claude Opus 4.7 Super Duper xHigh Thinking++_

Summary
Reduces the baseline heap footprint of the many generated Zod schemas (OpenAPI
*.gen.tsfiles) by deferring their construction until first use, and letting the GC reclaim materialized schemas once no consumer is holding on to them. Generated schemas are declared at module-load time but only a subset is actually exercised at runtime, so materializing all of them eagerly wastes memory — and pinning them for the process lifetime wastes memory even for schemas used once.Changes
lazySchema(factory)in@kbn/zod/v4— a Zod-typed wrapper aroundlazyGCableObjectused by generated schemas. Unused schemas stay as a single Proxy + closure; transiently-used schemas are collectible after their last reference is dropped.zod_operation_schema.handlebarstemplate in@kbn/openapi-generatorso generated request/response/params/body schemas and named component schemas are wrapped inlazySchema(() => …).Caveat documented on the helper:
instanceof z.ZodTypeon a lazy schema isfalsebecause the Proxy target is an empty object. Zod internals and typical consumers use structural_zod/.defchecks rather thaninstanceof, so this is safe in practice.Feature flag
Lazy loaded Zod schemas have been smoke tested and there aren't noticeable performance degradations revealed. Anyway
disableLazyZodSchemasfeature flag has been added to have an ability to disable this optimization. After applying the changes Kibana restart is required. To make the optimization disabled add the following to the Kibana configIdentify risks
instanceof z.ZodType/instanceof z.ZodObjectagainst a generated schema will seefalse. Mitigation: Zod and common helpers use structural checks (_zod,.def) rather thaninstanceof, and this is called out in the helper JSDoc. Reviewers should flag anyinstanceofusage against generated schemas.WeakRef, if the materialized schema is reclaimed between uses (only possible once the caller has dropped its reference), the next access rebuilds it from the factory — so repeatedly re-materializing the same schema across GC cycles adds overhead. Hot paths should retain a reference (e.g.const s = Schema; s.parse(...)) for the duration of their work.*.gen.tsfiles — regeneration is mechanical from the updated handlebars template.Part of #264170