@yolo-croket-dev/core 1.7.23 → 1.7.25-slowquery.0
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.
- package/interceptors/logging-service.interceptor.js +2 -2
- package/interceptors/rpc.interceptor.js +56 -16
- package/interfaces/exception.js +3 -3
- package/modules/graceful-shutdown/graceful-shutdown.module.js +2 -2
- package/modules/graceful-shutdown/graceful-shutdown.rmq.d.ts +8 -1
- package/modules/graceful-shutdown/graceful-shutdown.rmq.js +54 -2
- package/modules/graceful-shutdown/graceful-shutdown.service.js +2 -2
- package/modules/redis.module.js +2 -2
- package/package.json +1 -1
- package/pipes/rpc-validation.pipe.js +2 -2
|
@@ -188,7 +188,7 @@ let LoggingServiceInterceptor = class LoggingServiceInterceptor {
|
|
|
188
188
|
}));
|
|
189
189
|
}
|
|
190
190
|
};
|
|
191
|
-
|
|
192
|
-
exports.LoggingServiceInterceptor = LoggingServiceInterceptor = __decorate([
|
|
191
|
+
LoggingServiceInterceptor = __decorate([
|
|
193
192
|
(0, common_1.Injectable)()
|
|
194
193
|
], LoggingServiceInterceptor);
|
|
194
|
+
exports.LoggingServiceInterceptor = LoggingServiceInterceptor;
|
|
@@ -178,6 +178,16 @@ let RPCInterceptor = class RPCInterceptor {
|
|
|
178
178
|
const args = context.getArgs();
|
|
179
179
|
const arg = args[0];
|
|
180
180
|
const rmq = args[1];
|
|
181
|
+
// 메시지에서 AbortController 가져오기
|
|
182
|
+
const abortController = rmq.abortController;
|
|
183
|
+
// const messageId = (rmq as any).messageId;
|
|
184
|
+
// if (!abortController) {
|
|
185
|
+
// // AbortController가 없으면 기본 처리
|
|
186
|
+
// return next.handle().pipe(
|
|
187
|
+
// timeout(1000 * 60 * 29),
|
|
188
|
+
// // ... 기존 로직
|
|
189
|
+
// );
|
|
190
|
+
// }
|
|
181
191
|
const channel = rmq.getChannelRef();
|
|
182
192
|
const message = rmq.getMessage();
|
|
183
193
|
const now = Date.now();
|
|
@@ -339,7 +349,21 @@ let RPCInterceptor = class RPCInterceptor {
|
|
|
339
349
|
// eslint-disable-next-line consistent-return
|
|
340
350
|
return next
|
|
341
351
|
.handle()
|
|
342
|
-
.pipe(
|
|
352
|
+
.pipe(
|
|
353
|
+
// AbortController 신호 감지
|
|
354
|
+
(0, operators_1.takeUntil)(new rxjs_1.Observable(subscriber => {
|
|
355
|
+
const abortHandler = () => {
|
|
356
|
+
subscriber.next();
|
|
357
|
+
subscriber.complete();
|
|
358
|
+
};
|
|
359
|
+
abortController.signal.addEventListener('abort', abortHandler, { once: true });
|
|
360
|
+
// 정리 함수
|
|
361
|
+
return () => {
|
|
362
|
+
abortController.signal.removeEventListener('abort', abortHandler);
|
|
363
|
+
};
|
|
364
|
+
})),
|
|
365
|
+
// timeout(1000 * 60 * 29), // 30분 타임아웃
|
|
366
|
+
(0, operators_1.timeout)(1000 * 2), // 30분 타임아웃
|
|
343
367
|
(0, operators_1.tap)({
|
|
344
368
|
next: async (val) => {
|
|
345
369
|
val.isError = false;
|
|
@@ -365,20 +389,36 @@ let RPCInterceptor = class RPCInterceptor {
|
|
|
365
389
|
// eslint-disable-next-line consistent-return
|
|
366
390
|
(0, operators_1.catchError)(async (err) => {
|
|
367
391
|
// timeout 에러 처리
|
|
368
|
-
msaLogger.error('Timeout occurred, requeuing message', { traceId: execInterceptor.traceId }, {
|
|
369
|
-
error: {
|
|
370
|
-
message: err.message,
|
|
371
|
-
stack: err.stack,
|
|
372
|
-
name: err.name,
|
|
373
|
-
response: err.response,
|
|
374
|
-
},
|
|
375
|
-
});
|
|
376
392
|
const isTimeoutError = err instanceof Error && err.name === 'TimeoutError';
|
|
377
|
-
if (isTimeoutError
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
msaLogger.
|
|
393
|
+
if (isTimeoutError) {
|
|
394
|
+
if (abortController && abortController.signal.aborted) {
|
|
395
|
+
throw new Error('Message aborted due to timeout');
|
|
396
|
+
}
|
|
397
|
+
msaLogger.error('[타임 아웃] 메시지 재전송 처리', { traceId: execInterceptor.traceId }, {
|
|
398
|
+
error: {
|
|
399
|
+
message: err.message,
|
|
400
|
+
stack: err.stack,
|
|
401
|
+
name: err.name,
|
|
402
|
+
response: err.response,
|
|
403
|
+
},
|
|
404
|
+
});
|
|
405
|
+
if (!execInterceptor.isMessageAck) {
|
|
406
|
+
channel.nack(message, false, true);
|
|
407
|
+
execInterceptor.isMessageAck = true;
|
|
408
|
+
message.properties.replyTo = undefined; /** replyTo가 존재하면 sendToQueue가 호출되므로, 삭제해주기 */
|
|
409
|
+
msaLogger.debug('TimeoutError occurred, message requeued called', { traceId: execInterceptor.traceId });
|
|
410
|
+
return new rxjs_1.Observable();
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
msaLogger.error('에러 응답 전송 처리', { traceId: execInterceptor.traceId }, {
|
|
415
|
+
error: {
|
|
416
|
+
message: err.message,
|
|
417
|
+
stack: err.stack,
|
|
418
|
+
name: err.name,
|
|
419
|
+
response: err.response,
|
|
420
|
+
},
|
|
421
|
+
});
|
|
382
422
|
return new rxjs_1.Observable();
|
|
383
423
|
}
|
|
384
424
|
}), (0, operators_1.finalize)(() => {
|
|
@@ -386,9 +426,9 @@ let RPCInterceptor = class RPCInterceptor {
|
|
|
386
426
|
}));
|
|
387
427
|
}
|
|
388
428
|
};
|
|
389
|
-
|
|
390
|
-
exports.RPCInterceptor = RPCInterceptor = __decorate([
|
|
429
|
+
RPCInterceptor = __decorate([
|
|
391
430
|
(0, common_1.Injectable)(),
|
|
392
431
|
__metadata("design:paramtypes", [core_1.Reflector,
|
|
393
432
|
graceful_shutdown_service_1.GracefulShutdownService])
|
|
394
433
|
], RPCInterceptor);
|
|
434
|
+
exports.RPCInterceptor = RPCInterceptor;
|
package/interfaces/exception.js
CHANGED
|
@@ -18,10 +18,9 @@ var ErrActionTypeEnum;
|
|
|
18
18
|
ErrActionTypeEnum["LEGACY_SHOW_TOAST"] = "LEGACY_SHOW_TOAST";
|
|
19
19
|
ErrActionTypeEnum["FORCE_UPDATE"] = "FORCE_UPDATE";
|
|
20
20
|
ErrActionTypeEnum["UPDATE_ACCESS_TOKEN"] = "UPDATE_ACCESS_TOKEN";
|
|
21
|
-
})(ErrActionTypeEnum
|
|
21
|
+
})(ErrActionTypeEnum = exports.ErrActionTypeEnum || (exports.ErrActionTypeEnum = {}));
|
|
22
22
|
class ErrAction {
|
|
23
23
|
}
|
|
24
|
-
exports.ErrAction = ErrAction;
|
|
25
24
|
__decorate([
|
|
26
25
|
(0, swagger_1.ApiProperty)({
|
|
27
26
|
description: '액션 타입',
|
|
@@ -58,9 +57,9 @@ __decorate([
|
|
|
58
57
|
}),
|
|
59
58
|
__metadata("design:type", String)
|
|
60
59
|
], ErrAction.prototype, "webViewUrl", void 0);
|
|
60
|
+
exports.ErrAction = ErrAction;
|
|
61
61
|
class ErrInfo {
|
|
62
62
|
}
|
|
63
|
-
exports.ErrInfo = ErrInfo;
|
|
64
63
|
__decorate([
|
|
65
64
|
(0, swagger_1.ApiProperty)({
|
|
66
65
|
description: '에러 종류 식별자',
|
|
@@ -82,3 +81,4 @@ __decorate([
|
|
|
82
81
|
}),
|
|
83
82
|
__metadata("design:type", ErrAction)
|
|
84
83
|
], ErrInfo.prototype, "action", void 0);
|
|
84
|
+
exports.ErrInfo = ErrInfo;
|
|
@@ -25,8 +25,7 @@ let GracefulShutdownModule = class GracefulShutdownModule {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
|
-
|
|
29
|
-
exports.GracefulShutdownModule = GracefulShutdownModule = __decorate([
|
|
28
|
+
GracefulShutdownModule = __decorate([
|
|
30
29
|
(0, common_1.Global)(),
|
|
31
30
|
(0, common_1.Module)({
|
|
32
31
|
providers: [graceful_shutdown_service_1.GracefulShutdownService],
|
|
@@ -34,3 +33,4 @@ exports.GracefulShutdownModule = GracefulShutdownModule = __decorate([
|
|
|
34
33
|
}),
|
|
35
34
|
__metadata("design:paramtypes", [graceful_shutdown_service_1.GracefulShutdownService])
|
|
36
35
|
], GracefulShutdownModule);
|
|
36
|
+
exports.GracefulShutdownModule = GracefulShutdownModule;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
1
3
|
import { RmqOptions, ServerRMQ } from '@nestjs/microservices';
|
|
2
4
|
import { Channel } from 'amqp-connection-manager';
|
|
3
5
|
import { ConsumeMessage } from 'amqplib';
|
|
4
|
-
export type GracefulRmqOptions = RmqOptions['options'] & {
|
|
6
|
+
export declare type GracefulRmqOptions = RmqOptions['options'] & {
|
|
5
7
|
waitingEndingHandlersTimeoutMs?: number;
|
|
6
8
|
waitingEndingHandlersIntervalMs?: number;
|
|
7
9
|
};
|
|
@@ -12,9 +14,14 @@ export declare class GracefulServerRMQ extends ServerRMQ {
|
|
|
12
14
|
protected consumerTag: string | null;
|
|
13
15
|
protected readonly waitingEndingHandlersTimeoutMs: number;
|
|
14
16
|
protected readonly waitingEndingHandlersIntervalMs: number;
|
|
17
|
+
protected messageAbortControllers: Map<string, AbortController>;
|
|
18
|
+
protected messageTimeouts: Map<string, NodeJS.Timeout>;
|
|
15
19
|
constructor(options: GracefulRmqOptions);
|
|
16
20
|
setupChannel(channel: Channel, callback: () => void): Promise<void>;
|
|
17
21
|
protected waitingHandlers(): Promise<void>;
|
|
18
22
|
handleMessage(message: ConsumeMessage, channel: Channel): Promise<void>;
|
|
23
|
+
private cleanupMessage;
|
|
24
|
+
abortMessage(messageId: string): void;
|
|
25
|
+
abortAllMessages(): void;
|
|
19
26
|
close(): Promise<void>;
|
|
20
27
|
}
|
|
@@ -6,6 +6,8 @@ const node_util_1 = require("node:util");
|
|
|
6
6
|
const sleep = (0, node_util_1.promisify)(setTimeout);
|
|
7
7
|
const RMQ_DEFAULT_WAITING_ENDING_HANDLERS_TIMEOUT_MS = 5000;
|
|
8
8
|
const RMQ_DEFAULT_WAITING_ENDING_HANDLERS_INTERVAL_MS = 500;
|
|
9
|
+
// const RMQ_DEFAULT_MESSAGE_TIMEOUT_MS = 1000 * 60 * 29; // 29분
|
|
10
|
+
const RMQ_DEFAULT_MESSAGE_TIMEOUT_MS = 1000 * 2; // 2분
|
|
9
11
|
class GracefulServerRMQ extends microservices_1.ServerRMQ {
|
|
10
12
|
constructor(options) {
|
|
11
13
|
super(options);
|
|
@@ -13,6 +15,10 @@ class GracefulServerRMQ extends microservices_1.ServerRMQ {
|
|
|
13
15
|
this.runningMessages = 0;
|
|
14
16
|
this.closing = false;
|
|
15
17
|
this.consumerTag = null;
|
|
18
|
+
// 메시지별 AbortController 관리
|
|
19
|
+
this.messageAbortControllers = new Map();
|
|
20
|
+
// eslint-disable-next-line no-undef
|
|
21
|
+
this.messageTimeouts = new Map();
|
|
16
22
|
this.waitingEndingHandlersTimeoutMs = this.getOptionsProp(this.options, 'waitingEndingHandlersTimeoutMs')
|
|
17
23
|
|| RMQ_DEFAULT_WAITING_ENDING_HANDLERS_TIMEOUT_MS;
|
|
18
24
|
this.waitingEndingHandlersIntervalMs = this.getOptionsProp(this.options, 'waitingEndingHandlersIntervalMs')
|
|
@@ -36,13 +42,59 @@ class GracefulServerRMQ extends microservices_1.ServerRMQ {
|
|
|
36
42
|
}
|
|
37
43
|
}
|
|
38
44
|
async handleMessage(message, channel) {
|
|
45
|
+
const messageId = message.properties.messageId || message.properties.correlationId || `msg_${Date.now()}_${Math.random()}`;
|
|
46
|
+
// AbortController 생성
|
|
47
|
+
const abortController = new AbortController();
|
|
48
|
+
this.messageAbortControllers.set(messageId, abortController);
|
|
49
|
+
// 타임아웃 설정
|
|
50
|
+
const timeoutId = setTimeout(() => {
|
|
51
|
+
abortController.abort();
|
|
52
|
+
this.cleanupMessage(messageId);
|
|
53
|
+
}, RMQ_DEFAULT_MESSAGE_TIMEOUT_MS);
|
|
54
|
+
this.messageTimeouts.set(messageId, timeoutId);
|
|
39
55
|
this.runningMessages += 1;
|
|
40
|
-
|
|
56
|
+
try {
|
|
57
|
+
// AbortController를 메시지에 추가
|
|
58
|
+
// eslint-disable-next-line no-param-reassign, @typescript-eslint/no-explicit-any
|
|
59
|
+
message.abortController = abortController;
|
|
60
|
+
// eslint-disable-next-line no-param-reassign, @typescript-eslint/no-explicit-any
|
|
61
|
+
message.messageId = messageId;
|
|
62
|
+
return await super.handleMessage(message, channel);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
this.cleanupMessage(messageId);
|
|
41
66
|
this.runningMessages -= 1;
|
|
42
|
-
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
cleanupMessage(messageId) {
|
|
70
|
+
// 타임아웃 정리
|
|
71
|
+
const timeoutId = this.messageTimeouts.get(messageId);
|
|
72
|
+
if (timeoutId) {
|
|
73
|
+
clearTimeout(timeoutId);
|
|
74
|
+
this.messageTimeouts.delete(messageId);
|
|
75
|
+
}
|
|
76
|
+
// AbortController 정리
|
|
77
|
+
this.messageAbortControllers.delete(messageId);
|
|
78
|
+
}
|
|
79
|
+
// 특정 메시지 강제 중단
|
|
80
|
+
abortMessage(messageId) {
|
|
81
|
+
const abortController = this.messageAbortControllers.get(messageId);
|
|
82
|
+
if (abortController) {
|
|
83
|
+
abortController.abort();
|
|
84
|
+
this.cleanupMessage(messageId);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// 모든 실행 중인 메시지 중단
|
|
88
|
+
abortAllMessages() {
|
|
89
|
+
for (const [messageId, abortController,] of this.messageAbortControllers) {
|
|
90
|
+
abortController.abort();
|
|
91
|
+
this.cleanupMessage(messageId);
|
|
92
|
+
}
|
|
43
93
|
}
|
|
44
94
|
async close() {
|
|
45
95
|
this.closing = true;
|
|
96
|
+
// 모든 실행 중인 메시지 중단
|
|
97
|
+
this.abortAllMessages();
|
|
46
98
|
if (this.channel) {
|
|
47
99
|
await this.channel.removeSetup(undefined, (channel) => channel.cancel(this.consumerTag));
|
|
48
100
|
}
|
|
@@ -137,7 +137,7 @@ let GracefulShutdownService = class GracefulShutdownService {
|
|
|
137
137
|
});
|
|
138
138
|
}
|
|
139
139
|
};
|
|
140
|
-
|
|
141
|
-
exports.GracefulShutdownService = GracefulShutdownService = __decorate([
|
|
140
|
+
GracefulShutdownService = __decorate([
|
|
142
141
|
(0, common_1.Injectable)()
|
|
143
142
|
], GracefulShutdownService);
|
|
143
|
+
exports.GracefulShutdownService = GracefulShutdownService;
|
package/modules/redis.module.js
CHANGED
|
@@ -15,10 +15,10 @@ const redisProvider = {
|
|
|
15
15
|
};
|
|
16
16
|
let RedisModule = class RedisModule {
|
|
17
17
|
};
|
|
18
|
-
|
|
19
|
-
exports.RedisModule = RedisModule = __decorate([
|
|
18
|
+
RedisModule = __decorate([
|
|
20
19
|
(0, common_1.Module)({
|
|
21
20
|
providers: [redisProvider],
|
|
22
21
|
exports: [redisProvider],
|
|
23
22
|
})
|
|
24
23
|
], RedisModule);
|
|
24
|
+
exports.RedisModule = RedisModule;
|
package/package.json
CHANGED
|
@@ -31,8 +31,8 @@ let ValidationPipe = class ValidationPipe {
|
|
|
31
31
|
return value;
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
|
-
|
|
35
|
-
exports.ValidationPipe = ValidationPipe = __decorate([
|
|
34
|
+
ValidationPipe = __decorate([
|
|
36
35
|
(0, common_1.Injectable)(),
|
|
37
36
|
__metadata("design:paramtypes", [Object])
|
|
38
37
|
], ValidationPipe);
|
|
38
|
+
exports.ValidationPipe = ValidationPipe;
|