groupmq-plus 1.1.2 → 1.1.3

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.cjs CHANGED
@@ -2765,9 +2765,122 @@ function sleep(ms) {
2765
2765
  return new Promise((r) => setTimeout(r, ms));
2766
2766
  }
2767
2767
 
2768
+ //#endregion
2769
+ //#region src/strategies/priority-strategy.ts
2770
+ var PriorityStrategy = class {
2771
+ constructor(options = {}) {
2772
+ this.cache = /* @__PURE__ */ new Map();
2773
+ this.overrides = /* @__PURE__ */ new Map();
2774
+ this.algorithmConfig = options.algorithm ?? { type: "weighted-random" };
2775
+ this.defaultPriority = options.defaultPriority ?? 1;
2776
+ this.cacheTtlMs = options.cacheTtlMs ?? 5e3;
2777
+ this.onGetPriority = options.onGetPriority;
2778
+ }
2779
+ /**
2780
+ * 手动覆盖某个组的优先级(优先级高于 Redis 配置和 getPriority)
2781
+ * 主要用于测试或临时调整
2782
+ */
2783
+ setPriority(groupId, priority) {
2784
+ this.overrides.set(groupId, priority);
2785
+ }
2786
+ /**
2787
+ * 清除手动覆盖,恢复使用 Redis 配置或 getPriority
2788
+ */
2789
+ clearPriority(groupId) {
2790
+ this.overrides.delete(groupId);
2791
+ }
2792
+ /**
2793
+ * 获取组的基础优先级
2794
+ * 优先级来源顺序:overrides > cache > onGetPriority(config) 或 config.priority
2795
+ */
2796
+ async resolvePriority(queue, groupId) {
2797
+ const override = this.overrides.get(groupId);
2798
+ if (override !== void 0) return override;
2799
+ const now = Date.now();
2800
+ const cached = this.cache.get(groupId);
2801
+ if (cached && cached.expiresAt > now) return cached.priority;
2802
+ const config = await queue.getGroupConfig(groupId);
2803
+ let priority;
2804
+ if (this.onGetPriority) priority = await this.onGetPriority(groupId, config);
2805
+ else priority = config.priority !== 1 ? config.priority : this.defaultPriority;
2806
+ if (this.cacheTtlMs > 0) this.cache.set(groupId, {
2807
+ priority,
2808
+ expiresAt: now + this.cacheTtlMs
2809
+ });
2810
+ return priority;
2811
+ }
2812
+ /**
2813
+ * 严格优先级算法:总是返回优先级最高的组
2814
+ */
2815
+ selectByStrict(groups) {
2816
+ groups.sort((a, b) => b.priority - a.priority);
2817
+ return groups[0].groupId;
2818
+ }
2819
+ /**
2820
+ * 加权随机算法:根据优先级计算概率选择
2821
+ * 使用 minWeightRatio 确保低优先级组也有机会
2822
+ */
2823
+ selectByWeightedRandom(groups) {
2824
+ if (groups.length === 1) return groups[0].groupId;
2825
+ const minWeightRatio = this.algorithmConfig.minWeightRatio ?? .1;
2826
+ const minWeight = Math.max(...groups.map((g) => g.priority)) * minWeightRatio;
2827
+ const weights = groups.map((g) => ({
2828
+ groupId: g.groupId,
2829
+ weight: Math.max(g.priority, minWeight)
2830
+ }));
2831
+ const totalWeight = weights.reduce((sum, w) => sum + w.weight, 0);
2832
+ let random = Math.random() * totalWeight;
2833
+ for (const w of weights) {
2834
+ random -= w.weight;
2835
+ if (random <= 0) return w.groupId;
2836
+ }
2837
+ return groups[0].groupId;
2838
+ }
2839
+ /**
2840
+ * 时间衰减算法:等待时间越长,优先级加成越高
2841
+ */
2842
+ selectByAging(groups) {
2843
+ const now = Date.now();
2844
+ const intervalMs = this.algorithmConfig.intervalMs ?? 6e4;
2845
+ const adjustedGroups = groups.map((g) => {
2846
+ let ageBonus = 0;
2847
+ if (g.oldestTimestamp) {
2848
+ const waitTime = now - g.oldestTimestamp;
2849
+ ageBonus = Math.floor(waitTime / intervalMs);
2850
+ }
2851
+ return {
2852
+ groupId: g.groupId,
2853
+ adjustedPriority: g.priority + ageBonus
2854
+ };
2855
+ });
2856
+ adjustedGroups.sort((a, b) => b.adjustedPriority - a.adjustedPriority);
2857
+ return adjustedGroups[0].groupId;
2858
+ }
2859
+ async getNextGroup(queue) {
2860
+ const readyGroups = await queue.getReadyGroups(0, 100);
2861
+ if (readyGroups.length === 0) return null;
2862
+ const groupInfos = await Promise.all(readyGroups.map(async (groupId) => {
2863
+ const priority = await this.resolvePriority(queue, groupId);
2864
+ const info = {
2865
+ groupId,
2866
+ priority
2867
+ };
2868
+ if (this.algorithmConfig.type === "aging") info.oldestTimestamp = await queue.getGroupOldestTimestamp(groupId);
2869
+ return info;
2870
+ }));
2871
+ switch (this.algorithmConfig.type) {
2872
+ case "strict": return this.selectByStrict(groupInfos);
2873
+ case "aging": return this.selectByAging(groupInfos);
2874
+ case "weighted-random":
2875
+ default: return this.selectByWeightedRandom(groupInfos);
2876
+ }
2877
+ }
2878
+ };
2879
+
2768
2880
  //#endregion
2769
2881
  exports.BullBoardGroupMQAdapter = BullBoardGroupMQAdapter;
2770
2882
  exports.Job = Job;
2883
+ exports.PriorityStrategy = PriorityStrategy;
2771
2884
  exports.Queue = Queue;
2772
2885
  exports.UnrecoverableError = UnrecoverableError;
2773
2886
  exports.Worker = Worker;