Skip to content

Commit 33faea8

Browse files
authored
ringhash: fix normalizeWeights (#7156)
1 parent 0756c0d commit 33faea8

File tree

3 files changed

+61
-24
lines changed

3 files changed

+61
-24
lines changed

xds/internal/balancer/ringhash/ring.go

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -116,30 +116,37 @@ func newRing(subConns *resolver.AddressMap, minRingSize, maxRingSize uint64, log
116116
return &ring{items: items}
117117
}
118118

119-
// normalizeWeights divides all the weights by the sum, so that the total weight
120-
// is 1.
119+
// normalizeWeights calculates the normalized weights for each subConn in the
120+
// given subConns map. It returns a slice of subConnWithWeight structs, where
121+
// each struct contains a subConn and its corresponding weight. The function
122+
// also returns the minimum weight among all subConns.
123+
//
124+
// The normalized weight of each subConn is calculated by dividing its weight
125+
// attribute by the sum of all subConn weights. If the weight attribute is not
126+
// found on the address, a default weight of 1 is used.
127+
//
128+
// The addresses are sorted in ascending order to ensure consistent results.
121129
//
122130
// Must be called with a non-empty subConns map.
123131
func normalizeWeights(subConns *resolver.AddressMap) ([]subConnWithWeight, float64) {
124132
var weightSum uint32
125-
keys := subConns.Keys()
126-
for _, a := range keys {
127-
weightSum += getWeightAttribute(a)
133+
// Since attributes are explicitly ignored in the AddressMap key, we need to
134+
// iterate over the values to get the weights.
135+
scVals := subConns.Values()
136+
for _, a := range scVals {
137+
weightSum += a.(*subConn).weight
128138
}
129-
ret := make([]subConnWithWeight, 0, len(keys))
130-
min := float64(1.0)
131-
for _, a := range keys {
132-
v, _ := subConns.Get(a)
133-
scInfo := v.(*subConn)
134-
// getWeightAttribute() returns 1 if the weight attribute is not found
135-
// on the address. And since this function is guaranteed to be called
136-
// with a non-empty subConns map, weightSum is guaranteed to be
137-
// non-zero. So, we need not worry about divide a by zero error here.
138-
nw := float64(getWeightAttribute(a)) / float64(weightSum)
139+
ret := make([]subConnWithWeight, 0, subConns.Len())
140+
min := 1.0
141+
for _, a := range scVals {
142+
scInfo := a.(*subConn)
143+
// (*subConn).weight is set to 1 if the weight attribute is not found on
144+
// the address. And since this function is guaranteed to be called with
145+
// a non-empty subConns map, weightSum is guaranteed to be non-zero. So,
146+
// we need not worry about divide by zero error here.
147+
nw := float64(scInfo.weight) / float64(weightSum)
139148
ret = append(ret, subConnWithWeight{sc: scInfo, weight: nw})
140-
if nw < min {
141-
min = nw
142-
}
149+
min = math.Min(min, nw)
143150
}
144151
// Sort the addresses to return consistent results.
145152
//

xds/internal/balancer/ringhash/ring_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ func init() {
3838
testAddr("c", 4),
3939
}
4040
testSubConnMap = resolver.NewAddressMap()
41-
testSubConnMap.Set(testAddrs[0], &subConn{addr: "a"})
42-
testSubConnMap.Set(testAddrs[1], &subConn{addr: "b"})
43-
testSubConnMap.Set(testAddrs[2], &subConn{addr: "c"})
41+
testSubConnMap.Set(testAddrs[0], &subConn{addr: "a", weight: 3})
42+
testSubConnMap.Set(testAddrs[1], &subConn{addr: "b", weight: 3})
43+
testSubConnMap.Set(testAddrs[2], &subConn{addr: "c", weight: 4})
4444
}
4545

4646
func testAddr(addr string, weight uint32) resolver.Address {

xds/internal/balancer/ringhash/ringhash_test.go

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,17 +344,26 @@ func (s) TestThreeSubConnsAffinityMultiple(t *testing.T) {
344344
}
345345
}
346346

347+
// TestAddrWeightChange covers the following scenarios after setting up the
348+
// balancer with 3 addresses [A, B, C]:
349+
// - updates balancer with [A, B, C], a new Picker should not be sent.
350+
// - updates balancer with [A, B] (C removed), a new Picker is sent and the
351+
// ring is updated.
352+
// - updates balancer with [A, B], but B has a weight of 2, a new Picker is
353+
// sent. And the new ring should contain the correct number of entries
354+
// and weights.
347355
func (s) TestAddrWeightChange(t *testing.T) {
348-
wantAddrs := []resolver.Address{
356+
addrs := []resolver.Address{
349357
{Addr: testBackendAddrStrs[0]},
350358
{Addr: testBackendAddrStrs[1]},
351359
{Addr: testBackendAddrStrs[2]},
352360
}
353-
cc, b, p0 := setupTest(t, wantAddrs)
361+
cc, b, p0 := setupTest(t, addrs)
354362
ring0 := p0.(*picker).ring
355363

364+
// Update with the same addresses, should not send a new Picker.
356365
if err := b.UpdateClientConnState(balancer.ClientConnState{
357-
ResolverState: resolver.State{Addresses: wantAddrs},
366+
ResolverState: resolver.State{Addresses: addrs},
358367
BalancerConfig: testConfig,
359368
}); err != nil {
360369
t.Fatalf("UpdateClientConnState returned err: %v", err)
@@ -407,6 +416,27 @@ func (s) TestAddrWeightChange(t *testing.T) {
407416
if p2.(*picker).ring == ring1 {
408417
t.Fatalf("new picker after changing address weight has the same ring as before, want different")
409418
}
419+
// With the new update, the ring must look like this:
420+
// [
421+
// {idx:0 sc: {addr: testBackendAddrStrs[0], weight: 1}},
422+
// {idx:1 sc: {addr: testBackendAddrStrs[1], weight: 2}},
423+
// {idx:2 sc: {addr: testBackendAddrStrs[2], weight: 2}},
424+
// ].
425+
if len(p2.(*picker).ring.items) != 3 {
426+
t.Fatalf("new picker after changing address weight has %d entries, want 3", len(p2.(*picker).ring.items))
427+
}
428+
for _, i := range p2.(*picker).ring.items {
429+
if i.sc.addr == testBackendAddrStrs[0] {
430+
if i.sc.weight != 1 {
431+
t.Fatalf("new picker after changing address weight has weight %d for %v, want 1", i.sc.weight, i.sc.addr)
432+
}
433+
}
434+
if i.sc.addr == testBackendAddrStrs[1] {
435+
if i.sc.weight != 2 {
436+
t.Fatalf("new picker after changing address weight has weight %d for %v, want 2", i.sc.weight, i.sc.addr)
437+
}
438+
}
439+
}
410440
}
411441

412442
// TestSubConnToConnectWhenOverallTransientFailure covers the situation when the

0 commit comments

Comments
 (0)