-
-
Notifications
You must be signed in to change notification settings - Fork 672
feat: Optimize metadata-storage build with HashMap caching for O(1) lookups #1779
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
@MichalLytek Thanks for taking a look at this, tests should be fixed. The root issue was a lack of resetting the new cache state in the global metadata-storage instance, and I also ran into some weirdness of needing to reset the state in |
|
@MichalLytek Bumping this, would love to get it merged 😄 |
|
I promise I will try to find some time to review it 😉 |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1779 +/- ##
==========================================
+ Coverage 95.50% 95.59% +0.09%
==========================================
Files 113 114 +1
Lines 1847 1931 +84
Branches 364 397 +33
==========================================
+ Hits 1764 1846 +82
- Misses 83 85 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Hey @MichalLytek, bumping this again! Let me know if you get a chance to take a look. Thanks in advance. |
|
@MichalLytek Bumping this once more :) |
|
For anyone keeping up with this PR, I've published my fork to NPM under |
|
@benminer The rest looks good, we can merge this PR then 🎉 |
|
@benminer bumping this! 🙌 |
Hey @MichalLytek - great to hear from you! Apologies for the delay, was on holiday break. Doing this now! |
Neither me or owners of our org are seeing the optional presented in the documentation here. Not sure if deprecated, or what. Another option is giving you write access to my forked repository, where you could push changes to the |
|
@MichalLytek I've invited you as a collaborator with write access to the forked repo. Keep in mind, the default branch there is |
MichalLytek
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
|
@benminer Thank you very much for your contribution! ❤️ And sorry it took so long to merge this 😞 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| private static objectTypesInfoMap = new Map<Function, ObjectTypeInfo>(); | ||
|
|
||
| private static inputTypesInfo: InputObjectTypeInfo[] = []; | ||
|
|
||
| private static inputTypesInfoMap = new Map<Function, InputObjectTypeInfo>(); | ||
|
|
||
| private static interfaceTypesInfo: InterfaceTypeInfo[] = []; | ||
|
|
||
| private static interfaceTypesInfoMap = new Map<Function, InterfaceTypeInfo>(); | ||
|
|
||
| private static enumTypesInfo: EnumTypeInfo[] = []; | ||
|
|
||
| private static unionTypesInfo: UnionTypeInfo[] = []; | ||
|
|
||
| private static unionTypesInfoMap = new Map<symbol, UnionTypeInfo>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Static SchemaGenerator cache Maps are never cleared between schema builds, causing memory leak
The new static Maps (objectTypesInfoMap, inputTypesInfoMap, interfaceTypesInfoMap, unionTypesInfoMap) on SchemaGenerator accumulate entries across successive calls to generateFromMetadata and are never reset.
Root Cause and Impact
In generateFromMetadata (src/schema/schema-generator.ts:132-164), at the end of each build, only usedInterfaceTypes is cleared:
BuildContext.reset();
this.usedInterfaceTypes = new Set<Function>();The corresponding arrays (objectTypesInfo, interfaceTypesInfo, etc.) are fine because they are fully replaced via reassignment in buildTypesInfo (e.g., this.objectTypesInfo = this.metadataStorage.objectTypes.map(...) at line 272). However, the new Maps are only ever written to via .set() — entries from previous builds are never removed.
For example, if Schema A is built with types [X, Y] and then Schema B is built with types [Z], the objectTypesInfoMap will contain {X → infoA_X, Y → infoA_Y, Z → infoB_Z} — stale entries for X and Y persist. This is a memory leak that grows with each generateFromMetadata call using different types, which is common in test suites and applications that dynamically rebuild schemas.
Impact: Memory leak in long-running processes or test suites that build multiple schemas. While stale entries are unlikely to cause incorrect lookups (current-build thunks reference current-build targets), the unbounded growth of these Maps is a resource management issue.
Prompt for agents
In src/schema/schema-generator.ts, the static Maps objectTypesInfoMap, inputTypesInfoMap, interfaceTypesInfoMap, and unionTypesInfoMap need to be cleared at the start of buildTypesInfo() or at the end of generateFromMetadata(). The simplest fix is to add clearing statements at the beginning of buildTypesInfo (around line 205), before the maps are repopulated:
this.objectTypesInfoMap = new Map();
this.interfaceTypesInfoMap = new Map();
this.inputTypesInfoMap = new Map();
this.unionTypesInfoMap = new Map();
Alternatively, add these lines alongside the existing cleanup in generateFromMetadata (near line 154-155) where usedInterfaceTypes is already being reset.
Was this helpful? React with 👍 or 👎 to provide feedback.

Performance Optimization: Map-based Caches for Metadata Class Building
Problem
Our schema generation process was becoming exponentially slower as our schema grew, taking over a minute to complete in some cases. With our large schema containing ClassMetadata arrays ranging from 3,000 to 15,000 objects, the repeated
.find()and.filter()operations within forEach loops were creating significant performance bottlenecks.Solution
Implemented map-based "caches" during the metadata class build process to replace inefficient array operations. These caches are added as new variables on the MetadataStorage class, while preserving list-based attributes where required by existing components.
Impact
This optimization has dramatically improved performance as demonstrated by the following benchmarks:
before (using O(n) lookups)
after (using O(1) lookups)
Implementation Notes
.find()and.filter()operations within loop contexts