diff --git a/caddyhttp/proxy/policy.go b/caddyhttp/proxy/policy.go index 96e382a5c..0bf657cae 100644 --- a/caddyhttp/proxy/policy.go +++ b/caddyhttp/proxy/policy.go @@ -2,7 +2,7 @@ package proxy import ( "math/rand" - "sync/atomic" + "sync" ) // HostPool is a collection of UpstreamHosts. @@ -82,20 +82,22 @@ func (r *LeastConn) Select(pool HostPool) *UpstreamHost { // RoundRobin is a policy that selects hosts based on round robin ordering. type RoundRobin struct { - Robin uint32 + robin uint32 + mutex sync.Mutex } // Select selects an up host from the pool using a round robin ordering scheme. func (r *RoundRobin) Select(pool HostPool) *UpstreamHost { poolLen := uint32(len(pool)) - selection := atomic.AddUint32(&r.Robin, 1) % poolLen - host := pool[selection] - // if the currently selected host is not available, just ffwd to up host - for i := uint32(1); !host.Available() && i < poolLen; i++ { - host = pool[(selection+i)%poolLen] + r.mutex.Lock() + defer r.mutex.Unlock() + // Return next available host + for i := uint32(0); i < poolLen; i++ { + r.robin++ + host := pool[r.robin%poolLen] + if host.Available() { + return host + } } - if !host.Available() { - return nil - } - return host + return nil } diff --git a/caddyhttp/proxy/policy_test.go b/caddyhttp/proxy/policy_test.go index 4cc05f029..4ef591195 100644 --- a/caddyhttp/proxy/policy_test.go +++ b/caddyhttp/proxy/policy_test.go @@ -63,11 +63,18 @@ func TestRoundRobinPolicy(t *testing.T) { if h != pool[2] { t.Error("Expected to skip down host.") } - // mark host as full - pool[2].Conns = 1 - pool[2].MaxConns = 1 + // mark host as up + pool[1].Unhealthy = false + h = rrPolicy.Select(pool) - if h != pool[0] { + if h == pool[2] { + t.Error("Expected to balance evenly among healthy hosts") + } + // mark host as full + pool[1].Conns = 1 + pool[1].MaxConns = 1 + h = rrPolicy.Select(pool) + if h != pool[2] { t.Error("Expected to skip full host.") } }