Skip to content

Commit 63f6f01

Browse files
Fix bounded consistent hash with filtered endpoints (#4138)
1 parent e7d7014 commit 63f6f01

2 files changed

Lines changed: 79 additions & 0 deletions

File tree

loadbalancer/algorithm.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ func (ch *consistentHash) boundedLoadSearch(key string, balanceFactor float64, c
188188
for i := 0; i < ch.Len(); i++ {
189189
endpointIndex := ch.hashRing[ringIndex].index
190190
if skipEndpoint(ctx, endpointIndex) {
191+
ringIndex = (ringIndex + 1) % ch.Len()
191192
continue
192193
}
193194
load := ctx.Route.LBEndpoints[endpointIndex].Metrics.InflightRequests()

loadbalancer/algorithm_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,3 +701,81 @@ func BenchmarkWeightedRoundRobinAlgorithm(b *testing.B) {
701701
})
702702
}
703703
}
704+
705+
func TestConsistentHashBoundedLoadSearchSkipsFilteredEndpoints(t *testing.T) {
706+
endpoints := []string{
707+
"http://127.0.0.1:8080",
708+
"http://127.0.0.2:8080",
709+
"http://127.0.0.3:8080",
710+
}
711+
request, err := http.NewRequest("GET", "http://127.0.0.1:1234/foo", nil)
712+
require.NoError(t, err)
713+
714+
route := NewAlgorithmProvider().Do([]*routing.Route{{
715+
Route: eskip.Route{
716+
BackendType: eskip.LBBackend,
717+
LBAlgorithm: ConsistentHash.String(),
718+
LBEndpoints: eskip.NewLBEndpoints(endpoints),
719+
},
720+
}})[0]
721+
722+
endpointRegistry := routing.NewEndpointRegistry(routing.RegistryOptions{})
723+
defer endpointRegistry.Close()
724+
endpointRegistry.Do([]*routing.Route{route})
725+
726+
ch := route.LBAlgorithm.(*consistentHash)
727+
728+
var key string
729+
var selectedIndex int
730+
var skippedIndex int
731+
732+
for ringIndex, ringEntry := range ch.hashRing {
733+
nextIndex := ch.hashRing[(ringIndex+1)%ch.Len()].index
734+
if ringEntry.index == nextIndex {
735+
continue
736+
}
737+
738+
for vnode := 0; vnode < 100; vnode++ {
739+
candidateKey := fmt.Sprintf("%s-%d", endpoints[ringEntry.index], vnode)
740+
if hash(candidateKey) == ringEntry.hash {
741+
key = candidateKey
742+
selectedIndex = ringEntry.index
743+
skippedIndex = nextIndex
744+
break
745+
}
746+
}
747+
748+
if key != "" {
749+
break
750+
}
751+
}
752+
753+
require.NotEmpty(t, key)
754+
755+
filteredEndpoints := make([]routing.LBEndpoint, 0, len(route.LBEndpoints)-1)
756+
757+
for i, endpoint := range route.LBEndpoints {
758+
if i != skippedIndex {
759+
filteredEndpoints = append(filteredEndpoints, endpoint)
760+
}
761+
}
762+
763+
addInflightRequests(endpointRegistry, route.LBEndpoints[selectedIndex], 20)
764+
765+
ctx := &routing.LBContext{
766+
Request: request,
767+
Route: route,
768+
LBEndpoints: filteredEndpoints,
769+
Params: map[string]interface{}{
770+
ConsistentHashKey: key,
771+
ConsistentHashBalanceFactor: 1.25,
772+
},
773+
}
774+
775+
selected := ch.Apply(ctx)
776+
777+
if selected.Host == route.LBEndpoints[skippedIndex].Host {
778+
t.Fatalf("selected filtered endpoint %q", selected.Host)
779+
}
780+
781+
}

0 commit comments

Comments
 (0)