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 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<ReservedJob<T> | null>;
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
- * 决定下一个应该处理的 Group ID。
975
- * 如果返回 null,表示根据策略当前没有合适的 Group 需要处理(或者队列为空)。
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
- getNextGroup(queue: Queue<any>): Promise<string | null>;
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
- type GroupConfig = {
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
- * 调度算法配置,默认 { type: 'weighted-random' }
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
- /** 默认优先级(未配置的组使用此值),默认 1 */
1461
+ /**
1462
+ * 默认优先级(当无法从 Redis 获取配置时的回退值)
1463
+ * @default 1
1464
+ */
1444
1465
  defaultPriority?: number;
1445
- /** 优先级缓存 TTL(毫秒),默认 5000ms。设为 0 禁用缓存 */
1446
- cacheTtlMs?: number;
1447
1466
  /**
1448
- * 自定义优先级计算函数
1449
- * 如果提供,将使用此函数计算优先级,否则直接使用 config.priority
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
- onGetPriority?: OnGetPriority;
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
- private cacheTtlMs;
1468
- private onGetPriority?;
1487
+ readonly idleInterval: number;
1469
1488
  constructor(options?: PriorityStrategyOptions);
1470
1489
  /**
1471
- * 手动覆盖某个组的优先级(优先级高于 Redis 配置和 getPriority)
1472
- * 主要用于测试或临时调整
1490
+ * 获取下一个可执行的任务
1491
+ */
1492
+ acquireJob(queue: Queue<any>): Promise<ReservedJob<any> | null>;
1493
+ /**
1494
+ * 根据算法类型分发排序逻辑
1473
1495
  */
1474
- setPriority(groupId: string, priority: number): void;
1496
+ private sortGroupsByPriority;
1475
1497
  /**
1476
- * 清除手动覆盖,恢复使用 Redis 配置或 getPriority
1498
+ * 严格优先级排序:Priority 大的排前面
1477
1499
  */
1478
- clearPriority(groupId: string): void;
1500
+ private sortByStrict;
1479
1501
  /**
1480
- * 获取组的基础优先级
1481
- * 优先级来源顺序:overrides > cache > onGetPriority(config) 或 config.priority
1502
+ * 加权随机排序:Priority 越高,排在前面的概率越大
1482
1503
  */
1483
- private resolvePriority;
1504
+ private sortByWeightedRandom;
1484
1505
  /**
1485
- * 严格优先级算法:总是返回优先级最高的组
1506
+ * 时间衰减排序:Priority + 等待时间加成
1486
1507
  */
1487
- private selectByStrict;
1508
+ private sortByAging;
1509
+ }
1510
+ //#endregion
1511
+ //#region src/strategies/round-robin-strategy.d.ts
1512
+ interface RoundRobinStrategyOptions {
1488
1513
  /**
1489
- * 加权随机算法:根据优先级计算概率选择
1490
- * 使用 minWeightRatio 确保低优先级组也有机会
1514
+ * 每次轮询获取的候选 Group 数量
1515
+ * 数量越大,公平性范围越广,但 Redis 读取负载略微增加
1516
+ * @default 50
1491
1517
  */
1492
- private selectByWeightedRandom;
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 selectByAging;
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, Job, OnGetPriority, PriorityStrategy, PriorityStrategyOptions, Queue, QueueOptions, RepeatOptions, ReservedJob, StrictAlgorithmConfig, UnrecoverableError, WeightedRandomAlgorithmConfig, Worker, WorkerEvents, WorkerOptions, getWorkersStatus, waitForQueueToEmpty };
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