groupmq-plus 1.3.1 → 1.3.2
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/dist/index.d.mts +162 -116
- package/dist/index.d.ts +162 -116
- package/dist/index.js +75 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +38 -1
- package/dist/index.mjs.map +1 -1
- package/dist/lua/enqueue.lua +20 -1
- package/dist/lua/reserve-atomic.lua +2 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -140,6 +140,28 @@ interface LoggerInterface {
|
|
|
140
140
|
}
|
|
141
141
|
//#endregion
|
|
142
142
|
//#region src/queue.d.ts
|
|
143
|
+
/**
|
|
144
|
+
* Group 的配置信息
|
|
145
|
+
* concurrency 是核心字段,影响 Lua 脚本行为
|
|
146
|
+
* 其他字段为策略层使用的元数据(如 priority, weight 等)
|
|
147
|
+
*/
|
|
148
|
+
type GroupConfig = {
|
|
149
|
+
/** 并发限制 (Core Engine 使用) */
|
|
150
|
+
concurrency?: number;
|
|
151
|
+
/** 允许存储任意元数据供 Strategy 使用 */
|
|
152
|
+
[key: string]: any;
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* 组配置选项(用于 queue.add 的 groupConfig 参数)
|
|
156
|
+
*/
|
|
157
|
+
type GroupOptions = {
|
|
158
|
+
/** 组优先级 (被 PriorityStrategy 使用) */
|
|
159
|
+
priority?: number;
|
|
160
|
+
/** 并发限制 (被 Core 使用) */
|
|
161
|
+
concurrency?: number;
|
|
162
|
+
/** 允许传入任意其他策略需要的元数据 */
|
|
163
|
+
[key: string]: any;
|
|
164
|
+
};
|
|
143
165
|
/**
|
|
144
166
|
* Flow child result type with explicit status
|
|
145
167
|
*/
|
|
@@ -148,6 +170,17 @@ type FlowChildResult<R = any> = {
|
|
|
148
170
|
status: 'completed' | 'failed';
|
|
149
171
|
result: R;
|
|
150
172
|
};
|
|
173
|
+
/**
|
|
174
|
+
* Reserve result type with explicit status distinction between success, limit exceeded, and empty
|
|
175
|
+
*/
|
|
176
|
+
type ReserveResult<T = any> = {
|
|
177
|
+
status: 'success';
|
|
178
|
+
job: ReservedJob<T>;
|
|
179
|
+
} | {
|
|
180
|
+
status: 'limit_exceeded';
|
|
181
|
+
} | {
|
|
182
|
+
status: 'empty';
|
|
183
|
+
};
|
|
151
184
|
/**
|
|
152
185
|
* Options for configuring a GroupMQ queue
|
|
153
186
|
*/
|
|
@@ -504,6 +537,19 @@ type AddOptions<T> = {
|
|
|
504
537
|
* - Deduplication: Prevent duplicate jobs from being created
|
|
505
538
|
*/
|
|
506
539
|
jobId?: string;
|
|
540
|
+
/**
|
|
541
|
+
* (新增) 设置或更新组的配置
|
|
542
|
+
* 这些配置将随任务一起原子性写入 Redis
|
|
543
|
+
*
|
|
544
|
+
* @example { priority: 10 } // 设置组优先级
|
|
545
|
+
* @example { concurrency: 5 } // 设置组并发限制
|
|
546
|
+
* @example { priority: 10, concurrency: 3 } // 同时设置多个配置
|
|
547
|
+
*
|
|
548
|
+
* **When to use:**
|
|
549
|
+
* - 入队即配置:在添加任务时同时设置组配置
|
|
550
|
+
* - 动态调整:根据任务特性动态调整组优先级
|
|
551
|
+
*/
|
|
552
|
+
groupConfig?: GroupOptions;
|
|
507
553
|
};
|
|
508
554
|
type ReservedJob<T = any> = {
|
|
509
555
|
id: string;
|
|
@@ -543,6 +589,27 @@ declare class Queue<T = any> {
|
|
|
543
589
|
private batchBuffer;
|
|
544
590
|
private batchTimer?;
|
|
545
591
|
private flushing;
|
|
592
|
+
readonly groups: {
|
|
593
|
+
/**
|
|
594
|
+
* 设置组的配置和元数据
|
|
595
|
+
* 我们将使用 Hash 存储这些配置: groupmq:{ns}:config:{groupId}
|
|
596
|
+
* @param groupId 组 ID
|
|
597
|
+
* @param config 配置对象。concurrency 会影响核心调度,其他字段供 Strategy 使用。
|
|
598
|
+
*/
|
|
599
|
+
setConfig: (groupId: string, config: GroupOptions) => Promise<void>;
|
|
600
|
+
/** 获取当前配置 */
|
|
601
|
+
getConfig: (groupId: string) => Promise<GroupOptions>;
|
|
602
|
+
/**
|
|
603
|
+
* 设置指定组的并发上限
|
|
604
|
+
* @param groupId 组 ID
|
|
605
|
+
* @param limit 并发数 (必须 >= 1)
|
|
606
|
+
*/
|
|
607
|
+
setConcurrency: (groupId: string, limit: number) => Promise<void>;
|
|
608
|
+
/**
|
|
609
|
+
* 获取指定组的并发上限
|
|
610
|
+
*/
|
|
611
|
+
getConcurrency: (groupId: string) => Promise<number>;
|
|
612
|
+
};
|
|
546
613
|
constructor(opts: QueueOptions);
|
|
547
614
|
get redis(): Redis;
|
|
548
615
|
get namespace(): string;
|
|
@@ -741,37 +808,16 @@ declare class Queue<T = any> {
|
|
|
741
808
|
reserveBlocking(timeoutSec?: number, blockUntil?: number, blockingClient?: ioredis0.default): Promise<ReservedJob<T> | null>;
|
|
742
809
|
/**
|
|
743
810
|
* Reserve a job from a specific group atomically (eliminates race conditions)
|
|
811
|
+
* Returns an explicit status to distinguish between success, limit exceeded, and empty states
|
|
744
812
|
* @param groupId - The group to reserve from
|
|
745
813
|
*/
|
|
746
|
-
reserveAtomic(groupId: string): Promise<
|
|
814
|
+
reserveAtomic(groupId: string): Promise<ReserveResult<T>>;
|
|
747
815
|
/**
|
|
748
816
|
* 获取处于 Ready 状态的 Group 列表
|
|
749
817
|
* @param start
|
|
750
818
|
* @param end
|
|
751
819
|
*/
|
|
752
820
|
getReadyGroups(start?: number, end?: number): Promise<string[]>;
|
|
753
|
-
/**
|
|
754
|
-
* 设置组的元数据 (优先级/并发度)
|
|
755
|
-
* 我们将使用 Hash 存储这些配置: groupmq:{ns}:config:{groupId}
|
|
756
|
-
*/
|
|
757
|
-
setGroupConfig(groupId: string, config: {
|
|
758
|
-
priority?: number;
|
|
759
|
-
concurrency?: number;
|
|
760
|
-
}): Promise<void>;
|
|
761
|
-
getGroupConfig(groupId: string): Promise<{
|
|
762
|
-
priority: number;
|
|
763
|
-
concurrency: number;
|
|
764
|
-
}>;
|
|
765
|
-
/**
|
|
766
|
-
* 设置指定组的并发上限
|
|
767
|
-
* @param groupId 组 ID
|
|
768
|
-
* @param limit 并发数 (必须 >= 1)
|
|
769
|
-
*/
|
|
770
|
-
setGroupConcurrency(groupId: string, limit: number): Promise<void>;
|
|
771
|
-
/**
|
|
772
|
-
* 获取指定组的并发上限
|
|
773
|
-
*/
|
|
774
|
-
getGroupConcurrency(groupId: string): Promise<number>;
|
|
775
821
|
/**
|
|
776
822
|
* 获取组内最老任务的入队时间戳
|
|
777
823
|
* 用于 PriorityStrategy 的 aging 算法
|
|
@@ -971,10 +1017,29 @@ declare class BullBoardGroupMQAdapter<T = any> extends BaseAdapter {
|
|
|
971
1017
|
//#region src/strategies/dispatch-strategy.d.ts
|
|
972
1018
|
interface DispatchStrategy {
|
|
973
1019
|
/**
|
|
974
|
-
*
|
|
975
|
-
*
|
|
1020
|
+
* 尝试获取下一个可执行的任务
|
|
1021
|
+
*
|
|
1022
|
+
* 策略内部负责完整的获取流程:
|
|
1023
|
+
* 1. 批量获取处于 Ready 状态的 Group
|
|
1024
|
+
* 2. 应用优先级排序逻辑
|
|
1025
|
+
* 3. 循环尝试从排序后的 Group 中获取任务
|
|
1026
|
+
* 4. 处理"并发已满 (E_LIMIT)"和"队列为空"两种失败状态
|
|
1027
|
+
* 5. 返回第一个成功获取的任务,或 null(无可用任务)
|
|
1028
|
+
*
|
|
1029
|
+
* 此接口设计体现了 IoC(控制反转)原则:
|
|
1030
|
+
* - Worker 只需简单调用此方法,无需关心具体选择逻辑
|
|
1031
|
+
* - Strategy 掌控"如何获取"的全过程
|
|
1032
|
+
* - 不同的 Strategy 可以实现不同的分派算法(优先级、加权、衰减等)
|
|
1033
|
+
*
|
|
1034
|
+
* @param queue - Queue 实例,用于获取 Group 和尝试保留任务
|
|
1035
|
+
* @returns 成功获取的任务,或 null(暂无可用任务)
|
|
1036
|
+
*/
|
|
1037
|
+
acquireJob(queue: Queue<any>): Promise<ReservedJob<any> | null>;
|
|
1038
|
+
/**
|
|
1039
|
+
* 当 acquireJob 返回 null (无任务) 时,建议 Worker 休眠的时间 (毫秒)
|
|
1040
|
+
* 如果未定义,Worker 将使用默认值
|
|
976
1041
|
*/
|
|
977
|
-
|
|
1042
|
+
readonly idleInterval: number;
|
|
978
1043
|
}
|
|
979
1044
|
//#endregion
|
|
980
1045
|
//#region src/worker.d.ts
|
|
@@ -1199,12 +1264,6 @@ type WorkerOptions<T> = {
|
|
|
1199
1264
|
* 转而使用策略轮询模式。
|
|
1200
1265
|
*/
|
|
1201
1266
|
strategy?: DispatchStrategy;
|
|
1202
|
-
/**
|
|
1203
|
-
* 策略模式下的轮询间隔 (ms)
|
|
1204
|
-
* 当使用 Strategy 时,我们无法使用阻塞读取 (BZPOPMIN),必须退化为短轮询
|
|
1205
|
-
* @default 50
|
|
1206
|
-
*/
|
|
1207
|
-
strategyPollInterval?: number;
|
|
1208
1267
|
/**
|
|
1209
1268
|
* 是否自动启动 Worker
|
|
1210
1269
|
* 设置为 false 时,需要手动调用 run() 方法启动 Worker
|
|
@@ -1376,126 +1435,113 @@ declare function getWorkersStatus<T = any>(workers: Worker<T>[]): {
|
|
|
1376
1435
|
};
|
|
1377
1436
|
//#endregion
|
|
1378
1437
|
//#region src/strategies/priority-strategy.d.ts
|
|
1379
|
-
|
|
1380
|
-
priority: number;
|
|
1381
|
-
concurrency: number;
|
|
1382
|
-
};
|
|
1383
|
-
/**
|
|
1384
|
-
* 自定义优先级计算函数
|
|
1385
|
-
* @param groupId 组 ID
|
|
1386
|
-
* @param config 从 Redis 读取的组配置
|
|
1387
|
-
* @returns 优先级数值(数字越大优先级越高)
|
|
1388
|
-
*/
|
|
1389
|
-
type OnGetPriority = (groupId: string, config: GroupConfig) => number | Promise<number>;
|
|
1390
|
-
/**
|
|
1391
|
-
* 严格优先级算法
|
|
1392
|
-
* 总是选择优先级最高的组,可能导致低优先级组饥饿
|
|
1393
|
-
*/
|
|
1438
|
+
/** 严格优先级算法:总是选择优先级最高的组 */
|
|
1394
1439
|
type StrictAlgorithmConfig = {
|
|
1395
1440
|
type: 'strict';
|
|
1396
1441
|
};
|
|
1397
|
-
/**
|
|
1398
|
-
* 加权随机算法(默认)
|
|
1399
|
-
* 根据优先级计算概率选择,确保低优先级组也有机会被执行
|
|
1400
|
-
*/
|
|
1442
|
+
/** 加权随机算法(默认):根据优先级权重概率选择,避免低优先级饥饿 */
|
|
1401
1443
|
type WeightedRandomAlgorithmConfig = {
|
|
1402
1444
|
type: 'weighted-random';
|
|
1403
|
-
/**
|
|
1404
|
-
* 最低优先级组的保底权重比例,默认 0.1(10%)
|
|
1405
|
-
* 例如:VIP 优先级 100,普通用户优先级 1
|
|
1406
|
-
* 普通用户的权重会被提升到 100 * 0.1 = 10,而不是 1
|
|
1407
|
-
* 这样普通用户大约有 10/(100+10) ≈ 9% 的概率被选中
|
|
1408
|
-
*/
|
|
1445
|
+
/** 最低权重比例,默认 0.1 */
|
|
1409
1446
|
minWeightRatio?: number;
|
|
1410
1447
|
};
|
|
1411
|
-
/**
|
|
1412
|
-
* 时间衰减算法
|
|
1413
|
-
* 等待时间越长,优先级加成越高,确保所有任务最终都会被执行
|
|
1414
|
-
*/
|
|
1448
|
+
/** 时间衰减算法:等待时间越长,优先级加成越高 */
|
|
1415
1449
|
type AgingAlgorithmConfig = {
|
|
1416
1450
|
type: 'aging';
|
|
1417
|
-
/**
|
|
1418
|
-
* 每等待多少毫秒增加 1 点优先级,默认 60000(1分钟)
|
|
1419
|
-
* 例如:VIP 优先级 100,普通用户优先级 1
|
|
1420
|
-
* 普通用户等待 100 分钟后,优先级变为 1 + 100 = 101,超过 VIP
|
|
1421
|
-
*/
|
|
1451
|
+
/** 每增加1点优先级需要的等待毫秒数,默认 60000 */
|
|
1422
1452
|
intervalMs?: number;
|
|
1423
1453
|
};
|
|
1424
|
-
/**
|
|
1425
|
-
* 算法配置类型
|
|
1426
|
-
*/
|
|
1427
1454
|
type AlgorithmConfig = StrictAlgorithmConfig | WeightedRandomAlgorithmConfig | AgingAlgorithmConfig;
|
|
1428
1455
|
interface PriorityStrategyOptions {
|
|
1429
1456
|
/**
|
|
1430
|
-
*
|
|
1431
|
-
*
|
|
1432
|
-
* @example
|
|
1433
|
-
* // 严格优先级(VIP 绝对优先)
|
|
1434
|
-
* algorithm: { type: 'strict' }
|
|
1435
|
-
*
|
|
1436
|
-
* // 加权随机(默认,平衡公平性)
|
|
1437
|
-
* algorithm: { type: 'weighted-random', minWeightRatio: 0.1 }
|
|
1438
|
-
*
|
|
1439
|
-
* // 时间衰减(确保无饥饿)
|
|
1440
|
-
* algorithm: { type: 'aging', intervalMs: 60000 }
|
|
1457
|
+
* 调度算法类型
|
|
1458
|
+
* @default { type: 'weighted-random' }
|
|
1441
1459
|
*/
|
|
1442
1460
|
algorithm?: AlgorithmConfig;
|
|
1443
|
-
/**
|
|
1461
|
+
/**
|
|
1462
|
+
* 默认优先级(当无法从 Redis 获取配置时的回退值)
|
|
1463
|
+
* @default 1
|
|
1464
|
+
*/
|
|
1444
1465
|
defaultPriority?: number;
|
|
1445
|
-
/** 优先级缓存 TTL(毫秒),默认 5000ms。设为 0 禁用缓存 */
|
|
1446
|
-
cacheTtlMs?: number;
|
|
1447
1466
|
/**
|
|
1448
|
-
*
|
|
1449
|
-
*
|
|
1450
|
-
*
|
|
1451
|
-
* @example
|
|
1452
|
-
* // 根据 VIP 等级计算优先级
|
|
1453
|
-
* onGetPriority: (groupId, config) => {
|
|
1454
|
-
* if (groupId.startsWith('vip:')) return config.priority * 10;
|
|
1455
|
-
* return config.priority;
|
|
1456
|
-
* }
|
|
1467
|
+
* 当没有任务时,Worker 的轮询间隔 (ms)
|
|
1468
|
+
* @default 50
|
|
1457
1469
|
*/
|
|
1458
|
-
|
|
1470
|
+
pollInterval?: number;
|
|
1459
1471
|
}
|
|
1472
|
+
/**
|
|
1473
|
+
* 基于优先级的调度策略
|
|
1474
|
+
*
|
|
1475
|
+
* 此策略负责根据 Group 的优先级决定处理顺序。
|
|
1476
|
+
* 它支持多种排序算法(严格优先、加权随机、时间衰减),
|
|
1477
|
+
* 并实现了"快速试错"(Fallthrough)机制以应对并发限制。
|
|
1478
|
+
*
|
|
1479
|
+
* 性能特性:
|
|
1480
|
+
* - 使用 Lua 脚本 (Deep Scan) 一次性获取所有候选组及其配置。
|
|
1481
|
+
* - 零网络往返延迟 (N+1 free)。
|
|
1482
|
+
* - 无本地缓存,实时响应 Redis 配置变更。
|
|
1483
|
+
*/
|
|
1460
1484
|
declare class PriorityStrategy implements DispatchStrategy {
|
|
1461
|
-
/** 本地缓存:groupId -> { priority, expiresAt } */
|
|
1462
|
-
private cache;
|
|
1463
|
-
/** 手动覆盖的优先级(优先级最高) */
|
|
1464
|
-
private overrides;
|
|
1465
1485
|
private algorithmConfig;
|
|
1466
1486
|
private defaultPriority;
|
|
1467
|
-
|
|
1468
|
-
private onGetPriority?;
|
|
1487
|
+
readonly idleInterval: number;
|
|
1469
1488
|
constructor(options?: PriorityStrategyOptions);
|
|
1470
1489
|
/**
|
|
1471
|
-
*
|
|
1472
|
-
|
|
1490
|
+
* 获取下一个可执行的任务
|
|
1491
|
+
*/
|
|
1492
|
+
acquireJob(queue: Queue<any>): Promise<ReservedJob<any> | null>;
|
|
1493
|
+
/**
|
|
1494
|
+
* 根据算法类型分发排序逻辑
|
|
1473
1495
|
*/
|
|
1474
|
-
|
|
1496
|
+
private sortGroupsByPriority;
|
|
1475
1497
|
/**
|
|
1476
|
-
*
|
|
1498
|
+
* 严格优先级排序:Priority 大的排前面
|
|
1477
1499
|
*/
|
|
1478
|
-
|
|
1500
|
+
private sortByStrict;
|
|
1479
1501
|
/**
|
|
1480
|
-
*
|
|
1481
|
-
* 优先级来源顺序:overrides > cache > onGetPriority(config) 或 config.priority
|
|
1502
|
+
* 加权随机排序:Priority 越高,排在前面的概率越大
|
|
1482
1503
|
*/
|
|
1483
|
-
private
|
|
1504
|
+
private sortByWeightedRandom;
|
|
1484
1505
|
/**
|
|
1485
|
-
*
|
|
1506
|
+
* 时间衰减排序:Priority + 等待时间加成
|
|
1486
1507
|
*/
|
|
1487
|
-
private
|
|
1508
|
+
private sortByAging;
|
|
1509
|
+
}
|
|
1510
|
+
//#endregion
|
|
1511
|
+
//#region src/strategies/round-robin-strategy.d.ts
|
|
1512
|
+
interface RoundRobinStrategyOptions {
|
|
1488
1513
|
/**
|
|
1489
|
-
*
|
|
1490
|
-
*
|
|
1514
|
+
* 每次轮询获取的候选 Group 数量
|
|
1515
|
+
* 数量越大,公平性范围越广,但 Redis 读取负载略微增加
|
|
1516
|
+
* @default 50
|
|
1491
1517
|
*/
|
|
1492
|
-
|
|
1518
|
+
batchSize?: number;
|
|
1519
|
+
pollInterval?: number;
|
|
1520
|
+
}
|
|
1521
|
+
/**
|
|
1522
|
+
* 公平轮询策略 (Round Robin)
|
|
1523
|
+
*
|
|
1524
|
+
* 适用场景:
|
|
1525
|
+
* - 多租户系统 (SaaS)
|
|
1526
|
+
* - 需要防止某个 Group 的海量任务 "饿死" 其他 Group 的场景
|
|
1527
|
+
*
|
|
1528
|
+
* 工作原理:
|
|
1529
|
+
* 1. 从 Redis 获取一批当前等待最久的 Group (Batch Fetch)
|
|
1530
|
+
* 2. 在本地对这批 Group 进行随机洗牌 (Shuffle)
|
|
1531
|
+
* 3. 依次尝试获取任务
|
|
1532
|
+
* 4. 遇到并发限制 (Limit Exceeded) 自动跳过,尝试下一个
|
|
1533
|
+
*/
|
|
1534
|
+
declare class RoundRobinStrategy implements DispatchStrategy {
|
|
1535
|
+
private batchSize;
|
|
1536
|
+
readonly idleInterval: number;
|
|
1537
|
+
constructor(options?: RoundRobinStrategyOptions);
|
|
1538
|
+
acquireJob(queue: Queue<any>): Promise<ReservedJob<any> | null>;
|
|
1493
1539
|
/**
|
|
1494
|
-
*
|
|
1540
|
+
* Fisher-Yates 洗牌算法
|
|
1541
|
+
* 将数组元素原地随机打乱
|
|
1495
1542
|
*/
|
|
1496
|
-
private
|
|
1497
|
-
getNextGroup(queue: Queue<any>): Promise<string | null>;
|
|
1543
|
+
private shuffle;
|
|
1498
1544
|
}
|
|
1499
1545
|
//#endregion
|
|
1500
|
-
export { AddOptions, AgingAlgorithmConfig, AlgorithmConfig, BackoffStrategy, BullBoardGroupMQAdapter, DispatchStrategy, FlowChildResult, FlowJob, FlowOptions, GroupMQBullBoardAdapterOptions,
|
|
1546
|
+
export { AddOptions, AgingAlgorithmConfig, AlgorithmConfig, BackoffStrategy, BullBoardGroupMQAdapter, DispatchStrategy, FlowChildResult, FlowJob, FlowOptions, GroupConfig, GroupMQBullBoardAdapterOptions, GroupOptions, Job, PriorityStrategy, PriorityStrategyOptions, Queue, QueueOptions, RepeatOptions, ReserveResult, ReservedJob, RoundRobinStrategy, RoundRobinStrategyOptions, StrictAlgorithmConfig, UnrecoverableError, WeightedRandomAlgorithmConfig, Worker, WorkerEvents, WorkerOptions, getWorkersStatus, waitForQueueToEmpty };
|
|
1501
1547
|
//# sourceMappingURL=index.d.mts.map
|