Skip to content

Commit 1448ba4

Browse files
authored
extproc: pick random backend when multiple unweighted ones (#402)
**Commit Message** Right now if there are multiple, unweighted `backendRefs` in a [AIGatewayRouteRule], the first one is always returned. The correct behavior is to return a random one since they're essentially all weighted the same. [AIGatewayRouteRule]: https://envoy-ai-gateway.netlify.app/docs/api/#aigatewayrouterule **Related Issues/PRs (if applicable)** contributes to #53 Signed-off-by: David Xia <[email protected]>
1 parent 05043a2 commit 1448ba4

File tree

2 files changed

+25
-1
lines changed

2 files changed

+25
-1
lines changed

internal/extproc/router/router.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,12 @@ func (r *router) selectBackendFromRule(rule *filterapi.RouteRule) (backend *filt
5757
for _, b := range rule.Backends {
5858
totalWeight += b.Weight
5959
}
60+
61+
// Pick a random backend if none of them have a weight.
6062
if totalWeight == 0 {
61-
return &rule.Backends[0]
63+
return &rule.Backends[r.rng.Intn(len(rule.Backends))]
6264
}
65+
6366
selected := r.rng.Intn(totalWeight)
6467
for i := range rule.Backends {
6568
b := &rule.Backends[i]

internal/extproc/router/router_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ func TestRouter_Calculate(t *testing.T) {
5151
{Name: "x-model-name", Value: "llama3.3333"},
5252
},
5353
},
54+
{
55+
Backends: []filterapi.Backend{
56+
{Name: "baz", Schema: outSchema},
57+
{Name: "qux", Schema: outSchema},
58+
},
59+
Headers: []filterapi.HeaderMatch{
60+
{Name: "x-model-name", Value: "o1"},
61+
},
62+
},
5463
{
5564
Backends: []filterapi.Backend{
5665
{Name: "openai", Schema: outSchema},
@@ -76,6 +85,18 @@ func TestRouter_Calculate(t *testing.T) {
7685
require.Equal(t, "openai", b.Name)
7786
require.Equal(t, outSchema, b.Schema)
7887
})
88+
t.Run("matching rule - multiple unweighted backend choices", func(t *testing.T) {
89+
chosenNames := make(map[string]int)
90+
for i := 0; i < 1000; i++ {
91+
b, err := r.Calculate(map[string]string{"x-model-name": "o1"})
92+
require.NoError(t, err)
93+
chosenNames[b.Name]++
94+
require.Contains(t, []string{"baz", "qux"}, b.Name)
95+
require.Equal(t, outSchema, b.Schema)
96+
}
97+
require.InDelta(t, 500, chosenNames["qux"], 50)
98+
require.InDelta(t, 500, chosenNames["baz"], 50)
99+
})
79100
t.Run("matching rule - multiple backend choices", func(t *testing.T) {
80101
chosenNames := make(map[string]int)
81102
for i := 0; i < 1000; i++ {

0 commit comments

Comments
 (0)