Compare commits

...

2 Commits

Author SHA1 Message Date
Kamil Myśliwiec
d7250e42e5 tests() exclude async hooks 2018-12-29 12:45:42 +01:00
Kamil Myśliwiec
6a084a36b6 feature() add async_hooks module (async storage) 2018-12-29 10:59:57 +01:00
8 changed files with 150 additions and 0 deletions

View File

@@ -144,6 +144,7 @@
"packages/core/errors/**/*",
"packages/common/exceptions/*.ts",
"packages/common/http/*.ts",
"packages/common/hooks/*.ts",
"packages/common/utils/load-package.util.ts",
"packages/microservices/exceptions/",
"packages/microservices/microservices-module.ts",

View File

@@ -0,0 +1,63 @@
import { OnModuleDestroy, OnModuleInit } from '@nestjs/common';
import * as asyncHooks from 'async_hooks';
import { AsyncHooksHelper } from './async-hooks-helper';
import { AsyncHooksStorage } from './async-hooks-storage';
export class AsyncContext implements OnModuleInit, OnModuleDestroy {
private static _instance: AsyncContext;
private constructor(
private readonly internalStorage: Map<number, any>,
private readonly asyncHookRef: asyncHooks.AsyncHook,
) {}
static getInstance(): AsyncContext {
if (!this._instance) {
this.initialize();
}
return this._instance;
}
onModuleInit() {
this.asyncHookRef.enable();
}
onModuleDestroy() {
this.asyncHookRef.disable();
}
set<TKey = any, TValue = any>(key: TKey, value: TValue) {
const store = this.getAsyncStorage();
store.set(key, value);
}
get<TKey = any, TReturnValue = any>(key: TKey): TReturnValue {
const store = this.getAsyncStorage();
return store.get(key) as TReturnValue;
}
run(fn: Function) {
const eid = asyncHooks.executionAsyncId();
this.internalStorage.set(eid, new Map());
fn();
}
private getAsyncStorage(): Map<unknown, unknown> {
const eid = asyncHooks.executionAsyncId();
const state = this.internalStorage.get(eid);
if (!state) {
throw new Error(
`Async ID (${eid}) is not registered within internal cache.`,
);
}
return state;
}
private static initialize() {
const asyncHooksStorage = new AsyncHooksStorage();
const asyncHook = AsyncHooksHelper.createHooks(asyncHooksStorage);
const storage = asyncHooksStorage.getInternalStorage();
this._instance = new AsyncContext(storage, asyncHook);
}
}

View File

@@ -0,0 +1,26 @@
import * as asyncHooks from 'async_hooks';
import { AsyncHooksStorage } from './async-hooks-storage';
export class AsyncHooksHelper {
static createHooks(storage: AsyncHooksStorage): asyncHooks.AsyncHook {
function init(
asyncId: number,
type: string,
triggerId: number,
resource: Object,
) {
if (storage.has(triggerId)) {
storage.inherit(asyncId, triggerId);
}
}
function destroy(asyncId) {
storage.delete(asyncId);
}
return asyncHooks.createHook({
init,
destroy,
} as asyncHooks.HookCallbacks);
}
}

View File

@@ -0,0 +1,11 @@
import { Injectable, NestMiddleware } from '@nestjs/common';
import { AsyncContext } from './async-context';
@Injectable()
export class AsyncHooksMiddleware implements NestMiddleware {
constructor(private readonly asyncContext: AsyncContext) {}
use(req: any, res: any, next: () => void) {
this.asyncContext.run(next);
}
}

View File

@@ -0,0 +1,14 @@
import { Global, Module } from '@nestjs/common';
import { AsyncContext } from './async-context';
@Global()
@Module({
providers: [
{
provide: AsyncContext,
useValue: AsyncContext.getInstance(),
},
],
exports: [AsyncContext],
})
export class AsyncHooksModule {}

View File

@@ -0,0 +1,31 @@
export class AsyncHooksStorage {
constructor(private readonly asyncStorage = new Map<number, unknown>()) {
this.initialize();
}
get<T = any>(triggerId: number): T {
return this.asyncStorage.get(triggerId) as T;
}
has(triggerId: number): boolean {
return this.asyncStorage.has(triggerId);
}
inherit(asyncId: number, triggerId: number) {
const value = this.asyncStorage.get(triggerId);
this.asyncStorage.set(asyncId, value);
}
delete(asyncId: number) {
this.asyncStorage.delete(asyncId);
}
getInternalStorage(): Map<number, unknown> {
return this.asyncStorage;
}
private initialize() {
const initialAsyncId = 1;
this.asyncStorage.set(initialAsyncId, new Map());
}
}

View File

@@ -0,0 +1,3 @@
export * from './async-context';
export * from './async-hooks-middleware';
export * from './async-hooks-module';

View File

@@ -10,6 +10,7 @@ export * from './cache';
export * from './decorators';
export * from './enums';
export * from './exceptions';
export * from './hooks';
export * from './http';
export {
ArgumentMetadata,