@taicode/common-server 3.7.12 → 3.8.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/output/app/assemble.d.ts +16 -0
- package/output/app/assemble.d.ts.map +1 -0
- package/output/app/assemble.js +124 -0
- package/output/app/database.d.ts +36 -0
- package/output/app/database.d.ts.map +1 -0
- package/output/app/database.js +40 -0
- package/output/app/database.test.d.ts +2 -0
- package/output/app/database.test.d.ts.map +1 -0
- package/output/app/database.test.js +66 -0
- package/output/app/default-modules.d.ts +23 -0
- package/output/app/default-modules.d.ts.map +1 -0
- package/output/app/default-modules.js +40 -0
- package/output/app/define-server.d.ts +12 -0
- package/output/app/define-server.d.ts.map +1 -0
- package/output/app/define-server.js +289 -0
- package/output/app/define-server.test.d.ts +2 -0
- package/output/app/define-server.test.d.ts.map +1 -0
- package/output/app/define-server.test.js +299 -0
- package/output/app/env.d.ts +24 -0
- package/output/app/env.d.ts.map +1 -0
- package/output/app/env.js +38 -0
- package/output/app/env.test.d.ts +2 -0
- package/output/app/env.test.d.ts.map +1 -0
- package/output/app/env.test.js +78 -0
- package/output/app/index.d.ts +16 -0
- package/output/app/index.d.ts.map +1 -0
- package/output/app/index.js +9 -0
- package/output/app/module.d.ts +88 -0
- package/output/app/module.d.ts.map +1 -0
- package/output/app/module.js +34 -0
- package/output/app/shutdown.d.ts +9 -0
- package/output/app/shutdown.d.ts.map +1 -0
- package/output/app/shutdown.js +15 -0
- package/output/app/shutdown.test.d.ts +2 -0
- package/output/app/shutdown.test.d.ts.map +1 -0
- package/output/app/shutdown.test.js +149 -0
- package/output/app/signal.d.ts +12 -0
- package/output/app/signal.d.ts.map +1 -0
- package/output/app/signal.js +45 -0
- package/output/app/steps.d.ts +17 -0
- package/output/app/steps.d.ts.map +1 -0
- package/output/app/steps.js +41 -0
- package/output/app/types.d.ts +149 -0
- package/output/app/types.d.ts.map +1 -0
- package/output/app/types.js +1 -0
- package/output/rate-limiter/rate-limiter.d.ts.map +1 -1
- package/output/redis-cache/redis-cache.d.ts +3 -2
- package/output/redis-cache/redis-cache.d.ts.map +1 -1
- package/output/redis-cache/redis-cache.js +0 -7
- package/output/redis-queue/redis-queue-batch-consumer.d.ts +2 -2
- package/output/redis-queue/redis-queue-batch-consumer.d.ts.map +1 -1
- package/output/redis-queue/redis-queue-batch-consumer.js +5 -6
- package/output/redis-queue/redis-queue-batch-consumer.test.d.ts +1 -1
- package/output/redis-queue/redis-queue-batch-consumer.test.d.ts.map +1 -1
- package/output/redis-queue/redis-queue-batch-consumer.test.js +1 -3
- package/output/redis-queue/redis-queue-common.d.ts.map +1 -1
- package/output/redis-queue/redis-queue-common.test.d.ts +17 -13
- package/output/redis-queue/redis-queue-common.test.d.ts.map +1 -1
- package/output/redis-queue/redis-queue-consumer.d.ts +2 -2
- package/output/redis-queue/redis-queue-consumer.d.ts.map +1 -1
- package/output/redis-queue/redis-queue-consumer.js +2 -3
- package/output/redis-queue/redis-queue-consumer.test.d.ts +1 -1
- package/output/redis-queue/redis-queue-consumer.test.d.ts.map +1 -1
- package/output/redis-queue/redis-queue-consumer.test.js +1 -1
- package/output/redis-queue/redis-queue-provider.d.ts +2 -2
- package/output/redis-queue/redis-queue-provider.d.ts.map +1 -1
- package/output/redis-queue/redis-queue-provider.js +1 -1
- package/output/redis-queue/redis-queue-provider.test.d.ts +2 -2
- package/output/redis-queue/redis-queue-provider.test.d.ts.map +1 -1
- package/output/redis-queue/redis-queue-provider.test.js +42 -42
- package/output/redis-queue/test-helpers.d.ts +11 -14
- package/output/redis-queue/test-helpers.d.ts.map +1 -1
- package/output/redis-queue/test-helpers.js +9 -23
- package/output/redis-queue/test-helpers.test.d.ts +1 -1
- package/output/redis-queue/test-helpers.test.d.ts.map +1 -1
- package/output/redis-queue/test-helpers.test.js +7 -13
- package/output/redis-queue/types.d.ts +7 -0
- package/output/redis-queue/types.d.ts.map +1 -1
- package/output/schema/index.d.ts +1 -1
- package/output/schema/index.d.ts.map +1 -1
- package/package.json +14 -3
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { defineServer } from './define-server.js';
|
|
3
|
+
import { SHUTDOWN_ORDER } from './shutdown.js';
|
|
4
|
+
const P1_OPT_OUT = {
|
|
5
|
+
context: false,
|
|
6
|
+
response: false,
|
|
7
|
+
cors: false,
|
|
8
|
+
schema: false,
|
|
9
|
+
'rate-limit': false,
|
|
10
|
+
auth: false,
|
|
11
|
+
openapi: false,
|
|
12
|
+
database: false,
|
|
13
|
+
};
|
|
14
|
+
const DUMMY_ERRORS = {
|
|
15
|
+
UserError: class extends Error {
|
|
16
|
+
},
|
|
17
|
+
SystemError: class extends Error {
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
function createTestLog() {
|
|
21
|
+
const errors = [];
|
|
22
|
+
const log = {
|
|
23
|
+
debug: () => undefined,
|
|
24
|
+
info: () => undefined,
|
|
25
|
+
warn: () => undefined,
|
|
26
|
+
error: (obj) => {
|
|
27
|
+
errors.push(obj);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
return { log, errors };
|
|
31
|
+
}
|
|
32
|
+
function createBaseOptions(overrides = {}) {
|
|
33
|
+
return {
|
|
34
|
+
serviceName: 'test',
|
|
35
|
+
title: 'Test',
|
|
36
|
+
description: 'Test service',
|
|
37
|
+
errors: DUMMY_ERRORS,
|
|
38
|
+
config: { loadConfig: () => ({ debug: false, port: 0 }) },
|
|
39
|
+
modules: { ...P1_OPT_OUT },
|
|
40
|
+
serverOptions: { port: 0, host: '127.0.0.1' },
|
|
41
|
+
signal: false,
|
|
42
|
+
createServices: () => ({}),
|
|
43
|
+
registerRouters: () => undefined,
|
|
44
|
+
...overrides,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function createRecordingShutdownModule(record, throwOn) {
|
|
48
|
+
const createStep = (name) => () => {
|
|
49
|
+
record.push(name);
|
|
50
|
+
if (name === throwOn) {
|
|
51
|
+
throw new Error(`boom:${name}`);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const shutdown = {
|
|
55
|
+
stopProducers: createStep('stopProducers'),
|
|
56
|
+
unsubscribeConfig: createStep('unsubscribeConfig'),
|
|
57
|
+
disposeConfig: createStep('disposeConfig'),
|
|
58
|
+
disposeServices: createStep('disposeServices'),
|
|
59
|
+
disposeClients: createStep('disposeClients'),
|
|
60
|
+
disposeRedis: createStep('disposeRedis'),
|
|
61
|
+
disposeDb: createStep('disposeDb'),
|
|
62
|
+
};
|
|
63
|
+
return { name: 'env', shutdown };
|
|
64
|
+
}
|
|
65
|
+
describe('关闭 7 步', () => {
|
|
66
|
+
it('顺序写死、beforeShutdown 在最前', async () => {
|
|
67
|
+
const record = [];
|
|
68
|
+
const app = await defineServer(createBaseOptions({
|
|
69
|
+
modules: { ...P1_OPT_OUT, env: createRecordingShutdownModule(record) },
|
|
70
|
+
beforeShutdown: () => {
|
|
71
|
+
record.push('beforeShutdown');
|
|
72
|
+
},
|
|
73
|
+
}));
|
|
74
|
+
await app.close();
|
|
75
|
+
expect(record).toEqual(['beforeShutdown', ...SHUTDOWN_ORDER]);
|
|
76
|
+
});
|
|
77
|
+
it('单步抛错不阻断后续,且聚合上报(不吞第一个错误)', async () => {
|
|
78
|
+
const record = [];
|
|
79
|
+
const { log, errors } = createTestLog();
|
|
80
|
+
const app = await defineServer(createBaseOptions({
|
|
81
|
+
log,
|
|
82
|
+
modules: { ...P1_OPT_OUT, env: createRecordingShutdownModule(record, 'disposeConfig') },
|
|
83
|
+
beforeShutdown: () => {
|
|
84
|
+
record.push('beforeShutdown');
|
|
85
|
+
throw new Error('boom:beforeShutdown');
|
|
86
|
+
},
|
|
87
|
+
}));
|
|
88
|
+
await app.close();
|
|
89
|
+
// disposeConfig 抛错后,4~7 步仍然执行
|
|
90
|
+
expect(record).toEqual(['beforeShutdown', ...SHUTDOWN_ORDER]);
|
|
91
|
+
expect(errors).toHaveLength(1);
|
|
92
|
+
const aggregated = errors[0];
|
|
93
|
+
expect(aggregated.count).toBe(2);
|
|
94
|
+
expect(aggregated.errors).toHaveLength(2);
|
|
95
|
+
});
|
|
96
|
+
it('shutdown 选项整体覆盖默认关闭流程', async () => {
|
|
97
|
+
const record = [];
|
|
98
|
+
const app = await defineServer(createBaseOptions({
|
|
99
|
+
modules: { ...P1_OPT_OUT, env: createRecordingShutdownModule(record) },
|
|
100
|
+
beforeShutdown: () => {
|
|
101
|
+
record.push('beforeShutdown');
|
|
102
|
+
},
|
|
103
|
+
shutdown: () => {
|
|
104
|
+
record.push('custom');
|
|
105
|
+
},
|
|
106
|
+
}));
|
|
107
|
+
await app.close();
|
|
108
|
+
expect(record).toEqual(['custom']);
|
|
109
|
+
});
|
|
110
|
+
it('模块 hooks.beforeShutdown 生效(调用方选项优先)', async () => {
|
|
111
|
+
const record = [];
|
|
112
|
+
const module = {
|
|
113
|
+
name: 'env',
|
|
114
|
+
hooks: {
|
|
115
|
+
beforeShutdown: () => {
|
|
116
|
+
record.push('module-before');
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
shutdown: {
|
|
120
|
+
stopProducers: () => {
|
|
121
|
+
record.push('stopProducers');
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
const app = await defineServer(createBaseOptions({ modules: { ...P1_OPT_OUT, env: module } }));
|
|
126
|
+
await app.close();
|
|
127
|
+
expect(record).toEqual(['module-before', 'stopProducers']);
|
|
128
|
+
});
|
|
129
|
+
it('默认 disposeServices 用 Promise.allSettled 释放 services(单服务抛错不影响其它)', async () => {
|
|
130
|
+
let disposed = 0;
|
|
131
|
+
const app = await defineServer(createBaseOptions({
|
|
132
|
+
createServices: () => ({
|
|
133
|
+
a: {
|
|
134
|
+
dispose: () => {
|
|
135
|
+
disposed += 1;
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
b: {
|
|
139
|
+
dispose: () => {
|
|
140
|
+
disposed += 1;
|
|
141
|
+
throw new Error('service dispose failed');
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
}),
|
|
145
|
+
}));
|
|
146
|
+
await app.close();
|
|
147
|
+
expect(disposed).toBe(2);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import type { LoggerLike } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* 内置信号处理(design §4.1 第 15 步尾部)。
|
|
5
|
+
*
|
|
6
|
+
* `SIGTERM` / `SIGINT` / `uncaughtException` / `unhandledRejection` ⇒ `app.close()`(触发 onClose
|
|
7
|
+
* 的关闭 7 步)⇒ `process.exit(0/1)`。可通过 `signal: false` 整体关掉(测试 / 嵌入场景)。
|
|
8
|
+
*
|
|
9
|
+
* @returns detach 函数(移除本次注册的监听器)
|
|
10
|
+
*/
|
|
11
|
+
export declare function attachSignalHandlers(app: FastifyInstance, log: LoggerLike): () => void;
|
|
12
|
+
//# sourceMappingURL=signal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../../source/app/signal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,UAAU,GAAG,MAAM,IAAI,CA2CtF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 内置信号处理(design §4.1 第 15 步尾部)。
|
|
3
|
+
*
|
|
4
|
+
* `SIGTERM` / `SIGINT` / `uncaughtException` / `unhandledRejection` ⇒ `app.close()`(触发 onClose
|
|
5
|
+
* 的关闭 7 步)⇒ `process.exit(0/1)`。可通过 `signal: false` 整体关掉(测试 / 嵌入场景)。
|
|
6
|
+
*
|
|
7
|
+
* @returns detach 函数(移除本次注册的监听器)
|
|
8
|
+
*/
|
|
9
|
+
export function attachSignalHandlers(app, log) {
|
|
10
|
+
let closing = false;
|
|
11
|
+
const shutdown = (signal, code) => {
|
|
12
|
+
if (closing) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
closing = true;
|
|
16
|
+
log.info({ signal }, '[server] received shutdown signal');
|
|
17
|
+
app.close().then(() => {
|
|
18
|
+
log.info({ signal }, '[server] shutdown complete');
|
|
19
|
+
process.exit(code);
|
|
20
|
+
}, (error) => {
|
|
21
|
+
log.error({ error }, '[server] error during shutdown');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
const onSigterm = () => shutdown('SIGTERM', 0);
|
|
26
|
+
const onSigint = () => shutdown('SIGINT', 0);
|
|
27
|
+
const onUncaught = (error) => {
|
|
28
|
+
log.error({ error }, '[server] uncaught exception');
|
|
29
|
+
shutdown('uncaughtException', 1);
|
|
30
|
+
};
|
|
31
|
+
const onRejection = (reason) => {
|
|
32
|
+
log.error({ reason }, '[server] unhandled rejection');
|
|
33
|
+
shutdown('unhandledRejection', 1);
|
|
34
|
+
};
|
|
35
|
+
process.on('SIGTERM', onSigterm);
|
|
36
|
+
process.on('SIGINT', onSigint);
|
|
37
|
+
process.on('uncaughtException', onUncaught);
|
|
38
|
+
process.on('unhandledRejection', onRejection);
|
|
39
|
+
return () => {
|
|
40
|
+
process.off('SIGTERM', onSigterm);
|
|
41
|
+
process.off('SIGINT', onSigint);
|
|
42
|
+
process.off('uncaughtException', onUncaught);
|
|
43
|
+
process.off('unhandledRejection', onRejection);
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ServerMode } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* 固定 15 步(design §4.1)——**顺序写死在数组里**。
|
|
4
|
+
*
|
|
5
|
+
* 模块只能往这些「既定槽位」里填实现,不得新增步骤、不得重排、不得改变 §4.1 的依赖偏序。
|
|
6
|
+
*/
|
|
7
|
+
export declare const STEP_ORDER: readonly ["createFastify", "setupCompilers", "database.createClient", "createRedis", "initGlobalConfig", "initClients", "registerCorePlugins", "createServices", "initServices", "backgroundWorkers", "registerRouters", "onClose", "ready", "afterReady", "listen"];
|
|
8
|
+
export type StepName = (typeof STEP_ORDER)[number];
|
|
9
|
+
/**
|
|
10
|
+
* 各模式跳过的步骤(design §4.1 / §4.3)。
|
|
11
|
+
*
|
|
12
|
+
* - `test`:关后台 worker 与 ready 后动作(第 10 / 14 步)。
|
|
13
|
+
* - `openapi-build`:跳过 [5,6,9,10,14,15] 与一切连库/连 Redis 的动作,但仍保留 [3,4](10-3:
|
|
14
|
+
* P1 沿用四家现状「不跳过第 3/4 步」,跳过错步留 P2),并保留 [7,8,11] 以保证 `app.swagger()` 完整。
|
|
15
|
+
*/
|
|
16
|
+
export declare const SKIP_BY_MODE: Record<ServerMode, readonly StepName[]>;
|
|
17
|
+
//# sourceMappingURL=steps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"steps.d.ts","sourceRoot":"","sources":["../../source/app/steps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;GAIG;AACH,eAAO,MAAM,UAAU,sQAgBb,CAAA;AAEV,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,SAAS,QAAQ,EAAE,CAWhE,CAAA"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 固定 15 步(design §4.1)——**顺序写死在数组里**。
|
|
3
|
+
*
|
|
4
|
+
* 模块只能往这些「既定槽位」里填实现,不得新增步骤、不得重排、不得改变 §4.1 的依赖偏序。
|
|
5
|
+
*/
|
|
6
|
+
export const STEP_ORDER = [
|
|
7
|
+
'createFastify',
|
|
8
|
+
'setupCompilers',
|
|
9
|
+
'database.createClient',
|
|
10
|
+
'createRedis',
|
|
11
|
+
'initGlobalConfig',
|
|
12
|
+
'initClients',
|
|
13
|
+
'registerCorePlugins',
|
|
14
|
+
'createServices',
|
|
15
|
+
'initServices',
|
|
16
|
+
'backgroundWorkers',
|
|
17
|
+
'registerRouters',
|
|
18
|
+
'onClose',
|
|
19
|
+
'ready',
|
|
20
|
+
'afterReady',
|
|
21
|
+
'listen',
|
|
22
|
+
];
|
|
23
|
+
/**
|
|
24
|
+
* 各模式跳过的步骤(design §4.1 / §4.3)。
|
|
25
|
+
*
|
|
26
|
+
* - `test`:关后台 worker 与 ready 后动作(第 10 / 14 步)。
|
|
27
|
+
* - `openapi-build`:跳过 [5,6,9,10,14,15] 与一切连库/连 Redis 的动作,但仍保留 [3,4](10-3:
|
|
28
|
+
* P1 沿用四家现状「不跳过第 3/4 步」,跳过错步留 P2),并保留 [7,8,11] 以保证 `app.swagger()` 完整。
|
|
29
|
+
*/
|
|
30
|
+
export const SKIP_BY_MODE = {
|
|
31
|
+
production: [],
|
|
32
|
+
test: ['backgroundWorkers', 'afterReady'],
|
|
33
|
+
'openapi-build': [
|
|
34
|
+
'initGlobalConfig',
|
|
35
|
+
'initClients',
|
|
36
|
+
'initServices',
|
|
37
|
+
'backgroundWorkers',
|
|
38
|
+
'afterReady',
|
|
39
|
+
'listen',
|
|
40
|
+
],
|
|
41
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { Disposer } from '@taicode/common-base';
|
|
2
|
+
import type { FastifyInstance, FastifyPluginAsync } from 'fastify';
|
|
3
|
+
import type { ModuleAssembly } from './module.js';
|
|
4
|
+
import type { ShutdownStepName } from './shutdown.js';
|
|
5
|
+
import type { StepName } from './steps.js';
|
|
6
|
+
/** 原生 Fastify 实例,原样返回(不包 `fastify-plugin`、不返回包装对象) */
|
|
7
|
+
export type ServerInstance = FastifyInstance;
|
|
8
|
+
export type ServerMode = 'production' | 'test' | 'openapi-build';
|
|
9
|
+
/** 错误工厂:由各服务 `errors.ts` 的 `define()` 产出(`class extends Error`) */
|
|
10
|
+
export type ErrorFactory = new (...args: never[]) => Error;
|
|
11
|
+
export interface ErrorFactories {
|
|
12
|
+
UserError: ErrorFactory;
|
|
13
|
+
SystemError: ErrorFactory;
|
|
14
|
+
}
|
|
15
|
+
/** 运行时配置快照(对应各仓导出的 `config` 常量) */
|
|
16
|
+
export interface RuntimeConfig {
|
|
17
|
+
debug: boolean;
|
|
18
|
+
port: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 配置模块端口(design §4.2)。`loadConfig` 必填;依赖数据层的服务才需要 `loadPostgresConfig`。
|
|
22
|
+
*
|
|
23
|
+
* 骨架不替调用方加载 env —— 请先自行调用 `loadEnvFiles()`(详见 `./env.js`)。
|
|
24
|
+
*/
|
|
25
|
+
export interface ConfigModule {
|
|
26
|
+
loadConfig: () => RuntimeConfig;
|
|
27
|
+
loadPostgresConfig?: () => unknown | Promise<unknown>;
|
|
28
|
+
resolveRedisUrl?: () => string | undefined;
|
|
29
|
+
resolveCachePrefix?: () => string | undefined;
|
|
30
|
+
}
|
|
31
|
+
/** 骨架只按 `(obj, msg)` 形态打日志;`app.log` 与 `@taicode/common-base` 的 `Logger` 都满足 */
|
|
32
|
+
export interface LoggerLike {
|
|
33
|
+
debug: (obj: unknown, msg?: string) => void;
|
|
34
|
+
info: (obj: unknown, msg?: string) => void;
|
|
35
|
+
warn: (obj: unknown, msg?: string) => void;
|
|
36
|
+
error: (obj: unknown, msg?: string) => void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 骨架**不操作** Redis 命令,只持有并在关闭时透传 dispose;
|
|
40
|
+
* 因此保持开放类型,各仓注入自己的客户端(如 node-redis)不受约束。
|
|
41
|
+
*/
|
|
42
|
+
export type RedisLike = unknown;
|
|
43
|
+
export interface RedisHandle {
|
|
44
|
+
client: RedisLike;
|
|
45
|
+
dispose: () => Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
export type DisposeFunction = () => unknown;
|
|
48
|
+
export type UnsubscribeFunction = () => unknown;
|
|
49
|
+
export interface GlobalConfigOptions {
|
|
50
|
+
redisUrl?: string;
|
|
51
|
+
channelPrefix?: string;
|
|
52
|
+
}
|
|
53
|
+
/** 贯穿步骤 5/6/8 的依赖上下文 */
|
|
54
|
+
export interface DbContext<Db = unknown> {
|
|
55
|
+
db: Db;
|
|
56
|
+
redis?: RedisLike;
|
|
57
|
+
globalConfig: unknown;
|
|
58
|
+
}
|
|
59
|
+
export interface ServicesContext<S = unknown> {
|
|
60
|
+
services: S;
|
|
61
|
+
}
|
|
62
|
+
export interface InitContext<Db = unknown, S = unknown> extends DbContext<Db> {
|
|
63
|
+
services: S;
|
|
64
|
+
}
|
|
65
|
+
export interface WorkersContext<Db = unknown, S = unknown> extends DbContext<Db> {
|
|
66
|
+
services: S;
|
|
67
|
+
disposer: Disposer;
|
|
68
|
+
}
|
|
69
|
+
export interface ReadyContext<S = unknown> {
|
|
70
|
+
app: FastifyInstance;
|
|
71
|
+
services: S;
|
|
72
|
+
}
|
|
73
|
+
export interface ServerOptions {
|
|
74
|
+
port: number;
|
|
75
|
+
host?: string;
|
|
76
|
+
}
|
|
77
|
+
export type ResolveMessage = (type: string, context: unknown, explicit?: string) => string | undefined;
|
|
78
|
+
export type MapStatus = (type: string) => number | null;
|
|
79
|
+
/** 骨架内部持有的运行时句柄(配置 / 关闭流程用) */
|
|
80
|
+
export interface RuntimeState {
|
|
81
|
+
app?: FastifyInstance;
|
|
82
|
+
db?: unknown;
|
|
83
|
+
redis?: RedisHandle;
|
|
84
|
+
globalConfig?: unknown;
|
|
85
|
+
services?: unknown;
|
|
86
|
+
disposer: Disposer;
|
|
87
|
+
unsubscribe?: UnsubscribeFunction;
|
|
88
|
+
disposeConfig?: DisposeFunction;
|
|
89
|
+
}
|
|
90
|
+
/** 每个步骤执行时拿到的上下文(`log` 在 `createFastify` 之后默认降级/升级为 `app.log`) */
|
|
91
|
+
export interface StepContext {
|
|
92
|
+
options: DefineServerOptions;
|
|
93
|
+
state: RuntimeState;
|
|
94
|
+
log: LoggerLike;
|
|
95
|
+
}
|
|
96
|
+
export type StepRunner = (ctx: StepContext) => unknown | Promise<unknown>;
|
|
97
|
+
export type StepImplementations = Record<StepName, StepRunner>;
|
|
98
|
+
export type ShutdownRunner = (ctx: StepContext) => unknown | Promise<unknown>;
|
|
99
|
+
export type ShutdownImplementations = Record<ShutdownStepName, ShutdownRunner>;
|
|
100
|
+
/** 模块可贡献的生命周期 hook(design §4.2) */
|
|
101
|
+
export interface ServerHooks {
|
|
102
|
+
beforeShutdown?: () => unknown | Promise<unknown>;
|
|
103
|
+
shutdown?: (signal: string) => unknown | Promise<unknown>;
|
|
104
|
+
disposeServices?: (ctx: ServicesContext) => unknown | Promise<unknown>;
|
|
105
|
+
disposeClients?: () => unknown | Promise<unknown>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* `defineServer()` 入参(design §3.4)。
|
|
109
|
+
*
|
|
110
|
+
* `Db` 由 `modules.database`(`database` 模块的 `createClient` 返回类型)**推断**(决策 10-2),
|
|
111
|
+
* 因此调用方**不需要**在 `createServices` 里写 `as PrismaClient` 强转。
|
|
112
|
+
*/
|
|
113
|
+
export interface DefineServerOptions<Db = unknown, S = unknown> {
|
|
114
|
+
serviceName: string;
|
|
115
|
+
title: string;
|
|
116
|
+
description: string;
|
|
117
|
+
version?: string;
|
|
118
|
+
mode?: ServerMode;
|
|
119
|
+
serverOptions?: ServerOptions;
|
|
120
|
+
serverUrl?: string | (() => string | null);
|
|
121
|
+
openapiVersion?: string;
|
|
122
|
+
docPath?: string;
|
|
123
|
+
log?: LoggerLike;
|
|
124
|
+
signal?: boolean;
|
|
125
|
+
errors: ErrorFactories;
|
|
126
|
+
messages?: Record<string, string>;
|
|
127
|
+
resolveMessage?: ResolveMessage;
|
|
128
|
+
mapStatus?: MapStatus;
|
|
129
|
+
modules?: ModuleAssembly<Db>;
|
|
130
|
+
config: ConfigModule;
|
|
131
|
+
setupCompilers?: (app: FastifyInstance) => void;
|
|
132
|
+
createRedis?: (config: ConfigModule) => RedisHandle | undefined;
|
|
133
|
+
initGlobalConfig?: (db: Db, opts: GlobalConfigOptions) => void | DisposeFunction;
|
|
134
|
+
onGlobalConfigChange?: (globalConfig: unknown) => void;
|
|
135
|
+
initClients?: (ctx: DbContext<Db>) => Promise<void | UnsubscribeFunction>;
|
|
136
|
+
extraPlugins?: FastifyPluginAsync[];
|
|
137
|
+
createServices: (ctx: DbContext<Db>) => S;
|
|
138
|
+
initServices?: (ctx: ServicesContext<S>) => Promise<void>;
|
|
139
|
+
backgroundWorkers?: (ctx: WorkersContext<Db, S>) => Promise<void>;
|
|
140
|
+
registerRouters: (app: FastifyInstance, ctx: ServicesContext<S>) => Promise<void> | void;
|
|
141
|
+
afterInit?: (ctx: InitContext<Db, S>) => Promise<DisposeFunction | void>;
|
|
142
|
+
afterReady?: (ctx: ReadyContext<S>) => Promise<void>;
|
|
143
|
+
beforeShutdown?: () => Promise<void> | void;
|
|
144
|
+
disposeServices?: (ctx: ServicesContext<S>) => Promise<void> | void;
|
|
145
|
+
disposeClients?: () => Promise<void> | void;
|
|
146
|
+
/** 覆盖默认关闭流程(design §3.4 / §5) */
|
|
147
|
+
shutdown?: (signal: string) => Promise<void> | void;
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../source/app/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../source/rate-limiter/rate-limiter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../source/rate-limiter/rate-limiter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,kBAAkB,EAAE,eAAe,EAAkB,MAAM,YAAY,CAAA;AAErH,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAgB;gBAEjB,OAAO,GAAE,kBAAuB;IAItC,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IAoC1E,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAI9D;;OAEG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;CAezF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAE7E"}
|
|
@@ -130,9 +130,10 @@ export declare class RedisCache {
|
|
|
130
130
|
* - 连接超时受限,避免 Redis 不可达时启动流程被拖住;
|
|
131
131
|
* - 重连采用固定上限的退避,避免无意义的密集重试。
|
|
132
132
|
*/
|
|
133
|
-
export
|
|
133
|
+
export interface CreateRedisClientOptions {
|
|
134
134
|
url: string;
|
|
135
135
|
logPrefix?: string;
|
|
136
136
|
connectTimeoutMs?: number;
|
|
137
|
-
}
|
|
137
|
+
}
|
|
138
|
+
export declare function createRedisClient(options: CreateRedisClientOptions): RedisClientType;
|
|
138
139
|
//# sourceMappingURL=redis-cache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis-cache.d.ts","sourceRoot":"","sources":["../../source/redis-cache/redis-cache.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,OAAO,CAAA;AAE1D;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAA;IACd,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kDAAkD;IAClD,WAAW,CAAC,EAAE,eAAe,CAAA;IAC7B,uBAAuB;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,kCAAkC;IAClC,EAAE,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAA;CACjC;AAaD;;;;;;;;;;GAUG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IACjD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IAEvC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAA0B;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAEhC,gDAAgD;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAE/D,OAAO,CAAC,gBAAgB,CAAI;gBAEhB,OAAO,EAAE,iBAAiB;IA6BtC,uBAAuB;IACvB,IAAW,IAAI,IAAI,MAAM,CAExB;IAED,sBAAsB;IACtB,IAAW,QAAQ,IAAI,MAAM,CAE5B;IAED,wCAAwC;IACxC,IAAW,WAAW,IAAI,OAAO,CAEhC;IAED;;;OAGG;IACU,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAOrC,0BAA0B;IACnB,UAAU,IAAI,IAAI;IAOzB;;;;;OAKG;IACU,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrC;;;OAGG;IACI,KAAK,IAAI,IAAI;IAQP,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAItC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa/C;;;;;;OAMG;IACU,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAwB5D;;;;;;OAMG;IACU,QAAQ,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"redis-cache.d.ts","sourceRoot":"","sources":["../../source/redis-cache/redis-cache.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,OAAO,CAAA;AAE1D;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAA;IACd,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kDAAkD;IAClD,WAAW,CAAC,EAAE,eAAe,CAAA;IAC7B,uBAAuB;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,kCAAkC;IAClC,EAAE,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAA;CACjC;AAaD;;;;;;;;;;GAUG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IACjD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IAEvC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAA0B;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAEhC,gDAAgD;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAE/D,OAAO,CAAC,gBAAgB,CAAI;gBAEhB,OAAO,EAAE,iBAAiB;IA6BtC,uBAAuB;IACvB,IAAW,IAAI,IAAI,MAAM,CAExB;IAED,sBAAsB;IACtB,IAAW,QAAQ,IAAI,MAAM,CAE5B;IAED,wCAAwC;IACxC,IAAW,WAAW,IAAI,OAAO,CAEhC;IAED;;;OAGG;IACU,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAOrC,0BAA0B;IACnB,UAAU,IAAI,IAAI;IAOzB;;;;;OAKG;IACU,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrC;;;OAGG;IACI,KAAK,IAAI,IAAI;IAQP,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAItC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa/C;;;;;;OAMG;IACU,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAwB5D;;;;;;OAMG;IACU,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IA6BhG;;;;OAIG;IACH,OAAO,CAAC,QAAQ;IAIhB,8CAA8C;YAChC,aAAa;YAmBb,aAAa;IAY3B,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,QAAQ;CAOjB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,wBAAwB;IACvC,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,eAAe,CAkBpF"}
|
|
@@ -264,13 +264,6 @@ export class RedisCache {
|
|
|
264
264
|
logger.error(`[${this.getLogPrefix()}] ${operation} failed, cache degraded to pass-through:`, key, error);
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
|
-
/**
|
|
268
|
-
* 创建 Redis 客户端。
|
|
269
|
-
*
|
|
270
|
-
* 与 RedisQueueCommon 保持同样的连接策略,但补了两点:
|
|
271
|
-
* - 连接超时受限,避免 Redis 不可达时启动流程被拖住;
|
|
272
|
-
* - 重连采用固定上限的退避,避免无意义的密集重试。
|
|
273
|
-
*/
|
|
274
267
|
export function createRedisClient(options) {
|
|
275
268
|
const logPrefix = options.logPrefix ?? 'Redis';
|
|
276
269
|
const client = createClient({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RedisQueueCommon } from './redis-queue-common.js';
|
|
2
|
-
import type { BatchConsumerConfig, QueueStats, RedisQueueRegistry } from './types.js';
|
|
2
|
+
import type { AnyRegistry, BatchConsumerConfig, QueueStats, RedisQueueRegistry } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Redis 批量队列消费者
|
|
5
5
|
*
|
|
@@ -47,7 +47,7 @@ import type { BatchConsumerConfig, QueueStats, RedisQueueRegistry } from './type
|
|
|
47
47
|
* consumer.disconnect()
|
|
48
48
|
* ```
|
|
49
49
|
*/
|
|
50
|
-
export declare class RedisQueueBatchConsumer<R extends
|
|
50
|
+
export declare class RedisQueueBatchConsumer<R extends AnyRegistry = RedisQueueRegistry, K extends keyof R & string = keyof R & string> extends RedisQueueCommon {
|
|
51
51
|
private consumerRunning;
|
|
52
52
|
private consumerInterval;
|
|
53
53
|
private recoveryInterval;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis-queue-batch-consumer.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-batch-consumer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"redis-queue-batch-consumer.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-batch-consumer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAA0B,UAAU,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAE1H;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,qBAAa,uBAAuB,CAClC,CAAC,SAAS,WAAW,GAAG,kBAAkB,EAC1C,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CAC7C,SAAQ,gBAAgB;IACxB,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,iBAAiB,CAAI;IAC7B,OAAO,CAAC,kBAAkB,CAA2B;IAErD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAOtB;gBAEW,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAuB1D,SAAS,CAAC,YAAY,IAAI,MAAM;IAIhC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAO9B;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBjC;;OAEG;YACW,YAAY;IA2G1B;;OAEG;IACH,OAAO,CAAC,aAAa;IAoBrB;;OAEG;IACH,OAAO,CAAC,YAAY;IAQpB;;OAEG;IACH,OAAO,CAAC,aAAa;IAyErB;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IAevC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;CAMjC"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { catchIt } from '@taicode/common-base';
|
|
2
|
-
import { logger } from '@taicode/common-base';
|
|
1
|
+
import { catchIt, logger } from '@taicode/common-base';
|
|
3
2
|
import { RedisQueueCommon } from './redis-queue-common.js';
|
|
4
3
|
/**
|
|
5
4
|
* Redis 批量队列消费者
|
|
@@ -99,7 +98,7 @@ export class RedisQueueBatchConsumer extends RedisQueueCommon {
|
|
|
99
98
|
if (this.processingPromises.size > 0) {
|
|
100
99
|
logger.info(`[RedisQueueBatchConsumer] Waiting for ${this.processingPromises.size} batches to complete...`);
|
|
101
100
|
await Promise.allSettled(this.processingPromises);
|
|
102
|
-
logger.info(
|
|
101
|
+
logger.info('[RedisQueueBatchConsumer] All batches completed');
|
|
103
102
|
}
|
|
104
103
|
// 最后断开 Redis 连接
|
|
105
104
|
super.disconnect();
|
|
@@ -116,7 +115,7 @@ export class RedisQueueBatchConsumer extends RedisQueueCommon {
|
|
|
116
115
|
const tasks = await Promise.all(taskIds.map(id => this.getTask(id)));
|
|
117
116
|
const validTasks = tasks.filter((task) => task !== null);
|
|
118
117
|
if (validTasks.length === 0) {
|
|
119
|
-
logger.warn(
|
|
118
|
+
logger.warn('[RedisQueueBatchConsumer] No valid tasks found in batch');
|
|
120
119
|
return;
|
|
121
120
|
}
|
|
122
121
|
// 过滤出有效的待处理任务
|
|
@@ -151,7 +150,7 @@ export class RedisQueueBatchConsumer extends RedisQueueCommon {
|
|
|
151
150
|
logger.info(`[RedisQueueBatchConsumer] Batch completed: ${updatedCount} tasks`);
|
|
152
151
|
}
|
|
153
152
|
catch (error) {
|
|
154
|
-
logger.error(
|
|
153
|
+
logger.error('[RedisQueueBatchConsumer] Batch failed:', error);
|
|
155
154
|
// 批量重试或失败
|
|
156
155
|
for (const task of tasksToProcess) {
|
|
157
156
|
if (task.retryCount < task.maxRetries) {
|
|
@@ -294,7 +293,7 @@ export class RedisQueueBatchConsumer extends RedisQueueCommon {
|
|
|
294
293
|
if (taskIds.length > 0) {
|
|
295
294
|
// 处理这批任务(不等待完成)
|
|
296
295
|
this.processBatch(taskIds).catch(error => {
|
|
297
|
-
logger.error(
|
|
296
|
+
logger.error('[RedisQueueBatchConsumer] Unhandled error in processBatch:', error);
|
|
298
297
|
});
|
|
299
298
|
}
|
|
300
299
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis-queue-batch-consumer.test.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-batch-consumer.test.ts"],"names":[],"mappings":"AAQA,KAAK,YAAY,GAAG;IAClB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACvB,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"redis-queue-batch-consumer.test.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-batch-consumer.test.ts"],"names":[],"mappings":"AAQA,KAAK,YAAY,GAAG;IAClB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACvB,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,CAAA;AAGD,OAAO,QAAQ,YAAY,CAAC;IAC1B,UAAU,kBAAkB;QAC1B,CAAC,GAAG,EAAE,cAAc,MAAM,EAAE,GAAG,YAAY,CAAA;KAC5C;CACF"}
|
|
@@ -145,9 +145,7 @@ describe('RedisQueueBatchConsumer', () => {
|
|
|
145
145
|
});
|
|
146
146
|
describe('重试机制', () => {
|
|
147
147
|
it('失败的批次应该重试', async () => {
|
|
148
|
-
let attemptCount = 0;
|
|
149
148
|
const { provider, consumer } = createQueue(async () => {
|
|
150
|
-
attemptCount++;
|
|
151
149
|
throw new Error('Batch failed');
|
|
152
150
|
}, { batchSize: 2, maxRetries: 1 });
|
|
153
151
|
await provider.connect();
|
|
@@ -292,7 +290,7 @@ describe('RedisQueueBatchConsumer', () => {
|
|
|
292
290
|
describe('超时恢复', () => {
|
|
293
291
|
it('超时的任务应该直接标记为失败', { timeout: 15000 }, async () => {
|
|
294
292
|
let processCount = 0;
|
|
295
|
-
const { provider, consumer } = createQueue(async (
|
|
293
|
+
const { provider, consumer } = createQueue(async () => {
|
|
296
294
|
processCount++;
|
|
297
295
|
// 模拟处理卡住,永远不完成 (但超时机制会检测到)
|
|
298
296
|
await new Promise(resolve => setTimeout(resolve, 20000));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis-queue-common.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,OAAO,CAAA;AAE1D,OAAO,KAAK,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"redis-queue-common.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,OAAO,CAAA;AAE1D,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAEtE;;;GAGG;AACH,8BAAsB,gBAAgB;IACpC,SAAS,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAO;IAC9C,SAAS,CAAC,eAAe,EAAE,OAAO,CAAQ;IAE1C,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACnC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACpC,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAGvC,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IACtC,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IACvC,SAAS,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAC1C,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;gBAE7B,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,sBAA2B;IAwCjE;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,YAAY,IAAI,MAAM;IAEzC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAS9B;;OAEG;IACH,UAAU,IAAI,IAAI;IAalB;;OAEG;cACa,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAW7D;;OAEG;IACH,SAAS,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAUlD;;OAEG;cACa,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqChG;;;OAGG;cACa,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAqD1G;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAevG;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAUhC;;;;;;;OAOG;cACa,cAAc,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CA4E/G"}
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
+
type TestQueueData = {
|
|
2
|
+
id?: string;
|
|
3
|
+
[key: string]: unknown;
|
|
4
|
+
};
|
|
1
5
|
declare module './types.js' {
|
|
2
6
|
interface RedisQueueRegistry {
|
|
3
|
-
'test-queue':
|
|
4
|
-
'test-queue-error':
|
|
5
|
-
'short':
|
|
6
|
-
'valid-key':
|
|
7
|
-
'test-default':
|
|
8
|
-
'test-custom':
|
|
9
|
-
'':
|
|
10
|
-
'my-queue':
|
|
11
|
-
'test-error':
|
|
12
|
-
'test-bad':
|
|
13
|
-
'queue-one':
|
|
14
|
-
'queue-two':
|
|
15
|
-
[key: `test:reconnect:${number}`]:
|
|
7
|
+
'test-queue': TestQueueData;
|
|
8
|
+
'test-queue-error': TestQueueData;
|
|
9
|
+
'short': TestQueueData;
|
|
10
|
+
'valid-key': TestQueueData;
|
|
11
|
+
'test-default': TestQueueData;
|
|
12
|
+
'test-custom': TestQueueData;
|
|
13
|
+
'': TestQueueData;
|
|
14
|
+
'my-queue': TestQueueData;
|
|
15
|
+
'test-error': TestQueueData;
|
|
16
|
+
'test-bad': TestQueueData;
|
|
17
|
+
'queue-one': TestQueueData;
|
|
18
|
+
'queue-two': TestQueueData;
|
|
19
|
+
[key: `test:reconnect:${number}`]: TestQueueData;
|
|
16
20
|
}
|
|
17
21
|
}
|
|
18
22
|
export {};
|