Smooth Weighted Round-Robin Balancer

Distribute selections across weighted nodes with the smooth weighted round-robin algorithm, keeping interleaving fair without randomness.

TypescriptResilience

Code

snippet
interface WeightedNode<T> {
  value: T;
  weight: number;
  current: number;
}

export class SmoothWeightedRoundRobin<T> {
  private readonly nodes: WeightedNode<T>[] = [];
  private totalWeight = 0;

  add(value: T, weight: number): this {
    if (!Number.isInteger(weight) || weight < 1) {
      throw new RangeError("weight must be a positive integer");
    }
    this.nodes.push({ value, weight, current: 0 });
    this.totalWeight += weight;
    return this;
  }

  remove(value: T): boolean {
    const index = this.nodes.findIndex((node) => Object.is(node.value, value));
    if (index === -1) return false;
    this.totalWeight -= this.nodes[index].weight;
    this.nodes.splice(index, 1);
    return true;
  }

  next(): T {
    if (this.nodes.length === 0) throw new Error("no nodes available");
    let best = this.nodes[0];
    for (const node of this.nodes) {
      node.current += node.weight;
      if (node.current > best.current) best = node;
    }
    best.current -= this.totalWeight;
    return best.value;
  }

  get size(): number {
    return this.nodes.length;
  }
}