xds: eds balancer handleEDSUpdate cleanup (#3639)
This commit is contained in:
@ -23,7 +23,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"google.golang.org/grpc/balancer"
|
"google.golang.org/grpc/balancer"
|
||||||
@ -37,8 +36,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultTimeout = 10 * time.Second
|
edsName = "eds_experimental"
|
||||||
edsName = "eds_experimental"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -62,13 +60,13 @@ func (b *edsBalancerBuilder) Build(cc balancer.ClientConn, opts balancer.BuildOp
|
|||||||
cc: cc,
|
cc: cc,
|
||||||
buildOpts: opts,
|
buildOpts: opts,
|
||||||
grpcUpdate: make(chan interface{}),
|
grpcUpdate: make(chan interface{}),
|
||||||
xdsClientUpdate: make(chan interface{}),
|
xdsClientUpdate: make(chan *edsUpdate),
|
||||||
childPolicyUpdate: buffer.NewUnbounded(),
|
childPolicyUpdate: buffer.NewUnbounded(),
|
||||||
}
|
}
|
||||||
loadStore := lrs.NewStore()
|
loadStore := lrs.NewStore()
|
||||||
x.logger = grpclog.NewPrefixLogger(loggingPrefix(x))
|
x.logger = grpclog.NewPrefixLogger(loggingPrefix(x))
|
||||||
x.edsImpl = newEDSBalancer(x.cc, x.enqueueChildBalancerState, loadStore, x.logger)
|
x.edsImpl = newEDSBalancer(x.cc, x.enqueueChildBalancerState, loadStore, x.logger)
|
||||||
x.client = newXDSClientWrapper(x.handleEDSUpdate, x.loseContact, x.buildOpts, loadStore, x.logger)
|
x.client = newXDSClientWrapper(x.handleEDSUpdate, x.buildOpts, loadStore, x.logger)
|
||||||
x.logger.Infof("Created")
|
x.logger.Infof("Created")
|
||||||
go x.run()
|
go x.run()
|
||||||
return x
|
return x
|
||||||
@ -117,7 +115,7 @@ type edsBalancer struct {
|
|||||||
|
|
||||||
// edsBalancer continuously monitor the channels below, and will handle events from them in sync.
|
// edsBalancer continuously monitor the channels below, and will handle events from them in sync.
|
||||||
grpcUpdate chan interface{}
|
grpcUpdate chan interface{}
|
||||||
xdsClientUpdate chan interface{}
|
xdsClientUpdate chan *edsUpdate
|
||||||
childPolicyUpdate *buffer.Unbounded
|
childPolicyUpdate *buffer.Unbounded
|
||||||
|
|
||||||
client *xdsclientWrapper // may change when passed a different service config
|
client *xdsclientWrapper // may change when passed a different service config
|
||||||
@ -140,12 +138,8 @@ func (x *edsBalancer) run() {
|
|||||||
u := update.(*balancerStateWithPriority)
|
u := update.(*balancerStateWithPriority)
|
||||||
x.edsImpl.updateState(u.priority, u.s)
|
x.edsImpl.updateState(u.priority, u.s)
|
||||||
case <-x.ctx.Done():
|
case <-x.ctx.Done():
|
||||||
if x.client != nil {
|
x.client.close()
|
||||||
x.client.close()
|
x.edsImpl.close()
|
||||||
}
|
|
||||||
if x.edsImpl != nil {
|
|
||||||
x.edsImpl.close()
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,9 +148,7 @@ func (x *edsBalancer) run() {
|
|||||||
func (x *edsBalancer) handleGRPCUpdate(update interface{}) {
|
func (x *edsBalancer) handleGRPCUpdate(update interface{}) {
|
||||||
switch u := update.(type) {
|
switch u := update.(type) {
|
||||||
case *subConnStateUpdate:
|
case *subConnStateUpdate:
|
||||||
if x.edsImpl != nil {
|
x.edsImpl.handleSubConnStateChange(u.sc, u.state.ConnectivityState)
|
||||||
x.edsImpl.handleSubConnStateChange(u.sc, u.state.ConnectivityState)
|
|
||||||
}
|
|
||||||
case *balancer.ClientConnState:
|
case *balancer.ClientConnState:
|
||||||
x.logger.Infof("Receive update from resolver, balancer config: %+v", u.BalancerConfig)
|
x.logger.Infof("Receive update from resolver, balancer config: %+v", u.BalancerConfig)
|
||||||
cfg, _ := u.BalancerConfig.(*EDSConfig)
|
cfg, _ := u.BalancerConfig.(*EDSConfig)
|
||||||
@ -174,7 +166,7 @@ func (x *edsBalancer) handleGRPCUpdate(update interface{}) {
|
|||||||
|
|
||||||
// We will update the edsImpl with the new child policy, if we got a
|
// We will update the edsImpl with the new child policy, if we got a
|
||||||
// different one.
|
// different one.
|
||||||
if x.edsImpl != nil && !cmp.Equal(cfg.ChildPolicy, x.config.ChildPolicy) {
|
if !cmp.Equal(cfg.ChildPolicy, x.config.ChildPolicy) {
|
||||||
if cfg.ChildPolicy != nil {
|
if cfg.ChildPolicy != nil {
|
||||||
x.edsImpl.handleChildPolicy(cfg.ChildPolicy.Name, cfg.ChildPolicy.Config)
|
x.edsImpl.handleChildPolicy(cfg.ChildPolicy.Name, cfg.ChildPolicy.Config)
|
||||||
} else {
|
} else {
|
||||||
@ -189,17 +181,13 @@ func (x *edsBalancer) handleGRPCUpdate(update interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *edsBalancer) handleXDSClientUpdate(update interface{}) {
|
func (x *edsBalancer) handleXDSClientUpdate(update *edsUpdate) {
|
||||||
switch u := update.(type) {
|
if update.err != nil {
|
||||||
// TODO: this func should accept (xdsclient.EndpointsUpdate, error), and process
|
// TODO: handle errors from EDS callback. E.g. if CDS resource is
|
||||||
// the error, instead of having a separate loseContact signal.
|
// removed, the EDS watch should be canceled.
|
||||||
case xdsclient.EndpointsUpdate:
|
return
|
||||||
x.edsImpl.handleEDSResponse(u)
|
|
||||||
case *loseContact:
|
|
||||||
// loseContact can be useful for going into fallback.
|
|
||||||
default:
|
|
||||||
panic("unexpected xds client update type")
|
|
||||||
}
|
}
|
||||||
|
x.edsImpl.handleEDSResponse(update.resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
type subConnStateUpdate struct {
|
type subConnStateUpdate struct {
|
||||||
@ -233,24 +221,14 @@ func (x *edsBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *edsBalancer) handleEDSUpdate(resp xdsclient.EndpointsUpdate) error {
|
type edsUpdate struct {
|
||||||
// TODO: this function should take (resp, error), and send them together on
|
resp xdsclient.EndpointsUpdate
|
||||||
// the channel. There doesn't need to be a separate `loseContact` function.
|
err error
|
||||||
select {
|
|
||||||
case x.xdsClientUpdate <- resp:
|
|
||||||
case <-x.ctx.Done():
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type loseContact struct {
|
func (x *edsBalancer) handleEDSUpdate(resp xdsclient.EndpointsUpdate, err error) {
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: delete loseContact when handleEDSUpdate takes (resp, error).
|
|
||||||
func (x *edsBalancer) loseContact() {
|
|
||||||
select {
|
select {
|
||||||
case x.xdsClientUpdate <- &loseContact{}:
|
case x.xdsClientUpdate <- &edsUpdate{resp: resp, err: err}:
|
||||||
case <-x.ctx.Done():
|
case <-x.ctx.Done():
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -412,7 +412,7 @@ func (s) TestEDS_UpdateSubBalancerName(t *testing.T) {
|
|||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
_, err := p0.Pick(balancer.PickInfo{})
|
_, err := p0.Pick(balancer.PickInfo{})
|
||||||
if err != testutils.ErrTestConstPicker {
|
if err != testutils.ErrTestConstPicker {
|
||||||
t.Fatalf("picker.Pick, got err %q, want err %q", err, testutils.ErrTestConstPicker)
|
t.Fatalf("picker.Pick, got err %+v, want err %+v", err, testutils.ErrTestConstPicker)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,10 +108,12 @@ func (f *fakeEDSBalancer) handleChildPolicy(name string, config json.RawMessage)
|
|||||||
f.childPolicy.Send(&loadBalancingConfig{Name: name, Config: config})
|
f.childPolicy.Send(&loadBalancingConfig{Name: name, Config: config})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeEDSBalancer) close() {}
|
|
||||||
func (f *fakeEDSBalancer) handleEDSResponse(edsResp xdsclient.EndpointsUpdate) {}
|
func (f *fakeEDSBalancer) handleEDSResponse(edsResp xdsclient.EndpointsUpdate) {}
|
||||||
|
|
||||||
func (f *fakeEDSBalancer) updateState(priority priorityType, s balancer.State) {}
|
func (f *fakeEDSBalancer) updateState(priority priorityType, s balancer.State) {}
|
||||||
|
|
||||||
|
func (f *fakeEDSBalancer) close() {}
|
||||||
|
|
||||||
func (f *fakeEDSBalancer) waitForChildPolicy(wantPolicy *loadBalancingConfig) error {
|
func (f *fakeEDSBalancer) waitForChildPolicy(wantPolicy *loadBalancingConfig) error {
|
||||||
val, err := f.childPolicy.Receive()
|
val, err := f.childPolicy.Receive()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -50,8 +50,7 @@ var (
|
|||||||
type xdsclientWrapper struct {
|
type xdsclientWrapper struct {
|
||||||
logger *grpclog.PrefixLogger
|
logger *grpclog.PrefixLogger
|
||||||
|
|
||||||
newEDSUpdate func(xdsclient.EndpointsUpdate) error
|
newEDSUpdate func(xdsclient.EndpointsUpdate, error)
|
||||||
loseContact func()
|
|
||||||
bbo balancer.BuildOptions
|
bbo balancer.BuildOptions
|
||||||
loadStore lrs.Store
|
loadStore lrs.Store
|
||||||
|
|
||||||
@ -78,11 +77,10 @@ type xdsclientWrapper struct {
|
|||||||
//
|
//
|
||||||
// The given callbacks won't be called until the underlying xds_client is
|
// The given callbacks won't be called until the underlying xds_client is
|
||||||
// working and sends updates.
|
// working and sends updates.
|
||||||
func newXDSClientWrapper(newEDSUpdate func(xdsclient.EndpointsUpdate) error, loseContact func(), bbo balancer.BuildOptions, loadStore lrs.Store, logger *grpclog.PrefixLogger) *xdsclientWrapper {
|
func newXDSClientWrapper(newEDSUpdate func(xdsclient.EndpointsUpdate, error), bbo balancer.BuildOptions, loadStore lrs.Store, logger *grpclog.PrefixLogger) *xdsclientWrapper {
|
||||||
return &xdsclientWrapper{
|
return &xdsclientWrapper{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
newEDSUpdate: newEDSUpdate,
|
newEDSUpdate: newEDSUpdate,
|
||||||
loseContact: loseContact,
|
|
||||||
bbo: bbo,
|
bbo: bbo,
|
||||||
loadStore: loadStore,
|
loadStore: loadStore,
|
||||||
}
|
}
|
||||||
@ -188,16 +186,8 @@ func (c *xdsclientWrapper) startEndpointsWatch(nameToWatch string) {
|
|||||||
c.cancelEndpointsWatch()
|
c.cancelEndpointsWatch()
|
||||||
}
|
}
|
||||||
cancelEDSWatch := c.xdsclient.WatchEndpoints(c.edsServiceName, func(update xdsclient.EndpointsUpdate, err error) {
|
cancelEDSWatch := c.xdsclient.WatchEndpoints(c.edsServiceName, func(update xdsclient.EndpointsUpdate, err error) {
|
||||||
if err != nil {
|
|
||||||
// TODO: this should trigger a call to `c.loseContact`, when the
|
|
||||||
// error indicates "lose contact".
|
|
||||||
c.logger.Warningf("Watch error from xds-client %p: %v", c.xdsclient, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.logger.Infof("Watch update from xds-client %p, content: %+v", c.xdsclient, update)
|
c.logger.Infof("Watch update from xds-client %p, content: %+v", c.xdsclient, update)
|
||||||
if err := c.newEDSUpdate(update); err != nil {
|
c.newEDSUpdate(update, err)
|
||||||
c.logger.Warningf("xds: processing new EDS update failed due to %v.", err)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
c.logger.Infof("Watch started on resource name %v with xds-client %p", c.edsServiceName, c.xdsclient)
|
c.logger.Infof("Watch started on resource name %v with xds-client %p", c.edsServiceName, c.xdsclient)
|
||||||
c.cancelEndpointsWatch = func() {
|
c.cancelEndpointsWatch = func() {
|
||||||
|
@ -64,7 +64,7 @@ func (s) TestClientWrapperWatchEDS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
cw := newXDSClientWrapper(nil, nil, balancer.BuildOptions{Target: resolver.Target{Endpoint: testServiceName}}, nil, nil)
|
cw := newXDSClientWrapper(nil, balancer.BuildOptions{Target: resolver.Target{Endpoint: testServiceName}}, nil, nil)
|
||||||
defer cw.close()
|
defer cw.close()
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
@ -159,12 +159,11 @@ func (s) TestClientWrapperWatchEDS(t *testing.T) {
|
|||||||
// edsBalancer with the received error.
|
// edsBalancer with the received error.
|
||||||
func (s) TestClientWrapperHandleUpdateError(t *testing.T) {
|
func (s) TestClientWrapperHandleUpdateError(t *testing.T) {
|
||||||
edsRespChan := testutils.NewChannel()
|
edsRespChan := testutils.NewChannel()
|
||||||
newEDS := func(update xdsclient.EndpointsUpdate) error {
|
newEDS := func(update xdsclient.EndpointsUpdate, err error) {
|
||||||
edsRespChan.Send(update)
|
edsRespChan.Send(&edsUpdate{resp: update, err: err})
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cw := newXDSClientWrapper(newEDS, nil, balancer.BuildOptions{Target: resolver.Target{Endpoint: testServiceName}}, nil, nil)
|
cw := newXDSClientWrapper(newEDS, balancer.BuildOptions{Target: resolver.Target{Endpoint: testServiceName}}, nil, nil)
|
||||||
defer cw.close()
|
defer cw.close()
|
||||||
|
|
||||||
xdsC := fakeclient.NewClient()
|
xdsC := fakeclient.NewClient()
|
||||||
@ -176,14 +175,20 @@ func (s) TestClientWrapperHandleUpdateError(t *testing.T) {
|
|||||||
if gotCluster != testEDSClusterName {
|
if gotCluster != testEDSClusterName {
|
||||||
t.Fatalf("xdsClient.WatchEndpoints() called with cluster: %v, want %v", gotCluster, testEDSClusterName)
|
t.Fatalf("xdsClient.WatchEndpoints() called with cluster: %v, want %v", gotCluster, testEDSClusterName)
|
||||||
}
|
}
|
||||||
xdsC.InvokeWatchEDSCallback(xdsclient.EndpointsUpdate{}, errors.New("EDS watch callback error"))
|
watchErr := errors.New("EDS watch callback error")
|
||||||
|
xdsC.InvokeWatchEDSCallback(xdsclient.EndpointsUpdate{}, watchErr)
|
||||||
|
|
||||||
// The callback is called with an error, expect no update from edsRespChan.
|
// The callback is called with an error, expect no update from edsRespChan.
|
||||||
//
|
//
|
||||||
// TODO: check for loseContact() when errors indicating "lose contact" are
|
// TODO: check for loseContact() when errors indicating "lose contact" are
|
||||||
// handled correctly.
|
// handled correctly.
|
||||||
if gotUpdate, gotErr := edsRespChan.Receive(); gotErr != testutils.ErrRecvTimeout {
|
gotUpdate, err := edsRespChan.Receive()
|
||||||
t.Fatalf("edsBalancer got edsUpdate {%+v, %v}, when none was expected", gotUpdate, gotErr)
|
if err != nil {
|
||||||
|
t.Fatalf("edsBalancer failed to get edsUpdate %v", err)
|
||||||
|
}
|
||||||
|
update := gotUpdate.(*edsUpdate)
|
||||||
|
if !cmp.Equal(update.resp, (xdsclient.EndpointsUpdate{})) || update.err != watchErr {
|
||||||
|
t.Fatalf("want update {nil, %v}, got %+v", watchErr, update)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +203,7 @@ func (s) TestClientWrapperGetsXDSClientInAttributes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer func() { xdsclientNew = oldxdsclientNew }()
|
defer func() { xdsclientNew = oldxdsclientNew }()
|
||||||
|
|
||||||
cw := newXDSClientWrapper(nil, nil, balancer.BuildOptions{Target: resolver.Target{Endpoint: testServiceName}}, nil, nil)
|
cw := newXDSClientWrapper(nil, balancer.BuildOptions{Target: resolver.Target{Endpoint: testServiceName}}, nil, nil)
|
||||||
defer cw.close()
|
defer cw.close()
|
||||||
|
|
||||||
// Verify that the eds watch is registered for the expected resource name.
|
// Verify that the eds watch is registered for the expected resource name.
|
||||||
|
Reference in New Issue
Block a user