Backends are not equal - a bigger box should get more traffic. Weighted round-robin gives each backend a share proportional to its weight. Today you build it by expanding weights into a repeating schedule.
Distribute selection in proportion to weight, cycling A,A,A,B for weights 3 and 1.
Weighted round-robin lets an operator send more traffic to a more capable
backend. The simplest correct way to do it is schedule expansion: repeat each
backend in a list as many times as its weight, then run ordinary round-robin over
that expanded list. Weights 3 and 1 expand to [A, A, A, B], so four consecutive
picks give three As and one B, and the fifth pick wraps to A again.
The order within the cycle here is the blunt grouped one - all of A, then B -
which is easy to reason about and exact to test. Production balancers often use a
smooth weighted round-robin instead, which interleaves the picks (A, A, B, A)
so the same 3-to-1 ratio arrives more evenly spread over time; that is a natural
extension noted in the caveats. Build the schedule from Available() so a down
backend contributes no slots, and rebuild it when weights or membership change.
// build a schedule by repeating each backend weight times, in pool order:// A(3), B(1) -> [A, A, A, B]; then round-robin over that scheduletype WeightedRR struct { pool *Pool; sched []*Backend; n int }func (w *WeightedRR) build() {w.sched = nilfor _, b := range w.pool.Available() {for i := 0; i < b.Weight(); i++ { w.sched = append(w.sched, b) }}}// Select: build once (or when the pool changes), then sched[n % len]; n++