@yolo-croket-dev/core 1.7.21 → 1.7.23
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/rpc.interceptor.js +4 -4
- package/modules/graceful-shutdown/graceful-shutdown.module.d.ts +6 -1
- package/modules/graceful-shutdown/graceful-shutdown.module.js +16 -1
- package/modules/graceful-shutdown/graceful-shutdown.service.d.ts +5 -0
- package/modules/graceful-shutdown/graceful-shutdown.service.js +48 -5
- package/package.json +1 -1
|
@@ -198,7 +198,7 @@ let RPCInterceptor = class RPCInterceptor {
|
|
|
198
198
|
if (this.gracefulShutdownService.isApplicationExit) {
|
|
199
199
|
msaLogger.debug('RPCInterceptor isApplicationExit called', { traceId: 'IS_APPLICATION_EXIT' });
|
|
200
200
|
channel.cancel(message.fields.consumerTag);
|
|
201
|
-
return rxjs_1.
|
|
201
|
+
return new rxjs_1.Observable();
|
|
202
202
|
}
|
|
203
203
|
if (!staticRetryCount && reDeliveryCount > 0) {
|
|
204
204
|
msaLogger.debug('RETRY_COUNT_ZERO_OR_NO_RETRY 발생 전, redelivery 발생', { traceId: ((_b = content.data.param) === null || _b === void 0 ? void 0 : _b.traceId) || 'RETRY_COUNT_ZERO_OR_NO_RETRY' }, {
|
|
@@ -302,7 +302,7 @@ let RPCInterceptor = class RPCInterceptor {
|
|
|
302
302
|
channel.ack(message);
|
|
303
303
|
execInterceptor.isMessageAck = true;
|
|
304
304
|
}
|
|
305
|
-
return rxjs_1.
|
|
305
|
+
return new rxjs_1.Observable();
|
|
306
306
|
};
|
|
307
307
|
localErrorProcessor = localErrorProcessor.bind(this);
|
|
308
308
|
/** 파일 객체 처리 */
|
|
@@ -323,7 +323,7 @@ let RPCInterceptor = class RPCInterceptor {
|
|
|
323
323
|
}
|
|
324
324
|
catch (err) {
|
|
325
325
|
await localErrorProcessor(err);
|
|
326
|
-
return rxjs_1.
|
|
326
|
+
return new rxjs_1.Observable();
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
if (content.pattern === 'UPLOAD_FILE') {
|
|
@@ -379,7 +379,7 @@ let RPCInterceptor = class RPCInterceptor {
|
|
|
379
379
|
execInterceptor.isMessageAck = true;
|
|
380
380
|
message.properties.replyTo = undefined; /** replyTo가 존재하면 sendToQueue가 호출되므로, 삭제해주기 */
|
|
381
381
|
msaLogger.debug('TimeoutError occurred, message requeued called', { traceId: execInterceptor.traceId });
|
|
382
|
-
return rxjs_1.
|
|
382
|
+
return new rxjs_1.Observable();
|
|
383
383
|
}
|
|
384
384
|
}), (0, operators_1.finalize)(() => {
|
|
385
385
|
this.gracefulShutdownService.popActiveInterceptors(execInterceptor);
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { OnModuleDestroy } from '@nestjs/common';
|
|
2
|
+
import { GracefulShutdownService } from './graceful-shutdown.service';
|
|
3
|
+
export declare class GracefulShutdownModule implements OnModuleDestroy {
|
|
4
|
+
private readonly gracefulShutdownService;
|
|
5
|
+
constructor(gracefulShutdownService: GracefulShutdownService);
|
|
6
|
+
onModuleDestroy(): Promise<void>;
|
|
2
7
|
}
|
|
@@ -5,11 +5,25 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
7
|
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
8
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
12
|
exports.GracefulShutdownModule = void 0;
|
|
10
13
|
const common_1 = require("@nestjs/common");
|
|
11
14
|
const graceful_shutdown_service_1 = require("./graceful-shutdown.service");
|
|
12
15
|
let GracefulShutdownModule = class GracefulShutdownModule {
|
|
16
|
+
constructor(gracefulShutdownService) {
|
|
17
|
+
this.gracefulShutdownService = gracefulShutdownService;
|
|
18
|
+
}
|
|
19
|
+
async onModuleDestroy() {
|
|
20
|
+
// GracefulShutdownService가 완료될 때까지 대기
|
|
21
|
+
while (this.gracefulShutdownService.getActiveInterceptorsCount() > 0) {
|
|
22
|
+
await new Promise(resolve => {
|
|
23
|
+
setTimeout(() => resolve(), 100);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
13
27
|
};
|
|
14
28
|
exports.GracefulShutdownModule = GracefulShutdownModule;
|
|
15
29
|
exports.GracefulShutdownModule = GracefulShutdownModule = __decorate([
|
|
@@ -17,5 +31,6 @@ exports.GracefulShutdownModule = GracefulShutdownModule = __decorate([
|
|
|
17
31
|
(0, common_1.Module)({
|
|
18
32
|
providers: [graceful_shutdown_service_1.GracefulShutdownService],
|
|
19
33
|
exports: [graceful_shutdown_service_1.GracefulShutdownService],
|
|
20
|
-
})
|
|
34
|
+
}),
|
|
35
|
+
__metadata("design:paramtypes", [graceful_shutdown_service_1.GracefulShutdownService])
|
|
21
36
|
], GracefulShutdownModule);
|
|
@@ -9,10 +9,15 @@ export declare class GracefulShutdownService implements OnApplicationShutdown, O
|
|
|
9
9
|
private activeInterceptors;
|
|
10
10
|
isApplicationExit: boolean;
|
|
11
11
|
private readonly HEALTHY_FILE_PATH;
|
|
12
|
+
private shutdownSignal?;
|
|
13
|
+
private isShutdownInProgress;
|
|
12
14
|
onApplicationBootstrap(): void;
|
|
13
15
|
pushActiveInterceptors(execInterceptor: GracefulExecInterceptor): void;
|
|
14
16
|
popActiveInterceptors(execInterceptor: GracefulExecInterceptor): void;
|
|
17
|
+
getActiveInterceptorsCount(): number;
|
|
18
|
+
getShutdownStatus(): boolean;
|
|
15
19
|
onApplicationShutdown(signal?: string): Promise<void>;
|
|
16
20
|
onModuleDestroy(): Promise<void>;
|
|
21
|
+
private waitForActiveInterceptors;
|
|
17
22
|
}
|
|
18
23
|
export {};
|
|
@@ -39,6 +39,7 @@ let GracefulShutdownService = class GracefulShutdownService {
|
|
|
39
39
|
this.activeInterceptors = [];
|
|
40
40
|
this.isApplicationExit = false;
|
|
41
41
|
this.HEALTHY_FILE_PATH = '/tmp/healthy';
|
|
42
|
+
this.isShutdownInProgress = false;
|
|
42
43
|
}
|
|
43
44
|
onApplicationBootstrap() {
|
|
44
45
|
this.activeInterceptors = [];
|
|
@@ -54,6 +55,7 @@ let GracefulShutdownService = class GracefulShutdownService {
|
|
|
54
55
|
// Health check 용 파일 생성
|
|
55
56
|
(0, fs_1.writeFileSync)(this.HEALTHY_FILE_PATH, (new Date().toISOString()));
|
|
56
57
|
const isHealthyFileExist = (0, fs_1.existsSync)(this.HEALTHY_FILE_PATH);
|
|
58
|
+
// eslint-disable-next-line max-len
|
|
57
59
|
msaLogger.debug(`GracefulShutdownService.onApplicationBootstrap isExecLocal=${this.isExecLocal}`, { traceId: 'ON_APPLICATION_BOOTSTRAP' }, { isHealthyFileExist });
|
|
58
60
|
}
|
|
59
61
|
pushActiveInterceptors(execInterceptor) {
|
|
@@ -63,35 +65,76 @@ let GracefulShutdownService = class GracefulShutdownService {
|
|
|
63
65
|
// eslint-disable-next-line max-len
|
|
64
66
|
this.activeInterceptors = this.activeInterceptors.filter((item) => !(item.method === execInterceptor.method && item.traceId === execInterceptor.traceId && item.correlationId === execInterceptor.correlationId));
|
|
65
67
|
}
|
|
68
|
+
getActiveInterceptorsCount() {
|
|
69
|
+
return this.activeInterceptors.length;
|
|
70
|
+
}
|
|
71
|
+
getShutdownStatus() {
|
|
72
|
+
return this.isShutdownInProgress;
|
|
73
|
+
}
|
|
66
74
|
async onApplicationShutdown(signal) {
|
|
75
|
+
if (this.isShutdownInProgress) {
|
|
76
|
+
msaLogger.debug(`GracefulShutdownService.onApplicationShutdown already in progress, signal=${signal}`, { traceId: 'ON_APPLICATION_SHUTDOWN' });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.isShutdownInProgress = true;
|
|
80
|
+
this.shutdownSignal = signal;
|
|
81
|
+
this.isApplicationExit = true;
|
|
67
82
|
msaLogger.debug(`GracefulShutdownService.onApplicationShutdown signal=${signal}`, { traceId: 'ON_APPLICATION_SHUTDOWN' });
|
|
83
|
+
// onApplicationShutdown에서 모든 activeInterceptors 완료를 기다림
|
|
84
|
+
// 이렇게 하면 onModuleDestroy가 호출되기 전에 모든 이벤트가 처리됨
|
|
85
|
+
await this.waitForActiveInterceptors();
|
|
86
|
+
msaLogger.info('GracefulShutdownService.onApplicationShutdown: all interceptors finished, shutting down', { traceId: 'ON_APPLICATION_SHUTDOWN' });
|
|
87
|
+
// 모든 이벤트 처리 완료 후 종료
|
|
68
88
|
process.exit(0);
|
|
69
89
|
}
|
|
70
90
|
async onModuleDestroy() {
|
|
71
|
-
this.isApplicationExit = true;
|
|
72
91
|
msaLogger.debug('GracefulShutdownService.onModuleDestroy', { traceId: 'ON_MODULE_DESTROY' });
|
|
92
|
+
// onModuleDestroy에서는 단순히 플래그만 설정
|
|
93
|
+
// 실제 대기는 onApplicationShutdown에서 처리됨
|
|
94
|
+
this.isApplicationExit = true;
|
|
95
|
+
}
|
|
96
|
+
async waitForActiveInterceptors() {
|
|
73
97
|
if (this.isExecLocal) {
|
|
74
|
-
msaLogger.debug('GracefulShutdownService.
|
|
98
|
+
msaLogger.debug('GracefulShutdownService.waitForActiveInterceptors isExecLocal=true', { traceId: 'WAIT_FOR_INTERCEPTORS' });
|
|
75
99
|
return;
|
|
76
100
|
}
|
|
77
101
|
// eslint-disable-next-line max-len
|
|
78
|
-
msaLogger.debug('GracefulShutdownService.
|
|
102
|
+
msaLogger.debug('GracefulShutdownService.waitForActiveInterceptors activeInterceptors', { traceId: 'WAIT_FOR_INTERCEPTORS' }, { activeInterceptors: this.activeInterceptors });
|
|
79
103
|
let prevExecInterceptorCount = 0;
|
|
80
104
|
let count = 0;
|
|
81
|
-
|
|
105
|
+
const maxWaitTime = 1000 * 60 * 10; // 최대 10분 대기
|
|
106
|
+
const startTime = Date.now();
|
|
107
|
+
while (this.activeInterceptors.length > 0) {
|
|
82
108
|
count += 1;
|
|
109
|
+
// 최대 대기 시간 체크
|
|
110
|
+
if (Date.now() - startTime > maxWaitTime) {
|
|
111
|
+
// eslint-disable-next-line max-len
|
|
112
|
+
msaLogger.warn('GracefulShutdownService.waitForActiveInterceptors: max wait time exceeded, forcing shutdown', { traceId: 'WAIT_FOR_INTERCEPTORS' }, {
|
|
113
|
+
activeInterceptors: this.activeInterceptors,
|
|
114
|
+
count,
|
|
115
|
+
maxWaitTime,
|
|
116
|
+
});
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
83
119
|
if ((prevExecInterceptorCount !== this.activeInterceptors.length) || (count % 100 === 0)) {
|
|
84
120
|
prevExecInterceptorCount = this.activeInterceptors.length;
|
|
85
121
|
// eslint-disable-next-line max-len
|
|
86
|
-
msaLogger.debug('GracefulShutdownService.
|
|
122
|
+
msaLogger.debug('GracefulShutdownService.waitForActiveInterceptors: waiting for interceptors to finish', { traceId: 'WAIT_FOR_INTERCEPTORS' }, {
|
|
87
123
|
activeInterceptors: this.activeInterceptors,
|
|
88
124
|
count,
|
|
125
|
+
remainingCount: this.activeInterceptors.length,
|
|
126
|
+
elapsedTime: Date.now() - startTime,
|
|
89
127
|
});
|
|
90
128
|
}
|
|
91
129
|
await new Promise((resolve) => {
|
|
92
130
|
setTimeout(resolve, 1000);
|
|
93
131
|
});
|
|
94
132
|
}
|
|
133
|
+
msaLogger.info('GracefulShutdownService.waitForActiveInterceptors: all interceptors finished', { traceId: 'WAIT_FOR_INTERCEPTORS' }, {
|
|
134
|
+
finalCount: count,
|
|
135
|
+
shutdownSignal: this.shutdownSignal,
|
|
136
|
+
totalElapsedTime: Date.now() - startTime,
|
|
137
|
+
});
|
|
95
138
|
}
|
|
96
139
|
};
|
|
97
140
|
exports.GracefulShutdownService = GracefulShutdownService;
|