Skip to content

[9.4] [Security Solution] Make kbn-openapi-generator producing lazy loaded Zod schemas (#264125)#265139

Merged
maximpn merged 1 commit intoelastic:9.4from
maximpn:backport/9.4/pr-264125
Apr 22, 2026
Merged

[9.4] [Security Solution] Make kbn-openapi-generator producing lazy loaded Zod schemas (#264125)#265139
maximpn merged 1 commit intoelastic:9.4from
maximpn:backport/9.4/pr-264125

Conversation

@maximpn
Copy link
Copy Markdown
Contributor

@maximpn maximpn commented Apr 22, 2026

Backport

This will backport the following commits from main to 9.4:

Questions ?

Please refer to the Backport tool documentation

…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

(cherry picked from commit c9fbbf4)

# Conflicts:
#	packages/kbn-optimizer/limits.yml
#	x-pack/platform/plugins/shared/cases/common/bundled-types.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/live_query/create_live_query.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/live_query/find_live_query.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/live_query/get_live_query_results.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/live_query/live_queries.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/model/schema/common_attributes.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/packs/copy_pack.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/packs/create_pack.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/packs/find_packs.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/packs/packs.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/packs/update_packs.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/saved_query/copy_saved_query.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/saved_query/create_saved_query.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/saved_query/find_saved_query.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/saved_query/saved_query.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/saved_query/update_saved_query.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/scheduled_results/scheduled_results.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/unified_history/get_unified_history.gen.ts
#	x-pack/platform/plugins/shared/osquery/common/api/unified_history/unified_history.gen.ts
#	x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/find_endpoint_list_item/find_endpoint_list_item.gen.ts
#	x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/update_endpoint_list_item/update_endpoint_list_item.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/file_download/file_download.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/file_info/file_info.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/list/list.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/execute/execute.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/get_file/get_file.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/kill_process/kill_process.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/run_script/run_script.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/scan/scan.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/suspend_process/suspend_process.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/upload/upload.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/state/state.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/status/status.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/model/schema/common.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/policy/policy_response.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/endpoint/protection_updates_note/protection_updates_note.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/timeline/copy_timeline/copy_timeline_route.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/timeline/delete_timelines/delete_timelines_route.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/timeline/get_draft_timelines/get_draft_timelines_route.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/timeline/get_timeline/get_timeline_route.gen.ts
#	x-pack/solutions/security/plugins/security_solution/common/api/timeline/get_timelines/get_timelines_route.gen.ts
@maximpn maximpn added the backport This PR is a backport of another PR label Apr 22, 2026
@maximpn maximpn requested a review from kibanamachine as a code owner April 22, 2026 19:44
@maximpn maximpn enabled auto-merge (squash) April 22, 2026 19:44
@maximpn maximpn merged commit 7b7c697 into elastic:9.4 Apr 22, 2026
20 checks passed
@maximpn maximpn deleted the backport/9.4/pr-264125 branch April 22, 2026 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport This PR is a backport of another PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants