New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ringhash: don't recreate subConns when update doesn't change address information #5431
Changes from 1 commit
18cd4e9
a37c402
23fee52
a2ade6d
f0c9da2
1a46aa5
b6c6d2f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,7 +47,7 @@ type bb struct{} | |
func (bb) Build(cc balancer.ClientConn, bOpts balancer.BuildOptions) balancer.Balancer { | ||
b := &ringhashBalancer{ | ||
cc: cc, | ||
subConns: make(map[resolver.Address]*subConn), | ||
subConns: resolver.NewAddressMap(), | ||
scStates: make(map[balancer.SubConn]*subConn), | ||
csEvltr: &connectivityStateEvaluator{}, | ||
} | ||
|
@@ -180,7 +180,18 @@ type ringhashBalancer struct { | |
|
||
config *LBConfig | ||
|
||
subConns map[resolver.Address]*subConn // `attributes` is stripped from the keys of this map (the addresses) | ||
// The key for this map is a resolver.Address with the following | ||
// modification: | ||
// - `Attributes` field is cleared and rewritten with a single attribute | ||
// containing the weight of the address. The `AddressMap` type uses the | ||
// `Attributes` field to determine equality, but ignores the | ||
// `BalancerAttributes` field. Hence, we copy over the weight of the | ||
// address from the latter to the former. | ||
easwars marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// The ringhash LB policy is concerned only with the address value and its | ||
// weight, when comparing addresses received as part of a ClientConnUpdate. | ||
// | ||
// The value type stored in this map is a `*subConn`. | ||
subConns *resolver.AddressMap | ||
scStates map[balancer.SubConn]*subConn | ||
|
||
// ring is always in sync with subConns. When subConns change, a new ring is | ||
|
@@ -208,55 +219,51 @@ type ringhashBalancer struct { | |
// SubConn states are Idle. | ||
func (b *ringhashBalancer) updateAddresses(addrs []resolver.Address) bool { | ||
var addrsUpdated bool | ||
// addrsSet is the set converted from addrs, it's used for quick lookup of | ||
// an address. | ||
// | ||
// Addresses in this map all have attributes stripped, but metadata set to | ||
// the weight. So that weight change can be detected. | ||
// | ||
// TODO: this won't be necessary if there are ways to compare address | ||
// attributes. | ||
addrsSet := make(map[resolver.Address]struct{}) | ||
for _, a := range addrs { | ||
aNoAttrs := a | ||
// Strip attributes but set Metadata to the weight. | ||
aNoAttrs.Attributes = nil | ||
w := weightedroundrobin.GetAddrInfo(a).Weight | ||
if w == 0 { | ||
// addrsSet is the set converted from addrs, it's used for quick lookup of an | ||
// address. Key type here is the same as that of the `subConns` map. | ||
addrsSet := resolver.NewAddressMap() | ||
for _, addr := range addrs { | ||
addrInfo := weightedroundrobin.GetAddrInfo(addr) | ||
if addrInfo.Weight == 0 { | ||
|
||
// If weight is not set, use 1. | ||
w = 1 | ||
addrInfo.Weight = 1 | ||
} | ||
aNoAttrs.Metadata = w | ||
addrsSet[aNoAttrs] = struct{}{} | ||
if scInfo, ok := b.subConns[aNoAttrs]; !ok { | ||
modifiedAddr := addr | ||
modifiedAddr.Attributes = nil | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why clear There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you see line 240, we use the original There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have changed this to not ignore |
||
modifiedAddr = setWeightAttribute(modifiedAddr, addrInfo.Weight) | ||
addrsSet.Set(modifiedAddr, true) | ||
if val, ok := b.subConns.Get(modifiedAddr); !ok { | ||
// When creating SubConn, the original address with attributes is | ||
// passed through. So that connection configurations in attributes | ||
// (like creds) will be used. | ||
sc, err := b.cc.NewSubConn([]resolver.Address{a}, balancer.NewSubConnOptions{HealthCheckEnabled: true}) | ||
sc, err := b.cc.NewSubConn([]resolver.Address{addr}, balancer.NewSubConnOptions{HealthCheckEnabled: true}) | ||
if err != nil { | ||
logger.Warningf("base.baseBalancer: failed to create new SubConn: %v", err) | ||
continue | ||
} | ||
scs := &subConn{addr: a.Addr, sc: sc} | ||
scs := &subConn{addr: addr.Addr, sc: sc} | ||
scs.setState(connectivity.Idle) | ||
b.state = b.csEvltr.recordTransition(connectivity.Shutdown, connectivity.Idle) | ||
b.subConns[aNoAttrs] = scs | ||
b.subConns.Set(modifiedAddr, scs) | ||
b.scStates[sc] = scs | ||
addrsUpdated = true | ||
} else { | ||
// Always update the subconn's address in case the attributes | ||
// changed. The SubConn does a reflect.DeepEqual of the new and old | ||
// addresses. So this is a noop if the current address is the same | ||
// as the old one (including attributes). | ||
b.subConns[aNoAttrs] = scInfo | ||
b.cc.UpdateAddresses(scInfo.sc, []resolver.Address{a}) | ||
scInfo := val.(*subConn) | ||
b.cc.UpdateAddresses(scInfo.sc, []resolver.Address{addr}) | ||
} | ||
} | ||
for a, scInfo := range b.subConns { | ||
// a was removed by resolver. | ||
if _, ok := addrsSet[a]; !ok { | ||
for _, addr := range b.subConns.Keys() { | ||
// addr was removed by resolver. | ||
if _, ok := addrsSet.Get(addr); !ok { | ||
v, _ := b.subConns.Get(addr) | ||
scInfo := v.(*subConn) | ||
b.cc.RemoveSubConn(scInfo.sc) | ||
delete(b.subConns, a) | ||
b.subConns.Delete(addr) | ||
addrsUpdated = true | ||
// Keep the state of this sc in b.scStates until sc's state becomes Shutdown. | ||
// The entry will be deleted in UpdateSubConnState. | ||
|
@@ -304,7 +311,7 @@ func (b *ringhashBalancer) UpdateClientConnState(s balancer.ClientConnState) err | |
|
||
func (b *ringhashBalancer) ResolverError(err error) { | ||
b.resolverErr = err | ||
if len(b.subConns) == 0 { | ||
if b.subConns.Len() == 0 { | ||
b.state = connectivity.TransientFailure | ||
} | ||
|
||
|
@@ -392,7 +399,8 @@ func (b *ringhashBalancer) UpdateSubConnState(sc balancer.SubConn, state balance | |
// attempting to connect, we need to trigger one. But since the deleted | ||
// SubConn will eventually send a shutdown update, this code will run | ||
// and trigger the next SubConn to connect. | ||
for _, sc := range b.subConns { | ||
for _, v := range b.subConns.Values() { | ||
sc := v.(*subConn) | ||
if sc.isAttemptingToConnect() { | ||
return | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/* | ||
* | ||
* Copyright 2022 gRPC authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
package ringhash | ||
|
||
import "google.golang.org/grpc/resolver" | ||
|
||
// weightAttributeKey is used as the attribute key when storing the address | ||
// weight in the `Attributes` field of the address. | ||
type weightAttributeKey struct{} | ||
|
||
// setWeightAttribute returns a copy of addr in which the Attributes field is | ||
// updated with weight. | ||
func setWeightAttribute(addr resolver.Address, weight uint32) resolver.Address { | ||
addr.Attributes = addr.Attributes.WithValue(weightAttributeKey{}, weight) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Hold up, are we saying we want an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did not change the logic here wrt to what was being used from But I also seem to get your point that it doesn't seem to be right to be ignoring There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have changed the |
||
return addr | ||
} | ||
|
||
// getWeightAttribute returns the weight stored in the Attributes fields of | ||
// addr. | ||
func getWeightAttribute(addr resolver.Address) uint32 { | ||
v := addr.Attributes.Value(weightAttributeKey{}) | ||
weight, _ := v.(uint32) | ||
return weight | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's store
Keys()
in a local variable, since we range over it again later. We can also get it on the first line and uselen(keys)
instead ofsubConns.Len()
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.