mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
fix(core) improve topological sorting performance
This commit is contained in:
@@ -47,7 +47,7 @@ describe('Middleware (execution order)', () => {
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it(`should execute middleware of modules that are closer to the root module`, () => {
|
||||
it(`should execute middleware in topological order`, () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/hello')
|
||||
.expect(200, RETURN_VALUE_B);
|
||||
|
||||
@@ -120,13 +120,8 @@ export class Module {
|
||||
return this._distance;
|
||||
}
|
||||
|
||||
public updateDistance(distance: number, stack: Module[]) {
|
||||
this._distance = distance;
|
||||
Array.from(this._imports)
|
||||
.filter(module => module && !stack.includes(this))
|
||||
.forEach(module =>
|
||||
module.updateDistance(distance + 1, stack.concat(this)),
|
||||
);
|
||||
set distance(value: number) {
|
||||
this._distance = value;
|
||||
}
|
||||
|
||||
public addCoreProviders(container: NestContainer) {
|
||||
@@ -417,9 +412,6 @@ export class Module {
|
||||
|
||||
public addRelatedModule(module: Module) {
|
||||
this._imports.add(module);
|
||||
if (this._distance + 1 > module._distance) {
|
||||
module.updateDistance(this._distance + 1, [this]);
|
||||
}
|
||||
}
|
||||
|
||||
public replace(toReplace: string | symbol | Type<any>, options: any) {
|
||||
|
||||
@@ -68,14 +68,15 @@ export class MiddlewareModule {
|
||||
modules: Map<string, Module>,
|
||||
) {
|
||||
const moduleEntries = [...modules.entries()];
|
||||
await Promise.all(
|
||||
moduleEntries.map(async ([name, module]) => {
|
||||
const loadMiddlewareConfiguration = async ([name, module]: [
|
||||
string,
|
||||
Module,
|
||||
]) => {
|
||||
const instance = module.instance;
|
||||
|
||||
await this.loadConfiguration(middlewareContainer, instance, name);
|
||||
await this.resolver.resolveInstances(module, name);
|
||||
}),
|
||||
);
|
||||
};
|
||||
await Promise.all(moduleEntries.map(loadMiddlewareConfiguration));
|
||||
}
|
||||
|
||||
public async loadConfiguration(
|
||||
|
||||
@@ -12,11 +12,9 @@ export class MiddlewareResolver {
|
||||
const middleware = this.middlewareContainer.getMiddlewareCollection(
|
||||
moduleName,
|
||||
);
|
||||
await Promise.all(
|
||||
[...middleware.values()].map(async wrapper =>
|
||||
this.resolveMiddlewareInstance(wrapper, middleware, module),
|
||||
),
|
||||
);
|
||||
const resolveInstance = async (wrapper: InstanceWrapper) =>
|
||||
this.resolveMiddlewareInstance(wrapper, middleware, module);
|
||||
await Promise.all([...middleware.values()].map(resolveInstance));
|
||||
}
|
||||
|
||||
private async resolveMiddlewareInstance(
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
import { ApplicationConfig } from './application-config';
|
||||
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from './constants';
|
||||
import { CircularDependencyException } from './errors/exceptions/circular-dependency.exception';
|
||||
import { ModulesContainer } from './injector';
|
||||
import { NestContainer } from './injector/container';
|
||||
import { InstanceWrapper } from './injector/instance-wrapper';
|
||||
import { Module } from './injector/module';
|
||||
@@ -116,6 +117,7 @@ export class DependenciesScanner {
|
||||
this.reflectControllers(metatype, token);
|
||||
this.reflectExports(metatype, token);
|
||||
}
|
||||
this.calculateModulesDistance(modules);
|
||||
}
|
||||
|
||||
public async reflectImports(
|
||||
@@ -251,6 +253,26 @@ export class DependenciesScanner {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async calculateModulesDistance(modules: ModulesContainer) {
|
||||
const modulesStack = [];
|
||||
const modulesGenerator = modules.values();
|
||||
const rootModule = modulesGenerator.next().value;
|
||||
|
||||
const calculateDistance = (moduleRef: Module, distance = 1) => {
|
||||
if (modulesStack.includes(moduleRef)) {
|
||||
return;
|
||||
}
|
||||
modulesStack.push(moduleRef);
|
||||
|
||||
const moduleImports = rootModule.relatedModules;
|
||||
moduleImports.forEach(module => {
|
||||
module.distance = distance;
|
||||
calculateDistance(module, distance + 1);
|
||||
});
|
||||
};
|
||||
calculateDistance(rootModule);
|
||||
}
|
||||
|
||||
public async insertImport(related: any, token: string, context: string) {
|
||||
if (isUndefined(related)) {
|
||||
throw new CircularDependencyException(context);
|
||||
|
||||
@@ -481,44 +481,4 @@ describe('Module', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getter "distance"', () => {
|
||||
@ModuleDecorator({})
|
||||
class ModuleA {}
|
||||
|
||||
@ModuleDecorator({})
|
||||
class ModuleB {}
|
||||
|
||||
@ModuleDecorator({})
|
||||
class ModuleC {}
|
||||
|
||||
let moduleA, moduleB, moduleC;
|
||||
beforeEach(() => {
|
||||
moduleA = new Module(ModuleA as any, [], container);
|
||||
moduleB = new Module(ModuleB as any, [], container);
|
||||
moduleC = new Module(ModuleC as any, [], container);
|
||||
});
|
||||
|
||||
it('should calculate "distance" properly', () => {
|
||||
moduleA.addRelatedModule(moduleB);
|
||||
moduleC.addRelatedModule(moduleB);
|
||||
moduleA.addRelatedModule(moduleC);
|
||||
|
||||
expect(moduleA.distance).to.be.equal(0);
|
||||
expect(moduleB.distance).to.be.equal(2);
|
||||
expect(moduleC.distance).to.be.equal(1);
|
||||
});
|
||||
|
||||
it('should not throw an exception when circular dependency occurs', () => {
|
||||
expect(() => {
|
||||
moduleA.addRelatedModule(moduleB);
|
||||
moduleB.addRelatedModule(moduleA);
|
||||
}).to.not.throw;
|
||||
expect(() => {
|
||||
moduleA.addRelatedModule(moduleB);
|
||||
moduleB.addRelatedModule(moduleC);
|
||||
moduleC.addRelatedModule(moduleA);
|
||||
}).to.not.throw;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user