mega-framework 0.1.6 → 0.1.7
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/bin/mega-ws-hub.js +2 -2
- package/package.json +32 -8
- package/sample/crud/.env +1 -1
- package/sample/crud/.env.example +1 -1
- package/sample/crud/.mega/journal/history/20260612092543-create-users.json +261 -0
- package/sample/crud/.mega/journal/snapshot.json +261 -0
- package/sample/crud/apps/main/controllers/auth-controller.js +22 -14
- package/sample/crud/apps/main/controllers/web-controller.js +7 -5
- package/sample/crud/apps/main/migrations/20260606000001-create-users.js +91 -13
- package/sample/crud/apps/main/migrations/20260606000002-create-boards.js +165 -0
- package/sample/crud/apps/main/migrations/20260606000003-create-logs.js +107 -0
- package/sample/crud/apps/main/models/log-partition-model.js +105 -0
- package/sample/crud/apps/main/models/note-model.js +79 -0
- package/sample/crud/apps/main/models/user-level-model.js +24 -0
- package/sample/crud/apps/main/models/user-model.js +146 -0
- package/sample/crud/apps/main/models/user-type-model.js +21 -0
- package/sample/crud/apps/main/models/wallet-model.js +24 -0
- package/sample/crud/apps/main/routes/users.js +55 -10
- package/sample/crud/apps/main/schedules/log-partition-schedule.js +33 -0
- package/sample/crud/apps/main/services/auth-service.js +39 -24
- package/sample/crud/apps/main/services/log-partition-service.js +101 -0
- package/sample/crud/apps/main/services/note-service.js +6 -6
- package/sample/crud/apps/main/services/redis-demo-service.js +3 -3
- package/sample/crud/apps/main/services/user-service.js +62 -21
- package/sample/crud/apps/main/views/auth/login.ejs +6 -6
- package/sample/crud/apps/main/views/auth/register.ejs +46 -5
- package/sample/crud/apps/main/views/users/edit.ejs +42 -5
- package/sample/crud/apps/main/views/users/list.ejs +6 -2
- package/sample/crud/apps/main/views/users/new.ejs +56 -4
- package/sample/crud/docs/log_partition_design.mm.md +23 -0
- package/sample/crud/mega.config.js +3 -2
- package/sample/crud/package.json +1 -1
- package/sample/crud/scripts/start-ws-hub.sh +2 -2
- package/sample/simple/package.json +2 -2
- package/src/adapters/adapter-manager.js +2 -1
- package/src/adapters/adapter-options.js +30 -0
- package/src/adapters/maria-adapter.js +26 -3
- package/src/adapters/mega-db-adapter.js +7 -1
- package/src/adapters/mongo-adapter.js +19 -1
- package/src/adapters/postgres-adapter.js +25 -2
- package/src/adapters/sqlite-adapter.js +20 -1
- package/src/cli/commands/new.js +13 -3
- package/src/cli/commands/scaffold.js +137 -33
- package/src/cli/generators/index.js +82 -2
- package/src/cli/index.js +353 -100
- package/src/core/ajv-mapper.js +27 -2
- package/src/core/boot.js +464 -245
- package/src/core/cluster-metrics.js +13 -4
- package/src/core/ctx-builder.js +6 -2
- package/src/core/envelope.js +112 -12
- package/src/core/hub-link.js +65 -4
- package/src/core/i18n.js +11 -1
- package/src/core/index.js +6 -2
- package/src/core/mega-app.js +201 -463
- package/src/core/mega-cluster.js +4 -1
- package/src/core/mega-server.js +40 -9
- package/src/core/migration/dialect-registry.js +107 -0
- package/src/core/migration/dialects/README.md +62 -0
- package/src/core/migration/dialects/maria.js +496 -0
- package/src/core/migration/dialects/mongo.js +824 -0
- package/src/core/migration/dialects/postgres.js +563 -0
- package/src/core/migration/dialects/sqlite.js +476 -0
- package/src/core/migration/differ.js +456 -0
- package/src/core/migration/generate.js +508 -0
- package/src/core/migration/journal.js +167 -0
- package/src/core/migration/model-scan.js +84 -0
- package/src/core/migration/mongo-migration-db.js +97 -0
- package/src/core/migration/schema-builder.js +400 -0
- package/src/core/migration/schema-validator.js +315 -0
- package/src/core/migration-lock.js +205 -0
- package/src/core/migration-runner.js +166 -38
- package/src/core/multipart.js +28 -5
- package/src/core/pipeline.js +129 -0
- package/src/core/router.js +70 -65
- package/src/core/security.js +67 -9
- package/src/core/workers-manager.js +12 -1
- package/src/core/ws-cluster.js +10 -3
- package/src/core/ws-message.js +48 -4
- package/src/core/ws-presence.js +624 -0
- package/src/core/ws-roster.js +4 -1
- package/src/core/ws-upgrade.js +118 -12
- package/src/index.js +1 -1
- package/src/lib/hub-protocol.js +29 -0
- package/src/lib/mega-health.js +25 -4
- package/src/lib/mega-job-queue.js +98 -21
- package/src/lib/mega-job.js +29 -0
- package/src/lib/mega-metrics.js +3 -12
- package/src/lib/mega-plugin.js +34 -3
- package/src/lib/mega-schedule.js +40 -22
- package/src/lib/mega-shutdown.js +114 -39
- package/src/lib/mega-tracing.js +66 -19
- package/src/lib/mega-worker.js +5 -1
- package/src/lib/otel-resource.js +36 -0
- package/src/{cli → lib}/ws-hub.js +51 -8
- package/src/models/crud-sql-builder.js +133 -0
- package/src/models/mega-model.js +82 -2
- package/src/models/model-crud.js +483 -0
- package/src/models/mongo-crud.js +285 -0
- package/templates/model/code-mongo.tpl +35 -0
- package/templates/model/code.tpl +15 -1
- package/templates/model/test-mongo.tpl +38 -0
- package/templates/model/test.tpl +4 -0
- package/types/adapters/adapter-manager.d.ts +95 -0
- package/types/adapters/adapter-options.d.ts +91 -0
- package/types/adapters/file-adapter.d.ts +94 -0
- package/types/adapters/file-session-adapter.d.ts +101 -0
- package/types/adapters/index.d.ts +20 -0
- package/types/adapters/maria-adapter.d.ts +115 -0
- package/types/adapters/mega-adapter.d.ts +215 -0
- package/types/adapters/mega-bus-adapter.d.ts +45 -0
- package/types/adapters/mega-cache-adapter.d.ts +47 -0
- package/types/adapters/mega-db-adapter.d.ts +47 -0
- package/types/adapters/mega-lock-adapter.d.ts +62 -0
- package/types/adapters/mega-log-sink-adapter.d.ts +15 -0
- package/types/adapters/mega-session-adapter.d.ts +32 -0
- package/types/adapters/mongo-adapter.d.ts +139 -0
- package/types/adapters/nats-adapter.d.ts +108 -0
- package/types/adapters/postgres-adapter.d.ts +139 -0
- package/types/adapters/redis-adapter.d.ts +70 -0
- package/types/adapters/redis-session-adapter.d.ts +82 -0
- package/types/adapters/redlock-adapter.d.ts +149 -0
- package/types/adapters/registry.d.ts +46 -0
- package/types/adapters/sqlite-adapter.d.ts +106 -0
- package/types/auth/index.d.ts +24 -0
- package/types/cli/commands/console-cmd.d.ts +37 -0
- package/types/cli/commands/new.d.ts +16 -0
- package/types/cli/commands/routes.d.ts +36 -0
- package/types/cli/commands/scaffold.d.ts +78 -0
- package/types/cli/commands/test-cmd.d.ts +14 -0
- package/types/cli/generators/index.d.ts +112 -0
- package/types/cli/index.d.ts +249 -0
- package/types/cli/template-engine.d.ts +40 -0
- package/types/core/ajv-mapper.d.ts +27 -0
- package/types/core/boot.d.ts +233 -0
- package/types/core/cluster-metrics.d.ts +52 -0
- package/types/core/config-loader.d.ts +13 -0
- package/types/core/config-validator.d.ts +30 -0
- package/types/core/ctx-builder.d.ts +80 -0
- package/types/core/envelope.d.ts +79 -0
- package/types/core/error-mapper.d.ts +17 -0
- package/types/core/formbody.d.ts +41 -0
- package/types/core/hub-link.d.ts +264 -0
- package/types/core/i18n.d.ts +178 -0
- package/types/core/index.d.ts +28 -0
- package/types/core/mega-app.d.ts +529 -0
- package/types/core/mega-cluster.d.ts +104 -0
- package/types/core/mega-server.d.ts +91 -0
- package/types/core/mega-service.d.ts +31 -0
- package/types/core/migration/dialect-registry.d.ts +22 -0
- package/types/core/migration/dialects/maria.d.ts +99 -0
- package/types/core/migration/dialects/mongo.d.ts +89 -0
- package/types/core/migration/dialects/postgres.d.ts +117 -0
- package/types/core/migration/dialects/sqlite.d.ts +111 -0
- package/types/core/migration/differ.d.ts +47 -0
- package/types/core/migration/generate.d.ts +56 -0
- package/types/core/migration/journal.d.ts +52 -0
- package/types/core/migration/model-scan.d.ts +19 -0
- package/types/core/migration/mongo-migration-db.d.ts +7 -0
- package/types/core/migration/schema-builder.d.ts +197 -0
- package/types/core/migration/schema-validator.d.ts +20 -0
- package/types/core/migration-lock.d.ts +33 -0
- package/types/core/migration-runner.d.ts +101 -0
- package/types/core/multipart.d.ts +86 -0
- package/types/core/openapi.d.ts +62 -0
- package/types/core/pipeline.d.ts +92 -0
- package/types/core/router.d.ts +159 -0
- package/types/core/routes-loader.d.ts +21 -0
- package/types/core/scope-registry.d.ts +14 -0
- package/types/core/security.d.ts +77 -0
- package/types/core/services-loader.d.ts +27 -0
- package/types/core/session-cleanup-schedule.d.ts +19 -0
- package/types/core/session-store.d.ts +18 -0
- package/types/core/session.d.ts +77 -0
- package/types/core/static-assets.d.ts +73 -0
- package/types/core/template.d.ts +106 -0
- package/types/core/workers-manager.d.ts +79 -0
- package/types/core/ws-cluster.d.ts +208 -0
- package/types/core/ws-compression.d.ts +112 -0
- package/types/core/ws-controller.d.ts +65 -0
- package/types/core/ws-message.d.ts +106 -0
- package/types/core/ws-presence.d.ts +273 -0
- package/types/core/ws-roster.d.ts +96 -0
- package/types/core/ws-upgrade.d.ts +231 -0
- package/types/errors/config-error.d.ts +10 -0
- package/types/errors/http-errors.d.ts +120 -0
- package/types/errors/index.d.ts +3 -0
- package/types/errors/mega-error.d.ts +32 -0
- package/types/index.d.ts +39 -0
- package/types/lib/asp/config.d.ts +49 -0
- package/types/lib/asp/crypto.d.ts +43 -0
- package/types/lib/asp/errors.d.ts +30 -0
- package/types/lib/asp/nonce-cache.d.ts +52 -0
- package/types/lib/asp/plugin.d.ts +30 -0
- package/types/lib/asp/ws-terminator.d.ts +45 -0
- package/types/lib/env-mapper.d.ts +14 -0
- package/types/lib/hub-protocol.d.ts +106 -0
- package/types/lib/index.d.ts +22 -0
- package/types/lib/logger/telegram-core.d.ts +104 -0
- package/types/lib/logger/telegram-transport.d.ts +45 -0
- package/types/lib/mega-brute-force.d.ts +66 -0
- package/types/lib/mega-circuit-breaker.d.ts +241 -0
- package/types/lib/mega-cron.d.ts +66 -0
- package/types/lib/mega-hash.d.ts +32 -0
- package/types/lib/mega-health.d.ts +41 -0
- package/types/lib/mega-job-queue.d.ts +176 -0
- package/types/lib/mega-job-worker.d.ts +130 -0
- package/types/lib/mega-job.d.ts +138 -0
- package/types/lib/mega-logger.d.ts +45 -0
- package/types/lib/mega-metrics.d.ts +285 -0
- package/types/lib/mega-plugin.d.ts +245 -0
- package/types/lib/mega-retry.d.ts +85 -0
- package/types/lib/mega-schedule.d.ts +260 -0
- package/types/lib/mega-shutdown.d.ts +135 -0
- package/types/lib/mega-tracing.d.ts +224 -0
- package/types/lib/mega-worker.d.ts +127 -0
- package/types/lib/otel-resource.d.ts +16 -0
- package/types/lib/worker-runner/process-entry.d.ts +1 -0
- package/types/lib/worker-runner/task-dispatch.d.ts +28 -0
- package/types/lib/worker-runner/thread-entry.d.ts +1 -0
- package/types/lib/ws-hub.d.ts +234 -0
- package/types/models/crud-sql-builder.d.ts +48 -0
- package/types/models/index.d.ts +1 -0
- package/types/models/mega-model.d.ts +138 -0
- package/types/models/model-crud.d.ts +82 -0
- package/types/models/mongo-crud.d.ts +59 -0
- package/types/test/index.d.ts +84 -0
- package/.env +0 -127
- package/sample/crud/apps/main/migrations/20260606000002-add-auth-to-users.js +0 -30
- package/sample/crud/apps/main/models/note.js +0 -71
- package/sample/crud/apps/main/models/user.js +0 -86
- package/sample/crud/package-lock.json +0 -5665
- package/sample/crud/yarn.lock +0 -2142
- package/sample/simple/package-lock.json +0 -1851
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/** DLQ 스트림 `max_age` 디폴트(ms) — 7일. 무한 적재 방지(ADR-134). `dlqMaxAgeMs: 0` 으로 끌 수 있다. */
|
|
2
|
+
export const DEFAULT_DLQ_MAX_AGE_MS: number;
|
|
3
|
+
/**
|
|
4
|
+
* run 전체(재시도 포함) 실행 상한 디폴트(ms) — 30분. 행(hang)된 run 이 `working()` lease 를 영원히
|
|
5
|
+
* 갱신하며 메시지를 영구 점유하는 것을 막는 backstop. 잡별 `static timeoutMs` 로 override, `0` = 무제한.
|
|
6
|
+
*/
|
|
7
|
+
export const DEFAULT_RUN_TIMEOUT_MS: number;
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {Object} MegaJobQueueOptions
|
|
10
|
+
* @property {import('nats').NatsConnection} nc - **연결된** NatsConnection(`ctx.bus(alias).native`).
|
|
11
|
+
* @property {number} [ackWaitMs=30000] - consumer ack 대기(ms). 초과 시 JetStream 재전달. `working()`
|
|
12
|
+
* 하트비트가 이 값의 절반마다 lease 를 갱신한다.
|
|
13
|
+
* @property {number} [maxDeliver=5] - JetStream 최대 전달 횟수(워커 크래시 재전달 backstop).
|
|
14
|
+
* @property {number} [heartbeatMs] - `working()` 전송 주기(ms). 기본 `max(1000, ackWaitMs/2)`.
|
|
15
|
+
* @property {string} [streamPrefix='MEGA_JOBS'] - 스트림 이름 접두사.
|
|
16
|
+
* @property {number} [dlqMaxAgeMs=604800000] - DLQ 스트림 메시지 보존 기한(ms, 디폴트 7일). 초과한 실패
|
|
17
|
+
* 잡은 NATS 가 자동 만료시킨다(무한 적재 방지, ADR-134). `0` 이면 무제한(끔 — 영구 보존). **신규 DLQ
|
|
18
|
+
* 스트림 생성 시에만** 적용(멱등 — 기존 스트림은 운영자가 NATS CLI 로 갱신).
|
|
19
|
+
* @property {number} [dlqMaxBytes] - DLQ 스트림 최대 크기(bytes). 미지정이면 byte 상한 없음(`max_age` 가
|
|
20
|
+
* 주 가드). 디스크 상한이 필요한 운영 환경에서만 지정.
|
|
21
|
+
* @property {number} [runTimeoutMs=1800000] - run 전체(재시도 포함) 실행 상한 디폴트(ms, 기본 30분).
|
|
22
|
+
* 초과 시 잡을 실패로 판정해 DLQ 라우팅 — 행 잡이 `working()` lease 를 영구 갱신하며 메시지를 점유하는
|
|
23
|
+
* 것을 막는다. 잡별 `static timeoutMs` 가 우선, `0` = 무제한. ⚠️ 타임아웃돼도 진행 중 run 은 중단되지
|
|
24
|
+
* 않는다(백그라운드 계속 — run 멱등 설계 필요). 나중 실패는 fail(abandoned-run) 으로 표면화.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {Object} MegaJobHandleResult - {@link MegaJobQueue#_handleMessage} 결과(내부/테스트용).
|
|
28
|
+
* @property {boolean} ok - run 이 성공해 ack 됐는지.
|
|
29
|
+
* @property {any} [result] - run 반환값(성공 시).
|
|
30
|
+
* @property {Error} [error] - 실패 사유(실패 시).
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* JetStream 잡 큐 런타임. {@link MegaJob} 클래스를 받아 enqueue/consume·재시도·DLQ 를 처리한다.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* const queue = new MegaJobQueue({ nc: ctx.bus('jobs').native })
|
|
37
|
+
* queue.on('dlq', (e) => log.error(e, 'job moved to DLQ'))
|
|
38
|
+
* await queue.enqueue(SendEmailJob, { to: 'a@b.c' })
|
|
39
|
+
* const sub = await queue.consume(SendEmailJob, new SendEmailJob(), ctx)
|
|
40
|
+
* // graceful shutdown
|
|
41
|
+
* await sub.stop()
|
|
42
|
+
*/
|
|
43
|
+
export class MegaJobQueue extends EventEmitter<any> {
|
|
44
|
+
/**
|
|
45
|
+
* @param {MegaJobQueueOptions} options
|
|
46
|
+
* @throws {TypeError} nc 가 JetStream 가능한 NatsConnection 이 아니면(fail-fast).
|
|
47
|
+
*/
|
|
48
|
+
constructor({ nc, ackWaitMs, maxDeliver, heartbeatMs, streamPrefix, dlqMaxAgeMs, dlqMaxBytes, runTimeoutMs }?: MegaJobQueueOptions);
|
|
49
|
+
/** @param {'dispatch'|'start'|'done'|'retry'|'fail'|'dlq'} event @param {(payload: any) => void} listener @returns {this} */
|
|
50
|
+
on(event: "dispatch" | "start" | "done" | "retry" | "fail" | "dlq", listener: (payload: any) => void): this;
|
|
51
|
+
/** @param {'dispatch'|'start'|'done'|'retry'|'fail'|'dlq'} event @param {(payload: any) => void} listener @returns {this} */
|
|
52
|
+
off(event: "dispatch" | "start" | "done" | "retry" | "fail" | "dlq", listener: (payload: any) => void): this;
|
|
53
|
+
/** @param {'dispatch'|'start'|'done'|'retry'|'fail'|'dlq'} event @param {(payload: any) => void} listener @returns {this} */
|
|
54
|
+
once(event: "dispatch" | "start" | "done" | "retry" | "fail" | "dlq", listener: (payload: any) => void): this;
|
|
55
|
+
/** @param {'dispatch'|'start'|'done'|'retry'|'fail'|'dlq'} event @param {(payload: any) => void} listener @returns {this} */
|
|
56
|
+
addListener(event: "dispatch" | "start" | "done" | "retry" | "fail" | "dlq", listener: (payload: any) => void): this;
|
|
57
|
+
/** @param {'dispatch'|'start'|'done'|'retry'|'fail'|'dlq'} event @param {(payload: any) => void} listener @returns {this} */
|
|
58
|
+
removeListener(event: "dispatch" | "start" | "done" | "retry" | "fail" | "dlq", listener: (payload: any) => void): this;
|
|
59
|
+
/** @param {'dispatch'|'start'|'done'|'retry'|'fail'|'dlq'} event @param {(payload: any) => void} listener @returns {this} */
|
|
60
|
+
prependListener(event: "dispatch" | "start" | "done" | "retry" | "fail" | "dlq", listener: (payload: any) => void): this;
|
|
61
|
+
/** @param {'dispatch'|'start'|'done'|'retry'|'fail'|'dlq'} event @param {(payload: any) => void} listener @returns {this} */
|
|
62
|
+
prependOnceListener(event: "dispatch" | "start" | "done" | "retry" | "fail" | "dlq", listener: (payload: any) => void): this;
|
|
63
|
+
/**
|
|
64
|
+
* JetStream client/manager·codec 을 지연 초기화한다(멱등 — 동시 호출도 1회만). nats 드라이버는
|
|
65
|
+
* 여기서 lazy import(어댑터 정합 — 잡 미사용 앱에 nats 로드 강제 안 함).
|
|
66
|
+
* @returns {Promise<void>}
|
|
67
|
+
*/
|
|
68
|
+
ensureReady(): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* 잡 스트림(workqueue)과 DLQ 스트림(limits)을 멱등 생성한다. 이미 있으면 그대로 둔다(설정 변경은
|
|
71
|
+
* 운영 책임 — 자동 update 안 함). enqueue/consume 가 자동 호출하므로 보통 직접 부를 필요 없다.
|
|
72
|
+
* @param {typeof MegaJob} JobClass @returns {Promise<void>}
|
|
73
|
+
*/
|
|
74
|
+
ensureStream(JobClass: typeof MegaJob): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* 잡을 큐에 넣는다(JetStream publish — 서버에 영속 저장). 스트림이 없으면 만든다.
|
|
77
|
+
* @param {typeof MegaJob} JobClass @param {any} payload @returns {Promise<{ seq: number, stream: string, duplicate: boolean }>}
|
|
78
|
+
*/
|
|
79
|
+
enqueue(JobClass: typeof MegaJob, payload: any): Promise<{
|
|
80
|
+
seq: number;
|
|
81
|
+
stream: string;
|
|
82
|
+
duplicate: boolean;
|
|
83
|
+
}>;
|
|
84
|
+
/**
|
|
85
|
+
* 잡 처리를 시작한다 — durable consumer 를 만들고 메시지를 받아 `run(payload, ctx)` 를 재시도와 함께
|
|
86
|
+
* 실행한다. `static concurrency` 만큼 동시 처리한다. 반환 핸들의 `stop()` 으로 graceful 중단.
|
|
87
|
+
*
|
|
88
|
+
* (소비 워커 라이프사이클·bus 별명 배선은 `MegaJobWorker` 영역 — 본 메서드는 "처리 베이스".
|
|
89
|
+
* 정본 `MegaWorker` = CPU `worker_threads` 풀로 별개 추상(ADR-120/ADR-121).)
|
|
90
|
+
*
|
|
91
|
+
* ⚠️ **head-of-line blocking 운영 주의(Med)**: in-flight 상한은 `concurrency`(= `max_ack_pending`)다.
|
|
92
|
+
* 일시 장애로 **모든 in-flight 가 동시에 긴 백오프**(`static backoff.max` 큼 + `retries` 많음)에 들어가면
|
|
93
|
+
* 그 subject 처리량이 0 에 수렴하고 정상 메시지도 뒤에서 대기한다(메모리는 안전 — 무한 증가 X). 큰
|
|
94
|
+
* backoff 를 쓰면 `concurrency` 를 충분히 키우고, 재시도 적체를 메트릭(`job:start`/`fail` 이벤트)으로 관측하라.
|
|
95
|
+
*
|
|
96
|
+
* @param {typeof MegaJob} JobClass @param {MegaJob} instance - `run` 호출 대상(서브클래스 인스턴스).
|
|
97
|
+
* @param {Record<string, any>} ctx - run 에 넘길 컨텍스트.
|
|
98
|
+
* @returns {Promise<{ stop: () => Promise<void> }>}
|
|
99
|
+
*/
|
|
100
|
+
consume(JobClass: typeof MegaJob, instance: MegaJob, ctx: Record<string, any>): Promise<{
|
|
101
|
+
stop: () => Promise<void>;
|
|
102
|
+
}>;
|
|
103
|
+
/**
|
|
104
|
+
* 메시지 1건 처리 — decode → run(재시도) → ack | DLQ. **절대 throw 하지 않는다**(consume 루프의
|
|
105
|
+
* in-flight Promise 가 reject 되지 않게 — MegaScheduler #fire 와 동일 불변식). 내부/테스트용 seam 이라
|
|
106
|
+
* `_` 접두사(어댑터 `_connect` 컨벤션).
|
|
107
|
+
*
|
|
108
|
+
* @param {MegaJob} instance @param {Record<string, any>} ctx @param {import('nats').JsMsg} msg
|
|
109
|
+
* @param {ReturnType<typeof resolveJobRetryConfig>} retryConfig @param {string} subject
|
|
110
|
+
* @param {number} [runTimeoutMs] - run 전체(재시도 포함) 상한(ms). 미지정 시 큐 디폴트, 0 = 무제한.
|
|
111
|
+
* @returns {Promise<MegaJobHandleResult>}
|
|
112
|
+
*/
|
|
113
|
+
_handleMessage(instance: MegaJob, ctx: Record<string, any>, msg: import("nats").JsMsg, retryConfig: ReturnType<typeof resolveJobRetryConfig>, subject: string, runTimeoutMs?: number): Promise<MegaJobHandleResult>;
|
|
114
|
+
#private;
|
|
115
|
+
}
|
|
116
|
+
export type MegaJobQueueOptions = {
|
|
117
|
+
/**
|
|
118
|
+
* - **연결된** NatsConnection(`ctx.bus(alias).native`).
|
|
119
|
+
*/
|
|
120
|
+
nc: import("nats").NatsConnection;
|
|
121
|
+
/**
|
|
122
|
+
* - consumer ack 대기(ms). 초과 시 JetStream 재전달. `working()`
|
|
123
|
+
* 하트비트가 이 값의 절반마다 lease 를 갱신한다.
|
|
124
|
+
*/
|
|
125
|
+
ackWaitMs?: number;
|
|
126
|
+
/**
|
|
127
|
+
* - JetStream 최대 전달 횟수(워커 크래시 재전달 backstop).
|
|
128
|
+
*/
|
|
129
|
+
maxDeliver?: number;
|
|
130
|
+
/**
|
|
131
|
+
* - `working()` 전송 주기(ms). 기본 `max(1000, ackWaitMs/2)`.
|
|
132
|
+
*/
|
|
133
|
+
heartbeatMs?: number;
|
|
134
|
+
/**
|
|
135
|
+
* - 스트림 이름 접두사.
|
|
136
|
+
*/
|
|
137
|
+
streamPrefix?: string;
|
|
138
|
+
/**
|
|
139
|
+
* - DLQ 스트림 메시지 보존 기한(ms, 디폴트 7일). 초과한 실패
|
|
140
|
+
* 잡은 NATS 가 자동 만료시킨다(무한 적재 방지, ADR-134). `0` 이면 무제한(끔 — 영구 보존). **신규 DLQ
|
|
141
|
+
* 스트림 생성 시에만** 적용(멱등 — 기존 스트림은 운영자가 NATS CLI 로 갱신).
|
|
142
|
+
*/
|
|
143
|
+
dlqMaxAgeMs?: number;
|
|
144
|
+
/**
|
|
145
|
+
* - DLQ 스트림 최대 크기(bytes). 미지정이면 byte 상한 없음(`max_age` 가
|
|
146
|
+
* 주 가드). 디스크 상한이 필요한 운영 환경에서만 지정.
|
|
147
|
+
*/
|
|
148
|
+
dlqMaxBytes?: number;
|
|
149
|
+
/**
|
|
150
|
+
* - run 전체(재시도 포함) 실행 상한 디폴트(ms, 기본 30분).
|
|
151
|
+
* 초과 시 잡을 실패로 판정해 DLQ 라우팅 — 행 잡이 `working()` lease 를 영구 갱신하며 메시지를 점유하는
|
|
152
|
+
* 것을 막는다. 잡별 `static timeoutMs` 가 우선, `0` = 무제한. ⚠️ 타임아웃돼도 진행 중 run 은 중단되지
|
|
153
|
+
* 않는다(백그라운드 계속 — run 멱등 설계 필요). 나중 실패는 fail(abandoned-run) 으로 표면화.
|
|
154
|
+
*/
|
|
155
|
+
runTimeoutMs?: number;
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* - {@link MegaJobQueue#_handleMessage} 결과(내부/테스트용).
|
|
159
|
+
*/
|
|
160
|
+
export type MegaJobHandleResult = {
|
|
161
|
+
/**
|
|
162
|
+
* - run 이 성공해 ack 됐는지.
|
|
163
|
+
*/
|
|
164
|
+
ok: boolean;
|
|
165
|
+
/**
|
|
166
|
+
* - run 반환값(성공 시).
|
|
167
|
+
*/
|
|
168
|
+
result?: any;
|
|
169
|
+
/**
|
|
170
|
+
* - 실패 사유(실패 시).
|
|
171
|
+
*/
|
|
172
|
+
error?: Error;
|
|
173
|
+
};
|
|
174
|
+
import { EventEmitter } from 'node:events';
|
|
175
|
+
import { MegaJob } from './mega-job.js';
|
|
176
|
+
import { resolveJobRetryConfig } from './mega-job.js';
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} JobEntry - 등록된 잡 1건의 내부 메타데이터.
|
|
3
|
+
* @property {string} name - 잡 클래스명(= 등록 키).
|
|
4
|
+
* @property {typeof MegaJob} JobClass - 등록된 잡 클래스.
|
|
5
|
+
* @property {MegaJob} instance - run 호출 대상 인스턴스.
|
|
6
|
+
* @property {string} subject - NATS subject.
|
|
7
|
+
* @property {string} busAlias - bus 별명(`ctx.bus(alias)`).
|
|
8
|
+
* @property {{ stop: () => Promise<void> }|null} handle - start() 가 만든 consume 핸들(미 start 면 null).
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* MegaJob 소비 워커 런타임. 잡 클래스를 등록하고 `start()` 로 소비를 시작, `stop()` 으로 graceful 중단한다.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const worker = new MegaJobWorker({ ctx }) // ctx.bus(alias).native 로 nc 해석
|
|
15
|
+
* worker.on('dlq', (e) => log.error(e, 'job moved to DLQ'))
|
|
16
|
+
* worker.register(SendEmailJob).register(ResizeImageJob)
|
|
17
|
+
* await worker.start()
|
|
18
|
+
* MegaShutdown.register('worker', async () => { await worker.stop() }) // graceful shutdown 통합
|
|
19
|
+
*/
|
|
20
|
+
export class MegaJobWorker extends EventEmitter<any> {
|
|
21
|
+
/**
|
|
22
|
+
* @param {object} args
|
|
23
|
+
* @param {Record<string, any>} args.ctx - 실행 컨텍스트. **반드시 `ctx.bus(alias)`(함수)** 가 있어야
|
|
24
|
+
* 하며(잡 bus 별명 해석), run(payload, ctx) 에도 그대로 전달된다.
|
|
25
|
+
* @param {Partial<Omit<import('./mega-job-queue.js').MegaJobQueueOptions, 'nc'>>} [args.queueOptions]
|
|
26
|
+
* - MegaJobQueue 옵션(`ackWaitMs`/`maxDeliver`/`heartbeatMs`/`streamPrefix`/`dlqMaxAgeMs`/`dlqMaxBytes`).
|
|
27
|
+
* `nc` 는 워커가 배선하므로 제외. DLQ 한도(`dlqMaxAgeMs` 디폴트 7일)는 그대로 큐로 전달된다(ADR-134).
|
|
28
|
+
* @throws {TypeError} ctx 또는 ctx.bus 가 없을 때(fail-fast — bus 해석 불가).
|
|
29
|
+
*/
|
|
30
|
+
constructor({ ctx, queueOptions }?: {
|
|
31
|
+
ctx: Record<string, any>;
|
|
32
|
+
queueOptions?: Partial<Omit<import("./mega-job-queue.js").MegaJobQueueOptions, "nc">>;
|
|
33
|
+
});
|
|
34
|
+
/** @param {'dispatch'|'start'|'done'|'retry'|'fail'|'dlq'} event @param {(payload: any) => void} listener @returns {this} */
|
|
35
|
+
on(event: "dispatch" | "start" | "done" | "retry" | "fail" | "dlq", listener: (payload: any) => void): this;
|
|
36
|
+
/** @param {'dispatch'|'start'|'done'|'retry'|'fail'|'dlq'} event @param {(payload: any) => void} listener @returns {this} */
|
|
37
|
+
off(event: "dispatch" | "start" | "done" | "retry" | "fail" | "dlq", listener: (payload: any) => void): this;
|
|
38
|
+
/** @param {'dispatch'|'start'|'done'|'retry'|'fail'|'dlq'} event @param {(payload: any) => void} listener @returns {this} */
|
|
39
|
+
once(event: "dispatch" | "start" | "done" | "retry" | "fail" | "dlq", listener: (payload: any) => void): this;
|
|
40
|
+
/** @param {'dispatch'|'start'|'done'|'retry'|'fail'|'dlq'} event @param {(payload: any) => void} listener @returns {this} */
|
|
41
|
+
addListener(event: "dispatch" | "start" | "done" | "retry" | "fail" | "dlq", listener: (payload: any) => void): this;
|
|
42
|
+
/** @param {'dispatch'|'start'|'done'|'retry'|'fail'|'dlq'} event @param {(payload: any) => void} listener @returns {this} */
|
|
43
|
+
removeListener(event: "dispatch" | "start" | "done" | "retry" | "fail" | "dlq", listener: (payload: any) => void): this;
|
|
44
|
+
/** @param {'dispatch'|'start'|'done'|'retry'|'fail'|'dlq'} event @param {(payload: any) => void} listener @returns {this} */
|
|
45
|
+
prependListener(event: "dispatch" | "start" | "done" | "retry" | "fail" | "dlq", listener: (payload: any) => void): this;
|
|
46
|
+
/** @param {'dispatch'|'start'|'done'|'retry'|'fail'|'dlq'} event @param {(payload: any) => void} listener @returns {this} */
|
|
47
|
+
prependOnceListener(event: "dispatch" | "start" | "done" | "retry" | "fail" | "dlq", listener: (payload: any) => void): this;
|
|
48
|
+
/**
|
|
49
|
+
* 잡 클래스를 등록한다(아직 소비 안 함 — {@link MegaJobWorker#start} 가 consume 을 건다). subject·bus 를
|
|
50
|
+
* 즉시 검증해 잘못된 등록을 부팅 시점에 드러낸다(fail-fast — MegaScheduler.register 패턴).
|
|
51
|
+
*
|
|
52
|
+
* @param {typeof MegaJob} JobClass - `MegaJob` 를 상속한 클래스.
|
|
53
|
+
* @returns {this} 체이닝용.
|
|
54
|
+
* @throws {TypeError} MegaJob 서브클래스가 아니거나 subject 가 비었을 때.
|
|
55
|
+
* @throws {Error} 이름 중복일 때.
|
|
56
|
+
* @throws {MegaConfigError} `static bus` 가 비었거나 미선언 별명일 때.
|
|
57
|
+
*/
|
|
58
|
+
register(JobClass: typeof MegaJob): this;
|
|
59
|
+
/**
|
|
60
|
+
* 등록된 모든 잡의 소비를 시작한다(각 잡의 bus 큐로 `consume`). 이미 start 된 잡은 건너뛴다. 멱등.
|
|
61
|
+
* @returns {Promise<this>}
|
|
62
|
+
*/
|
|
63
|
+
start(): Promise<this>;
|
|
64
|
+
/**
|
|
65
|
+
* 모든 잡 소비를 graceful 중단한다(consume 핸들 stop — 진행 중 처리는 마무리까지 대기). graceful
|
|
66
|
+
* shutdown 필수(`MegaShutdown.register('worker', () => worker.stop())`). 한 잡의 stop 실패가 나머지
|
|
67
|
+
* 정리를 막지 않도록 allSettled 로 모은다(방어). 단, 하부 `MegaJobQueue` 의 consume 핸들 `stop()` 은
|
|
68
|
+
* `ConsumerMessages.close()` 가 reject 하지 않는 설계라(nats 2.29: drain 오류를 `Promise<void|Error>` 로
|
|
69
|
+
* resolve) close 에러를 **큐 이벤트가 아니라 console(stderr)로 표면화**한다(L-3). 즉 allSettled 의
|
|
70
|
+
* rejected 슬롯은 정상 경로에선 비고, 방어적 안전망으로만 둔다.
|
|
71
|
+
* @returns {Promise<this>}
|
|
72
|
+
*/
|
|
73
|
+
stop(): Promise<this>;
|
|
74
|
+
/**
|
|
75
|
+
* 잡을 큐에 넣는다(편의 위임 — producer 측). 워커가 해당 bus 큐를 통해 enqueue 한다.
|
|
76
|
+
* @param {typeof MegaJob} JobClass @param {any} payload
|
|
77
|
+
* @returns {Promise<{ seq: number, stream: string, duplicate: boolean }>}
|
|
78
|
+
*/
|
|
79
|
+
enqueue(JobClass: typeof MegaJob, payload: any): Promise<{
|
|
80
|
+
seq: number;
|
|
81
|
+
stream: string;
|
|
82
|
+
duplicate: boolean;
|
|
83
|
+
}>;
|
|
84
|
+
/**
|
|
85
|
+
* 등록된 잡 메타데이터 목록. 모니터링/CLI 용.
|
|
86
|
+
* @returns {Array<{ name: string, subject: string, bus: string, consuming: boolean }>}
|
|
87
|
+
*/
|
|
88
|
+
list(): Array<{
|
|
89
|
+
name: string;
|
|
90
|
+
subject: string;
|
|
91
|
+
bus: string;
|
|
92
|
+
consuming: boolean;
|
|
93
|
+
}>;
|
|
94
|
+
/** @returns {boolean} start() 된 상태면 true. */
|
|
95
|
+
get isStarted(): boolean;
|
|
96
|
+
#private;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* - 등록된 잡 1건의 내부 메타데이터.
|
|
100
|
+
*/
|
|
101
|
+
export type JobEntry = {
|
|
102
|
+
/**
|
|
103
|
+
* - 잡 클래스명(= 등록 키).
|
|
104
|
+
*/
|
|
105
|
+
name: string;
|
|
106
|
+
/**
|
|
107
|
+
* - 등록된 잡 클래스.
|
|
108
|
+
*/
|
|
109
|
+
JobClass: typeof MegaJob;
|
|
110
|
+
/**
|
|
111
|
+
* - run 호출 대상 인스턴스.
|
|
112
|
+
*/
|
|
113
|
+
instance: MegaJob;
|
|
114
|
+
/**
|
|
115
|
+
* - NATS subject.
|
|
116
|
+
*/
|
|
117
|
+
subject: string;
|
|
118
|
+
/**
|
|
119
|
+
* - bus 별명(`ctx.bus(alias)`).
|
|
120
|
+
*/
|
|
121
|
+
busAlias: string;
|
|
122
|
+
/**
|
|
123
|
+
* - start() 가 만든 consume 핸들(미 start 면 null).
|
|
124
|
+
*/
|
|
125
|
+
handle: {
|
|
126
|
+
stop: () => Promise<void>;
|
|
127
|
+
} | null;
|
|
128
|
+
};
|
|
129
|
+
import { EventEmitter } from 'node:events';
|
|
130
|
+
import { MegaJob } from './mega-job.js';
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 잡 클래스의 `static retries`/`static backoff` 를 {@link import('./mega-retry.js').MegaRetryOptions}
|
|
3
|
+
* 형식으로 매핑한다(03-api-spec 필드 → p-retry 옵션). 미지정 필드는 OQ-012 디폴트로 채운다.
|
|
4
|
+
*
|
|
5
|
+
* @param {typeof MegaJob} JobClass - 잡 클래스.
|
|
6
|
+
* @returns {{ retries: number, minTimeout: number, maxTimeout: number, factor: number, jitter: boolean }}
|
|
7
|
+
* @throws {TypeError} retries 가 음수/비정수이거나 backoff 형식이 무효일 때(fail-fast).
|
|
8
|
+
*/
|
|
9
|
+
export function resolveJobRetryConfig(JobClass: typeof MegaJob): {
|
|
10
|
+
retries: number;
|
|
11
|
+
minTimeout: number;
|
|
12
|
+
maxTimeout: number;
|
|
13
|
+
factor: number;
|
|
14
|
+
jitter: boolean;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* 잡 클래스의 `static timeoutMs` 를 검증해 반환한다. 미지정(undefined/null)이면 `defaultMs`(큐 디폴트),
|
|
18
|
+
* `0` = 무제한. 음수/비정수는 운영 실수라 fail-fast(silent 보정 금지).
|
|
19
|
+
*
|
|
20
|
+
* @param {typeof MegaJob} JobClass - 잡 클래스.
|
|
21
|
+
* @param {number} defaultMs - 큐 레벨 디폴트(ms, 0 = 무제한).
|
|
22
|
+
* @returns {number} 적용할 타임아웃(ms). 0 = 무제한.
|
|
23
|
+
* @throws {TypeError} timeoutMs 가 0 이상 정수가 아닐 때.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveJobRunTimeoutMs(JobClass: typeof MegaJob, defaultMs: number): number;
|
|
26
|
+
/**
|
|
27
|
+
* MegaJob — NATS JetStream 기반 영속 잡 베이스 클래스 (ADR-119).
|
|
28
|
+
*
|
|
29
|
+
* 03-api-spec §6 의 "클래스 + static 설정" 패턴(MegaSchedule/MegaWorker 와 동형). 서브클래스가
|
|
30
|
+
* static 설정과 `async run(payload, ctx)` 를 정의하면, 실행 런타임 {@link MegaJobQueue}(JetStream
|
|
31
|
+
* wrapper)가 enqueue/consume·재시도·DLQ 를 담당한다.
|
|
32
|
+
*
|
|
33
|
+
* # 왜 JetStream 인가 (중학생용)
|
|
34
|
+
* 보통 메시지(core NATS)는 받을 사람이 그 순간 없으면 사라진다(at-most-once). 잡은 "꼭 한 번은
|
|
35
|
+
* 처리돼야" 하므로, 서버에 **저장(persist)** 해두고 워커가 가져가 처리한 뒤 "처리 완료(ack)" 를
|
|
36
|
+
* 보내야 지워지는 큐가 필요하다 — 그게 JetStream 의 **workqueue** 스트림이다. 여러 워커가 같은 큐를
|
|
37
|
+
* 봐도 메시지 1건은 **딱 한 워커**에게만 간다(분산 중복방지 = OQ-012 의 leader election 을 큐가 대신).
|
|
38
|
+
*
|
|
39
|
+
* # 재시도 → DLQ (MegaRetry 재사용)
|
|
40
|
+
* `run()` 이 실패하면 {@link MegaJobQueue} 가 `MegaRetry`(p-retry)로 `static retries`·
|
|
41
|
+
* `static backoff` 설정만큼 **인프로세스 지수 백오프 재시도**한다. 그래도 모두 실패하면 메시지를
|
|
42
|
+
* **DLQ(Dead Letter Queue) subject `<subject>.dlq`** 로 보내 따로 보관한다(원인 분석·재처리용).
|
|
43
|
+
*
|
|
44
|
+
* # 설정 필드 (03-api-spec §6 정본)
|
|
45
|
+
* - `static subject` : 잡 NATS subject(필수). 워크 스트림이 이 subject 를 저장한다.
|
|
46
|
+
* - `static bus` : bus 별명(`ctx.bus(alias)` → MegaNatsAdapter). 워커 배선이 사용.
|
|
47
|
+
* - `static concurrency` : 동시에 처리할 메시지 수(consumer `max_ack_pending`). 기본 1(순차·안전).
|
|
48
|
+
* - `static retries` : run 실패 시 **추가** 재시도 횟수(첫 시도 제외). 기본 3.
|
|
49
|
+
* - `static backoff` : `{ type:'exponential', initial, max }`. p-retry 로 매핑(factor=2, jitter=on).
|
|
50
|
+
* - `static timeoutMs` : run 전체(재시도 포함) 상한(ms). 미지정 시 큐 디폴트, `0` = 무제한.
|
|
51
|
+
*
|
|
52
|
+
* @module lib/mega-job
|
|
53
|
+
* @see ADR-119, ADR-028 (잡·스케줄러·워커 3종 분리), ADR-029 (라이브러리 래핑)
|
|
54
|
+
*/
|
|
55
|
+
/**
|
|
56
|
+
* 잡 백오프 설정. 03-api-spec §6 의 `static backoff` 형식. `type` 은 현재 `'exponential'` 만 지원한다
|
|
57
|
+
* (미지원 type 은 {@link resolveJobRetryConfig} 에서 throw — 추측 진행 금지).
|
|
58
|
+
*
|
|
59
|
+
* @typedef {Object} MegaJobBackoff
|
|
60
|
+
* @property {string} type - 백오프 종류. **현재 `'exponential'` 만 지원**(미지원 type 은
|
|
61
|
+
* {@link resolveJobRetryConfig} 가 런타임 throw — 타입은 서브클래스 리터럴 오버라이드 편의를 위해
|
|
62
|
+
* 넓게 두고 검증은 런타임이 담당).
|
|
63
|
+
* @property {number} initial - 첫 재시도 지연(ms) = p-retry `minTimeout`.
|
|
64
|
+
* @property {number} max - 재시도 지연 상한(ms) = p-retry `maxTimeout`.
|
|
65
|
+
*/
|
|
66
|
+
/**
|
|
67
|
+
* 잡 큐 실패·재시도·DLQ 기본값(OQ-012 결정, ADR-119). p-retry(MegaRetry) 정합 디폴트:
|
|
68
|
+
* 추가 재시도 3회, 지수 factor 2, 첫 지연 1s, 상한 30s, jitter on(thundering herd 완화).
|
|
69
|
+
* @type {{ retries: 3, backoff: MegaJobBackoff }}
|
|
70
|
+
*/
|
|
71
|
+
export const JOB_RETRY_DEFAULTS: {
|
|
72
|
+
retries: 3;
|
|
73
|
+
backoff: MegaJobBackoff;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* NATS JetStream 기반 영속 잡 베이스 클래스. 서브클래스가 static 설정과 `run(payload, ctx)` 를 정의한다.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* export class SendEmailJob extends MegaJob {
|
|
80
|
+
* static subject = 'send-email'
|
|
81
|
+
* static bus = 'jobs'
|
|
82
|
+
* static concurrency = 5
|
|
83
|
+
* static retries = 3
|
|
84
|
+
* static backoff = { type: 'exponential', initial: 1000, max: 30_000 }
|
|
85
|
+
* async run(payload, ctx) {
|
|
86
|
+
* await ctx.cache('main').set(`sent:${payload.id}`, '1')
|
|
87
|
+
* }
|
|
88
|
+
* }
|
|
89
|
+
*/
|
|
90
|
+
export class MegaJob {
|
|
91
|
+
/** @type {string|undefined} 잡 NATS subject(필수 — 미정의 시 큐 등록에서 throw). */
|
|
92
|
+
static subject: string | undefined;
|
|
93
|
+
/** @type {string|undefined} bus 별명(`ctx.bus(alias)`). 워커 배선이 nc 해석에 사용. */
|
|
94
|
+
static bus: string | undefined;
|
|
95
|
+
/** @type {number} 동시 처리 메시지 수(consumer max_ack_pending). 기본 1(순차·안전). */
|
|
96
|
+
static concurrency: number;
|
|
97
|
+
/** @type {number} run 실패 시 **추가** 재시도 횟수(첫 시도 제외). 기본 3(OQ-012). */
|
|
98
|
+
static retries: number;
|
|
99
|
+
/** @type {MegaJobBackoff} 지수 백오프 설정. 기본 { exponential, 1s, 30s }(OQ-012). */
|
|
100
|
+
static backoff: MegaJobBackoff;
|
|
101
|
+
/**
|
|
102
|
+
* @type {number|undefined} run 전체(재시도 포함) 실행 상한(ms). 초과 시 큐가 잡을 실패로 판정해 DLQ 로
|
|
103
|
+
* 보낸다 — 행(hang)된 run 이 `working()` lease 를 영원히 갱신하며 메시지를 영구 점유하는 것을 막는다.
|
|
104
|
+
* 미지정 시 {@link import('./mega-job-queue.js').MegaJobQueue} 의 `runTimeoutMs`(기본 30분), `0` = 무제한.
|
|
105
|
+
* ⚠️ 타임아웃돼도 진행 중이던 run 은 JS 특성상 중단되지 않는다(백그라운드 계속) — run 은 멱등하게
|
|
106
|
+
* 설계해야 한다(at-least-once, 모듈 docstring).
|
|
107
|
+
*/
|
|
108
|
+
static timeoutMs: number | undefined;
|
|
109
|
+
/**
|
|
110
|
+
* 메시지 1건마다 실행되는 본문. 서브클래스가 **반드시** 구현한다. throw 하면 {@link MegaJobQueue}
|
|
111
|
+
* 가 재시도하고, 재시도 소진 시 DLQ 로 보낸다 — 그러므로 비치명/일시 오류는 그냥 throw 하면 된다.
|
|
112
|
+
* @param {any} _payload - enqueue 된 잡 페이로드(JSON 디코드된 값).
|
|
113
|
+
* @param {Record<string, any>} _ctx - 실행 컨텍스트(`db/cache/bus/lock` 접근자 등).
|
|
114
|
+
* @returns {Promise<any>}
|
|
115
|
+
* @throws {Error} 서브클래스가 구현하지 않으면.
|
|
116
|
+
*/
|
|
117
|
+
run(_payload: any, _ctx: Record<string, any>): Promise<any>;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* 잡 백오프 설정. 03-api-spec §6 의 `static backoff` 형식. `type` 은 현재 `'exponential'` 만 지원한다
|
|
121
|
+
* (미지원 type 은 {@link resolveJobRetryConfig} 에서 throw — 추측 진행 금지).
|
|
122
|
+
*/
|
|
123
|
+
export type MegaJobBackoff = {
|
|
124
|
+
/**
|
|
125
|
+
* - 백오프 종류. **현재 `'exponential'` 만 지원**(미지원 type 은
|
|
126
|
+
* {@link resolveJobRetryConfig} 가 런타임 throw — 타입은 서브클래스 리터럴 오버라이드 편의를 위해
|
|
127
|
+
* 넓게 두고 검증은 런타임이 담당).
|
|
128
|
+
*/
|
|
129
|
+
type: string;
|
|
130
|
+
/**
|
|
131
|
+
* - 첫 재시도 지연(ms) = p-retry `minTimeout`.
|
|
132
|
+
*/
|
|
133
|
+
initial: number;
|
|
134
|
+
/**
|
|
135
|
+
* - 재시도 지연 상한(ms) = p-retry `maxTimeout`.
|
|
136
|
+
*/
|
|
137
|
+
max: number;
|
|
138
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `logger` config 의 sink 배열을 pino transport target 배열로 변환한다(순수 — 테스트 가능).
|
|
3
|
+
*
|
|
4
|
+
* @param {Array<Record<string, any>>} sinks - `[{ type:'console'|'file'|'telegram', ... }]`.
|
|
5
|
+
* @param {string} level - 전역 레벨(sink 가 자기 level 미지정 시 사용).
|
|
6
|
+
* @returns {Array<{ target: string, level: string, options: Record<string, any> }>}
|
|
7
|
+
*/
|
|
8
|
+
export function buildTargets(sinks: Array<Record<string, any>>, level: string): Array<{
|
|
9
|
+
target: string;
|
|
10
|
+
level: string;
|
|
11
|
+
options: Record<string, any>;
|
|
12
|
+
}>;
|
|
13
|
+
/**
|
|
14
|
+
* `logger` config → pino 옵션(또는 비활성 시 `null`). Fastify `logger` 또는 `pino()` 에 그대로 전달 가능.
|
|
15
|
+
*
|
|
16
|
+
* @param {unknown} config - `MegaLoggerConfig`(`{ level, sinks, redact? }`).
|
|
17
|
+
* @returns {{ level: string, mixin: Function, redact: { paths: string[] }, transport: { targets: any[] } } | null}
|
|
18
|
+
*/
|
|
19
|
+
export function buildLoggerOptions(config: unknown): {
|
|
20
|
+
level: string;
|
|
21
|
+
mixin: Function;
|
|
22
|
+
redact: {
|
|
23
|
+
paths: string[];
|
|
24
|
+
};
|
|
25
|
+
transport: {
|
|
26
|
+
targets: any[];
|
|
27
|
+
};
|
|
28
|
+
} | null;
|
|
29
|
+
/**
|
|
30
|
+
* `logger` config 로 pino 인스턴스를 만든다(비활성이면 `null`). bootApp 이 한 번 만들어 모든 앱이 공유한다
|
|
31
|
+
* (worker thread·파일 핸들 1벌). Fastify 는 인스턴스를 받으면 요청별 child(reqId 바인딩)를 만든다.
|
|
32
|
+
*
|
|
33
|
+
* @param {unknown} config - `MegaLoggerConfig`.
|
|
34
|
+
* @returns {import('pino').Logger | null}
|
|
35
|
+
*/
|
|
36
|
+
export function buildLogger(config: unknown): import("pino").Logger | null;
|
|
37
|
+
/** sink 디폴트 — 텔레그램은 warn 이상(ADR-023). */
|
|
38
|
+
export const TELEGRAM_DEFAULT_LEVEL: "warn";
|
|
39
|
+
/**
|
|
40
|
+
* 기본 시크릿 redact 경로(ADR-023, H2 보안). 사용자가 `logger.redact` 를 지정하지 않아도 **항상** 적용해
|
|
41
|
+
* 토큰·비밀번호·인증 헤더가 stdout/파일/텔레그램(외부 sink)으로 평문 유출되는 것을 막는다(CLAUDE.md P5).
|
|
42
|
+
* pino(fast-redact) 경로 문법으로 부팅 시 1회 검증됨(leading wildcard `*.x` 포함). 사용자 redact 는 병합된다.
|
|
43
|
+
* @type {ReadonlyArray<string>}
|
|
44
|
+
*/
|
|
45
|
+
export const DEFAULT_REDACT_PATHS: ReadonlyArray<string>;
|