Skip to content

Commit 316d442

Browse files
committed
fix: made type-metadata.storage.ts much faster
Changed the data modeling of type-metadata storage to store the data in maps instead of arrays. This results in a dramatic improvement of application startup time since many of the lookups for metadata are now in O(1) instead of O(n)
1 parent fd060a8 commit 316d442

File tree

2 files changed

+168
-82
lines changed

2 files changed

+168
-82
lines changed

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

Lines changed: 125 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@ import {
88
PropertyMetadata,
99
ResolverClassMetadata,
1010
} from '../metadata';
11-
import { InterfaceMetadata } from '../metadata/interface.metadata';
1211
import { ObjectTypeMetadata } from '../metadata/object-type.metadata';
1312

1413
export class MapByName<T> {
1514
protected map = new Map<string, T>();
16-
protected all: T[] = [];
15+
protected all: (T extends any[] ? T[number] : T)[] = [];
1716

1817
getAll() {
1918
return this.all;
@@ -39,19 +38,36 @@ export class MapByName<T> {
3938
}
4039

4140
export class MapArrayByName<T> extends MapByName<T[]> {
41+
constructor(protected globalArray: Array<T> = null) {
42+
super();
43+
}
44+
4245
getByName(name: string): T[] {
4346
return super.getByName(name) || [];
4447
}
4548

46-
add(value: T[] extends any[] ? T[][number] : T[], name: string) {
47-
let arrayResult = this.map.get(name);
49+
add(value: T, name: string) {
50+
let arrayResult = super.getByName(name);
4851
if (!arrayResult) {
4952
arrayResult = [];
5053
this.map.set(name, arrayResult);
5154
}
5255

5356
arrayResult.push(value);
5457
this.all.push(value);
58+
this.globalArray && this.globalArray.push(value);
59+
}
60+
61+
unshift(value: T, name: string) {
62+
let arrayResult = super.getByName(name);
63+
if (!arrayResult) {
64+
arrayResult = [];
65+
this.map.set(name, arrayResult);
66+
}
67+
68+
arrayResult.unshift(value);
69+
this.all.push(value);
70+
this.globalArray && this.globalArray.unshift(value);
5571
}
5672
}
5773

@@ -67,63 +83,133 @@ export class FieldDirectiveMap extends MapArrayByName<PropertyDirectiveMetadata>
6783

6884
this.sdls.add(value.sdl);
6985
this.fieldNames.add(value.fieldName);
86+
this.globalArray && this.globalArray.push(value);
87+
}
88+
}
89+
90+
class ArrayWithGlobalValues<T> extends Array<T> {
91+
constructor(private globalArray: Array<T>) {
92+
super();
93+
}
94+
95+
push(...items): number {
96+
this.globalArray.push(...items);
97+
return super.push(...items);
98+
}
99+
100+
unshift(...items): number {
101+
this.globalArray.unshift(...items);
102+
return super.unshift(...items);
70103
}
71104
}
72105

73106
export class TypeMetadataStorageModel {
74-
fieldDirectives = new FieldDirectiveMap();
75-
fieldExtensions = new MapArrayByName<PropertyExtensionsMetadata>();
107+
constructor(private all: AllMetadata) {}
108+
76109
fields = new MapByName<PropertyMetadata>();
77110
params = new MapArrayByName<MethodArgsMetadata>();
78-
classDirectives = new Array<ClassDirectiveMetadata>();
79-
classExtensions = new Array<ClassExtensionsMetadata>();
80-
argumentType: ClassMetadata;
81-
interface: InterfaceMetadata;
82-
inputType: ClassMetadata;
83-
objectType: ObjectTypeMetadata;
84-
resolver: ResolverClassMetadata;
111+
fieldDirectives = new FieldDirectiveMap(this.all.fieldDirectives);
112+
fieldExtensions = new MapArrayByName<PropertyExtensionsMetadata>(
113+
this.all.fieldExtensions,
114+
);
115+
classDirectives = new ArrayWithGlobalValues<ClassDirectiveMetadata>(
116+
this.all.classDirectives,
117+
);
118+
classExtensions = new ArrayWithGlobalValues<ClassExtensionsMetadata>(
119+
this.all.classExtensions,
120+
);
121+
122+
set argumentType(val: ClassMetadata) {
123+
this._argumentType = val;
124+
this.all.argumentType.push(val);
125+
}
126+
get argumentType() {
127+
return this._argumentType;
128+
}
129+
130+
set interface(val: ClassMetadata) {
131+
this._interface = val;
132+
this.all.interface.push(val);
133+
}
134+
get interface() {
135+
return this._interface;
136+
}
137+
138+
set inputType(val: ClassMetadata) {
139+
this._inputType = val;
140+
this.all.inputType.push(val);
141+
}
142+
get inputType() {
143+
return this._inputType;
144+
}
145+
146+
set objectType(val: ObjectTypeMetadata) {
147+
this._objectType = val;
148+
this.all.objectType.push(val);
149+
}
150+
get objectType() {
151+
return this._objectType;
152+
}
153+
154+
set resolver(val: ResolverClassMetadata) {
155+
this._resolver = val;
156+
this.all.resolver.push(val);
157+
}
158+
get resolver() {
159+
return this._resolver;
160+
}
161+
162+
_argumentType: ClassMetadata;
163+
_interface: ClassMetadata;
164+
_inputType: ClassMetadata;
165+
_objectType: ObjectTypeMetadata;
166+
_resolver: ResolverClassMetadata;
167+
}
168+
169+
interface AllMetadata {
170+
argumentType: ClassMetadata[];
171+
interface: ClassMetadata[];
172+
inputType: ClassMetadata[];
173+
objectType: ObjectTypeMetadata[];
174+
resolver: ResolverClassMetadata[];
175+
classDirectives: [];
176+
classExtensions: [];
177+
fieldDirectives: [];
178+
fieldExtensions: [];
85179
}
86180

87181
export class TypeMetadataStorageModelList {
88182
private map = new Map<Function, TypeMetadataStorageModel>();
89183
private array = new Array<TypeMetadataStorageModel>();
90184

185+
public all: AllMetadata = {
186+
argumentType: [],
187+
interface: [],
188+
inputType: [],
189+
objectType: [],
190+
resolver: [],
191+
classDirectives: [],
192+
classExtensions: [],
193+
fieldDirectives: [],
194+
fieldExtensions: [],
195+
};
196+
91197
get(target: Function) {
92198
let metadata = this.map.get(target);
93199

94200
if (!metadata) {
95-
metadata = new TypeMetadataStorageModel();
201+
metadata = new TypeMetadataStorageModel(this.all);
96202
this.map.set(target, metadata);
97203
this.array.push(metadata);
98204
}
99205

100206
return metadata;
101207
}
102208

103-
private getAllWithoutNullsByPredicate<V>(
104-
predicate: (t: TypeMetadataStorageModel) => V,
105-
) {
106-
return this.array.reduce((prev, curr) => {
107-
const val = predicate(curr);
108-
if (val) prev.push(val);
109-
return prev;
110-
}, new Array<V>());
111-
}
112-
113-
getAll() {
114-
return {
115-
argumentType: () =>
116-
this.getAllWithoutNullsByPredicate((t) => t.argumentType),
117-
interface: () => this.getAllWithoutNullsByPredicate((t) => t.interface),
118-
inputType: () => this.getAllWithoutNullsByPredicate((t) => t.inputType),
119-
objectType: () => this.getAllWithoutNullsByPredicate((t) => t.objectType),
120-
resolver: () => this.getAllWithoutNullsByPredicate((t) => t.resolver),
121-
classDirectives: () => this.array.flatMap((t) => t.classDirectives),
122-
classExtensions: () => this.array.flatMap((t) => t.classExtensions),
123-
fieldDirectives: () =>
124-
this.array.flatMap((t) => t.fieldDirectives.getAll()),
125-
fieldExtensions: () =>
126-
this.array.flatMap((t) => t.fieldExtensions.getAll()),
127-
};
209+
compile() {
210+
this.all.classDirectives.reverse();
211+
this.all.classExtensions.reverse();
212+
this.all.fieldDirectives.reverse();
213+
this.all.fieldExtensions.reverse();
128214
}
129215
}

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

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export class TypeMetadataStorageHost {
7373
}
7474

7575
getArgumentsMetadata(): ClassMetadata[] {
76-
return this.targets.getAll().argumentType();
76+
return this.targets.all.argumentType;
7777
}
7878

7979
getArgumentsMetadataByTarget(
@@ -87,7 +87,7 @@ export class TypeMetadataStorageHost {
8787
}
8888

8989
getInterfacesMetadata(): InterfaceMetadata[] {
90-
return this.targets.getAll().interface();
90+
return this.targets.all.interface;
9191
}
9292

9393
getInterfaceMetadataByTarget(
@@ -101,7 +101,7 @@ export class TypeMetadataStorageHost {
101101
}
102102

103103
getInputTypesMetadata(): ClassMetadata[] {
104-
return this.targets.getAll().inputType();
104+
return this.targets.all.inputType;
105105
}
106106

107107
getInputTypeMetadataByTarget(
@@ -115,7 +115,7 @@ export class TypeMetadataStorageHost {
115115
}
116116

117117
getObjectTypesMetadata(): ObjectTypeMetadata[] {
118-
return this.targets.getAll().objectType();
118+
return this.targets.all.objectType;
119119
}
120120

121121
getObjectTypeMetadataByTarget(
@@ -188,11 +188,13 @@ export class TypeMetadataStorageHost {
188188
}
189189

190190
compile(orphanedTypes: (Function | object)[] = []) {
191+
this.targets.compile();
192+
191193
const classMetadata = [
192-
...this.targets.getAll().objectType(),
193-
...this.targets.getAll().inputType(),
194-
...this.targets.getAll().argumentType(),
195-
...this.targets.getAll().interface(),
194+
...this.targets.all.objectType,
195+
...this.targets.all.inputType,
196+
...this.targets.all.argumentType,
197+
...this.targets.all.interface,
196198
];
197199
this.loadClassPluginMetadata(classMetadata);
198200
this.compileClassMetadata(classMetadata);
@@ -332,8 +334,9 @@ export class TypeMetadataStorageHost {
332334
const objectTypeRef = this.targets.get(item.target).resolver.typeFn();
333335

334336
const objectOrInterfaceTypeMetadata =
335-
this.targets.get(item.target).objectType ||
336-
this.targets.get(item.target).interface;
337+
this.targets.get(objectTypeRef).objectType ||
338+
this.targets.get(objectTypeRef).interface;
339+
337340
if (!objectOrInterfaceTypeMetadata) {
338341
throw new CannotDetermineHostTypeError(
339342
item.schemaName,
@@ -383,40 +386,37 @@ export class TypeMetadataStorageHost {
383386
}
384387

385388
private compileExtendedResolversMetadata() {
386-
this.targets
387-
.getAll()
388-
.resolver()
389-
.forEach((item) => {
390-
let parentClass = Object.getPrototypeOf(item.target);
391-
392-
while (parentClass.prototype) {
393-
const parentMetadata = this.targets.get(item.target).resolver;
394-
395-
if (parentMetadata) {
396-
this.queries = this.mergeParentResolverHandlers(
397-
this.queries,
398-
parentClass,
399-
item,
400-
);
401-
this.mutations = this.mergeParentResolverHandlers(
402-
this.mutations,
403-
parentClass,
404-
item,
405-
);
406-
this.subscriptions = this.mergeParentResolverHandlers(
407-
this.subscriptions,
408-
parentClass,
409-
item,
410-
);
411-
this.fieldResolvers = this.mergeParentFieldHandlers(
412-
this.fieldResolvers,
413-
parentClass,
414-
item,
415-
);
416-
}
417-
parentClass = Object.getPrototypeOf(parentClass);
389+
this.targets.all.resolver.forEach((item) => {
390+
let parentClass = Object.getPrototypeOf(item.target);
391+
392+
while (parentClass.prototype) {
393+
const parentMetadata = this.targets.get(item.target).resolver;
394+
395+
if (parentMetadata) {
396+
this.queries = this.mergeParentResolverHandlers(
397+
this.queries,
398+
parentClass,
399+
item,
400+
);
401+
this.mutations = this.mergeParentResolverHandlers(
402+
this.mutations,
403+
parentClass,
404+
item,
405+
);
406+
this.subscriptions = this.mergeParentResolverHandlers(
407+
this.subscriptions,
408+
parentClass,
409+
item,
410+
);
411+
this.fieldResolvers = this.mergeParentFieldHandlers(
412+
this.fieldResolvers,
413+
parentClass,
414+
item,
415+
);
418416
}
419-
});
417+
parentClass = Object.getPrototypeOf(parentClass);
418+
}
419+
});
420420
}
421421

422422
private mergeParentResolverHandlers<

0 commit comments

Comments
 (0)