Skip to content

Commit edd5545

Browse files
committed
fix: various preformace imporvements
1) getObjectOrInterfaceTypeNameIfExists - This function was not fixed on the move to map based metadata storage. Now instead of looking inside an entire metadata array, it fetches the item from the map 2) MetadataCollectionModel - I noticed that the interfaces list recieves duplicate instances of the same interface, so I switched the implementation to a map to remvoe duplicates. 3) LazyMetadataStorageHost.load - In very large applications (ie several millions of metadata object) we encountered an invalid arugments size thrown from V8, this is because the spread operator behind the scenes translates the values to multiple arguments. Switching to flat() solves this issue. 4) GraphQLSchemaFactory.create & LazyMetadataStorageHost.updateStorage - We noticed that the entire GQL metadata model was being rebuilt several times during the bootstrap of the application, I assume this was done because of the need to clear and rebuild the arrays every time. Now when we use maps we can't get duplicates in the metadata store, so I verify that the updateStorage callback is called only once, and I removed the clear call from the GraphQLSchemaFactory.create(). This results in a tremendous memory consumption boost! (in our case, from 8GB to 700MB)
1 parent 956735d commit edd5545

File tree

8 files changed

+25
-18
lines changed

8 files changed

+25
-18
lines changed

packages/graphql/lib/decorators/resolver.decorator.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@ function getObjectOrInterfaceTypeNameIfExists(
2020
nameOrType: Function,
2121
): string | undefined {
2222
const ctor = getClassOrUndefined(nameOrType);
23-
const objectTypesMetadata = TypeMetadataStorage.getObjectTypesMetadata();
24-
const objectMetadata = objectTypesMetadata.find(
25-
(type) => type.target === ctor,
26-
);
23+
const objectMetadata =
24+
TypeMetadataStorage.getObjectTypeMetadataByTarget(ctor);
2725
if (!objectMetadata) {
2826
const interfaceTypesMetadata = TypeMetadataStorage.getInterfacesMetadata();
2927
const interfaceMetadata = interfaceTypesMetadata.find(

packages/graphql/lib/schema-builder/collections/metada-collection-model.interface.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { ObjectTypeMetadata } from '../metadata/object-type.metadata';
1010

1111
export interface MetadataCollectionModel {
1212
argumentType: ClassMetadata[];
13-
interface: ClassMetadata[];
13+
interface: Map<Function, ClassMetadata>;
1414
inputType: ClassMetadata[];
1515
objectType: ObjectTypeMetadata[];
1616
resolver: ResolverClassMetadata[];

packages/graphql/lib/schema-builder/collections/metadata-by-target.collection.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { MetadataCollectionModel } from './metada-collection-model.interface';
22
import { TargetMetadataCollection } from './target-metadata.collection';
3+
import { ClassMetadata } from '../metadata';
34

45
export class MetadataByTargetCollection {
56
public readonly all: MetadataCollectionModel = {
67
argumentType: [],
7-
interface: [],
8+
interface: new Map<Function, ClassMetadata>(),
89
inputType: [],
910
objectType: [],
1011
resolver: [],

packages/graphql/lib/schema-builder/collections/target-metadata.collection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export class TargetMetadataCollection {
4848

4949
set interface(val: ClassMetadata) {
5050
this._interface = val;
51-
this.all.interface.push(val);
51+
this.all.interface.set(val.target, val);
5252
}
5353

5454
get interface() {

packages/graphql/lib/schema-builder/graphql-schema.factory.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ export class GraphQLSchemaFactory {
6060
options = scalarsOrOptions;
6161
}
6262

63-
TypeMetadataStorage.clear();
6463
LazyMetadataStorage.load(resolvers);
6564
TypeMetadataStorage.compile(options.orphanedTypes);
6665

packages/graphql/lib/schema-builder/storages/lazy-metadata.storage.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,16 @@ export class LazyMetadataStorageHost {
4848
.filter((metadata) => metadata),
4949
);
5050

51-
loadersToExecute = loadersToExecute.concat(
52-
...(this.lazyMetadataByTarget.get(NO_TARGET_METADATA) || []),
53-
);
51+
loadersToExecute = [
52+
loadersToExecute,
53+
this.lazyMetadataByTarget.get(NO_TARGET_METADATA) || [],
54+
].flat();
5455

5556
if (!options.skipFieldLazyMetadata) {
56-
loadersToExecute = loadersToExecute.concat(
57-
...(this.lazyMetadataByTarget.get(FIELD_LAZY_METADATA) || []),
58-
);
57+
loadersToExecute = [
58+
loadersToExecute,
59+
this.lazyMetadataByTarget.get(FIELD_LAZY_METADATA) || [],
60+
].flat();
5961
}
6062
loadersToExecute.forEach((func) => func());
6163
}
@@ -83,10 +85,16 @@ export class LazyMetadataStorageHost {
8385

8486
private updateStorage(key: symbol | Type<unknown>, func: Function) {
8587
const existingArray = this.lazyMetadataByTarget.get(key);
88+
let called = false;
89+
const singleCallFunctionWrapper = () => {
90+
if (called) return;
91+
func();
92+
called = true;
93+
};
8694
if (existingArray) {
87-
existingArray.push(func);
95+
existingArray.push(singleCallFunctionWrapper);
8896
} else {
89-
this.lazyMetadataByTarget.set(key, [func]);
97+
this.lazyMetadataByTarget.set(key, [singleCallFunctionWrapper]);
9098
}
9199
}
92100
}

packages/graphql/lib/schema-builder/storages/type-metadata.storage.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export class TypeMetadataStorageHost {
8282
}
8383

8484
getInterfacesMetadata(): InterfaceMetadata[] {
85-
return this.metadataByTargetCollection.all.interface;
85+
return [...this.metadataByTargetCollection.all.interface.values()];
8686
}
8787

8888
getInterfaceMetadataByTarget(
@@ -195,7 +195,7 @@ export class TypeMetadataStorageHost {
195195
...this.metadataByTargetCollection.all.objectType,
196196
...this.metadataByTargetCollection.all.inputType,
197197
...this.metadataByTargetCollection.all.argumentType,
198-
...this.metadataByTargetCollection.all.interface,
198+
...this.metadataByTargetCollection.all.interface.values(),
199199
];
200200
this.loadClassPluginMetadata(classMetadata);
201201
this.compileClassMetadata(classMetadata);

packages/graphql/lib/utils/transform-schema.util.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ export function transformSchema(
132132

133133
return new GraphQLInterfaceType({
134134
...config,
135+
interfaces: () => config.interfaces.map(replaceNamedType),
135136
fields: () => replaceFields(config.fields),
136137
});
137138
} else if (isUnionType(type)) {

0 commit comments

Comments
 (0)