@yolo-croket-dev/core 1.7.44-slowquery.1 → 1.7.44-slowquery.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -8,6 +8,7 @@ interface RequestContext {
|
|
|
8
8
|
export declare class AbortContext {
|
|
9
9
|
private static asyncLocalStorage;
|
|
10
10
|
private static contextCounter;
|
|
11
|
+
private static pendingAbortCallbacks;
|
|
11
12
|
static run<T>(abortSignal: AbortSignal, requestId: string, callback: (context: RequestContext) => T): T;
|
|
12
13
|
static getCurrentAbortSignal(): AbortSignal | null;
|
|
13
14
|
static getCurrentRequestId(): string | null;
|
|
@@ -15,6 +16,14 @@ export declare class AbortContext {
|
|
|
15
16
|
abortSignal: AbortSignal;
|
|
16
17
|
requestId: string;
|
|
17
18
|
} | null;
|
|
19
|
+
/**
|
|
20
|
+
* 컨텍스트 종료 전에 모든 abort 이벤트 리스너가 완료될 때까지 대기
|
|
21
|
+
*/
|
|
22
|
+
static waitForAbortHandlers(requestId: string): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* 이 컨텍스트의 모든 abort 이벤트 처리 완료 후 disable 호출
|
|
25
|
+
*/
|
|
26
|
+
static disableAfterAbortHandlers(requestId: string): Promise<void>;
|
|
18
27
|
static disable(): void;
|
|
19
28
|
static disableDeferred(): void;
|
|
20
29
|
static endContext(context?: RequestContext): void;
|
|
@@ -12,6 +12,35 @@ class AbortContext {
|
|
|
12
12
|
createdAt: Date.now(),
|
|
13
13
|
parentContext: currentContext || undefined,
|
|
14
14
|
};
|
|
15
|
+
// abort 이벤트가 발생할 때까지 대기하는 Promise 생성
|
|
16
|
+
const abortPromise = new Promise((resolve) => {
|
|
17
|
+
const checkAbortStatus = () => {
|
|
18
|
+
if (abortSignal.aborted) {
|
|
19
|
+
// 이벤트 루프를 두 번 통과시켜서 모든 abort 리스너가 완료되도록 보장
|
|
20
|
+
setTimeout(() => {
|
|
21
|
+
setTimeout(() => {
|
|
22
|
+
resolve();
|
|
23
|
+
}, 0);
|
|
24
|
+
}, 0);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// 아직 abort가 발생하지 않았으면 즉시 resolve
|
|
28
|
+
resolve();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
abortSignal.addEventListener('abort', checkAbortStatus, { once: true });
|
|
32
|
+
// abort 이벤트가 이미 발생했는지 확인
|
|
33
|
+
if (abortSignal.aborted) {
|
|
34
|
+
checkAbortStatus();
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// abort가 발생하지 않은 상태라면 즉시 resolve
|
|
38
|
+
setTimeout(() => {
|
|
39
|
+
resolve();
|
|
40
|
+
}, 0);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
this.pendingAbortCallbacks.set(newContext.requestId, abortPromise);
|
|
15
44
|
return this.asyncLocalStorage.run(newContext, callback.bind(this, newContext));
|
|
16
45
|
}
|
|
17
46
|
static getCurrentAbortSignal() {
|
|
@@ -32,6 +61,23 @@ class AbortContext {
|
|
|
32
61
|
}
|
|
33
62
|
return null;
|
|
34
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* 컨텍스트 종료 전에 모든 abort 이벤트 리스너가 완료될 때까지 대기
|
|
66
|
+
*/
|
|
67
|
+
static async waitForAbortHandlers(requestId) {
|
|
68
|
+
const promise = this.pendingAbortCallbacks.get(requestId);
|
|
69
|
+
if (promise) {
|
|
70
|
+
await promise;
|
|
71
|
+
this.pendingAbortCallbacks.delete(requestId);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 이 컨텍스트의 모든 abort 이벤트 처리 완료 후 disable 호출
|
|
76
|
+
*/
|
|
77
|
+
static async disableAfterAbortHandlers(requestId) {
|
|
78
|
+
await this.waitForAbortHandlers(requestId);
|
|
79
|
+
this.asyncLocalStorage.disable();
|
|
80
|
+
}
|
|
35
81
|
static disable() {
|
|
36
82
|
this.asyncLocalStorage.disable();
|
|
37
83
|
}
|
|
@@ -94,3 +140,5 @@ class AbortContext {
|
|
|
94
140
|
exports.AbortContext = AbortContext;
|
|
95
141
|
AbortContext.asyncLocalStorage = new async_hooks_1.AsyncLocalStorage();
|
|
96
142
|
AbortContext.contextCounter = 0;
|
|
143
|
+
// abort 이벤트 처리를 추적하기 위한 Map
|
|
144
|
+
AbortContext.pendingAbortCallbacks = new Map();
|
|
@@ -5,6 +5,19 @@ export declare class ContextPromisePatch {
|
|
|
5
5
|
private static originalSetInterval;
|
|
6
6
|
private static originalClearTimeout;
|
|
7
7
|
private static originalClearInterval;
|
|
8
|
+
private static activeAbortListeners;
|
|
9
|
+
/**
|
|
10
|
+
* abort 리스너를 추적
|
|
11
|
+
*/
|
|
12
|
+
static registerAbortListener(contextId: string): void;
|
|
13
|
+
/**
|
|
14
|
+
* abort 리스너 완료
|
|
15
|
+
*/
|
|
16
|
+
static unregisterAbortListener(contextId: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* 특정 컨텍스트의 모든 abort 리스너가 완료될 때까지 대기
|
|
19
|
+
*/
|
|
20
|
+
static waitForAbortListeners(contextId: string, timeout?: number): Promise<void>;
|
|
8
21
|
static patch(): void;
|
|
9
22
|
private static patchPromise;
|
|
10
23
|
private static patchTimeouts;
|
|
@@ -3,6 +3,36 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ContextPromisePatch = void 0;
|
|
4
4
|
const abort_context_1 = require("./abort-context");
|
|
5
5
|
class ContextPromisePatch {
|
|
6
|
+
/**
|
|
7
|
+
* abort 리스너를 추적
|
|
8
|
+
*/
|
|
9
|
+
static registerAbortListener(contextId) {
|
|
10
|
+
this.activeAbortListeners.add(contextId);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* abort 리스너 완료
|
|
14
|
+
*/
|
|
15
|
+
static unregisterAbortListener(contextId) {
|
|
16
|
+
this.activeAbortListeners.delete(contextId);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 특정 컨텍스트의 모든 abort 리스너가 완료될 때까지 대기
|
|
20
|
+
*/
|
|
21
|
+
static async waitForAbortListeners(contextId, timeout = 1000) {
|
|
22
|
+
const startTime = Date.now();
|
|
23
|
+
while (this.activeAbortListeners.has(contextId)) {
|
|
24
|
+
if (Date.now() - startTime > timeout) {
|
|
25
|
+
// 타임아웃
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
await new Promise((resolve) => {
|
|
30
|
+
setTimeout(() => {
|
|
31
|
+
resolve();
|
|
32
|
+
}, 10);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
6
36
|
static patch() {
|
|
7
37
|
if (this.isPatched) {
|
|
8
38
|
return;
|
|
@@ -153,8 +183,13 @@ class ContextPromisePatch {
|
|
|
153
183
|
abortSignal,
|
|
154
184
|
});
|
|
155
185
|
const onAbort = () => {
|
|
156
|
-
|
|
157
|
-
|
|
186
|
+
ContextPromisePatch.registerAbortListener(contextId);
|
|
187
|
+
// 비동기로 처리해서 이벤트 루프에서 실행되도록 함
|
|
188
|
+
setTimeout(() => {
|
|
189
|
+
this.originalClearTimeout(timeoutId);
|
|
190
|
+
activeTimers.delete(timeoutId);
|
|
191
|
+
ContextPromisePatch.unregisterAbortListener(contextId);
|
|
192
|
+
}, 0);
|
|
158
193
|
};
|
|
159
194
|
abortSignal.addEventListener('abort', onAbort, { once: true });
|
|
160
195
|
return timeoutId;
|
|
@@ -200,8 +235,13 @@ class ContextPromisePatch {
|
|
|
200
235
|
abortSignal,
|
|
201
236
|
});
|
|
202
237
|
const onAbort = () => {
|
|
203
|
-
|
|
204
|
-
|
|
238
|
+
ContextPromisePatch.registerAbortListener(contextId);
|
|
239
|
+
// 비동기로 처리해서 이벤트 루프에서 실행되도록 함
|
|
240
|
+
setTimeout(() => {
|
|
241
|
+
this.originalClearInterval(intervalId);
|
|
242
|
+
activeTimers.delete(intervalId);
|
|
243
|
+
ContextPromisePatch.unregisterAbortListener(contextId);
|
|
244
|
+
}, 0);
|
|
205
245
|
};
|
|
206
246
|
abortSignal.addEventListener('abort', onAbort, { once: true });
|
|
207
247
|
return intervalId;
|
|
@@ -233,3 +273,5 @@ class ContextPromisePatch {
|
|
|
233
273
|
}
|
|
234
274
|
exports.ContextPromisePatch = ContextPromisePatch;
|
|
235
275
|
ContextPromisePatch.isPatched = false;
|
|
276
|
+
// abort 리스너 추적
|
|
277
|
+
ContextPromisePatch.activeAbortListeners = new Set();
|
|
@@ -355,9 +355,11 @@ let RPCInterceptor = class RPCInterceptor {
|
|
|
355
355
|
}
|
|
356
356
|
},
|
|
357
357
|
error: localErrorProcessor,
|
|
358
|
-
}), (0, operators_1.finalize)(() => {
|
|
358
|
+
}), (0, operators_1.finalize)(async () => {
|
|
359
359
|
this.gracefulShutdownService.popActiveInterceptors(execInterceptor);
|
|
360
|
-
|
|
360
|
+
// abort 이벤트 리스너가 모두 완료될 때까지 기다림
|
|
361
|
+
await context_promise_patch_1.ContextPromisePatch.waitForAbortListeners(execInterceptor.traceId);
|
|
362
|
+
functions_1.AbortContext.disable();
|
|
361
363
|
abortController = null;
|
|
362
364
|
return rxjs_1.EMPTY;
|
|
363
365
|
})));
|