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 +113 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +123 -1
- package/dist/index.d.ts +123 -1
- package/dist/index.js +113 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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;
|