Skip to content

Commit 1418e5e

Browse files
authored
clusterimpl: use gsb.UpdateClientConnState instead of switchTo, on receipt of config update (#7567)
1 parent ac41314 commit 1418e5e

File tree

2 files changed

+114
-5
lines changed

2 files changed

+114
-5
lines changed

xds/internal/balancer/clusterimpl/balancer_test.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package clusterimpl
2020

2121
import (
2222
"context"
23+
"encoding/json"
2324
"errors"
2425
"fmt"
2526
"strings"
@@ -40,6 +41,7 @@ import (
4041
"google.golang.org/grpc/internal/xds"
4142
"google.golang.org/grpc/internal/xds/bootstrap"
4243
"google.golang.org/grpc/resolver"
44+
"google.golang.org/grpc/serviceconfig"
4345
xdsinternal "google.golang.org/grpc/xds/internal"
4446
"google.golang.org/grpc/xds/internal/testutils/fakeclient"
4547
"google.golang.org/grpc/xds/internal/xdsclient"
@@ -839,6 +841,108 @@ func (s) TestUpdateLRSServer(t *testing.T) {
839841
}
840842
}
841843

844+
// Test verifies that child policies was updated on receipt of
845+
// configuration update.
846+
func (s) TestChildPolicyUpdatedOnConfigUpdate(t *testing.T) {
847+
xdsC := fakeclient.NewClient()
848+
849+
builder := balancer.Get(Name)
850+
cc := testutils.NewBalancerClientConn(t)
851+
b := builder.Build(cc, balancer.BuildOptions{})
852+
defer b.Close()
853+
854+
// Keep track of which child policy was updated
855+
updatedChildPolicy := ""
856+
857+
// Create stub balancers to track config updates
858+
const (
859+
childPolicyName1 = "stubBalancer1"
860+
childPolicyName2 = "stubBalancer2"
861+
)
862+
863+
stub.Register(childPolicyName1, stub.BalancerFuncs{
864+
UpdateClientConnState: func(_ *stub.BalancerData, _ balancer.ClientConnState) error {
865+
updatedChildPolicy = childPolicyName1
866+
return nil
867+
},
868+
})
869+
870+
stub.Register(childPolicyName2, stub.BalancerFuncs{
871+
UpdateClientConnState: func(_ *stub.BalancerData, _ balancer.ClientConnState) error {
872+
updatedChildPolicy = childPolicyName2
873+
return nil
874+
},
875+
})
876+
877+
// Initial config update with childPolicyName1
878+
if err := b.UpdateClientConnState(balancer.ClientConnState{
879+
ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
880+
BalancerConfig: &LBConfig{
881+
Cluster: testClusterName,
882+
ChildPolicy: &internalserviceconfig.BalancerConfig{
883+
Name: childPolicyName1,
884+
},
885+
},
886+
}); err != nil {
887+
t.Fatalf("Error updating the config: %v", err)
888+
}
889+
890+
if updatedChildPolicy != childPolicyName1 {
891+
t.Fatal("Child policy 1 was not updated on initial configuration update.")
892+
}
893+
894+
// Second config update with childPolicyName2
895+
if err := b.UpdateClientConnState(balancer.ClientConnState{
896+
ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
897+
BalancerConfig: &LBConfig{
898+
Cluster: testClusterName,
899+
ChildPolicy: &internalserviceconfig.BalancerConfig{
900+
Name: childPolicyName2,
901+
},
902+
},
903+
}); err != nil {
904+
t.Fatalf("Error updating the config: %v", err)
905+
}
906+
907+
if updatedChildPolicy != childPolicyName2 {
908+
t.Fatal("Child policy 2 was not updated after child policy name change.")
909+
}
910+
}
911+
912+
// Test verifies that config update fails if child policy config
913+
// failed to parse.
914+
func (s) TestFailedToParseChildPolicyConfig(t *testing.T) {
915+
xdsC := fakeclient.NewClient()
916+
917+
builder := balancer.Get(Name)
918+
cc := testutils.NewBalancerClientConn(t)
919+
b := builder.Build(cc, balancer.BuildOptions{})
920+
defer b.Close()
921+
922+
// Create a stub balancer which fails to ParseConfig.
923+
const parseConfigError = "failed to parse config"
924+
const childPolicyName = "stubBalancer-FailedToParseChildPolicyConfig"
925+
stub.Register(childPolicyName, stub.BalancerFuncs{
926+
ParseConfig: func(_ json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
927+
return nil, errors.New(parseConfigError)
928+
},
929+
})
930+
931+
err := b.UpdateClientConnState(balancer.ClientConnState{
932+
ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
933+
BalancerConfig: &LBConfig{
934+
Cluster: testClusterName,
935+
ChildPolicy: &internalserviceconfig.BalancerConfig{
936+
Name: childPolicyName,
937+
},
938+
},
939+
})
940+
941+
if err == nil || !strings.Contains(err.Error(), parseConfigError) {
942+
t.Fatalf("Got error: %v, want error: %s", err, parseConfigError)
943+
}
944+
}
945+
842946
func assertString(f func() (string, error)) string {
843947
s, err := f()
844948
if err != nil {

xds/internal/balancer/clusterimpl/clusterimpl.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,11 +231,16 @@ func (b *clusterImplBalancer) updateClientConnState(s balancer.ClientConnState)
231231
return err
232232
}
233233

234-
if b.config == nil || b.config.ChildPolicy.Name != newConfig.ChildPolicy.Name {
235-
if err := b.child.SwitchTo(bb); err != nil {
236-
return fmt.Errorf("error switching to child of type %q: %v", newConfig.ChildPolicy.Name, err)
237-
}
234+
// Build config for the gracefulswitch balancer. It is safe to ignore JSON
235+
// marshaling errors here, since the config was already validated as part of
236+
// ParseConfig().
237+
cfg := []map[string]any{{newConfig.ChildPolicy.Name: newConfig.ChildPolicy.Config}}
238+
cfgJSON, _ := json.Marshal(cfg)
239+
parsedCfg, err := gracefulswitch.ParseConfig(cfgJSON)
240+
if err != nil {
241+
return err
238242
}
243+
239244
b.config = newConfig
240245

241246
b.telemetryLabels = newConfig.TelemetryLabels
@@ -250,7 +255,7 @@ func (b *clusterImplBalancer) updateClientConnState(s balancer.ClientConnState)
250255
// Addresses and sub-balancer config are sent to sub-balancer.
251256
return b.child.UpdateClientConnState(balancer.ClientConnState{
252257
ResolverState: s.ResolverState,
253-
BalancerConfig: b.config.ChildPolicy.Config,
258+
BalancerConfig: parsedCfg,
254259
})
255260
}
256261

0 commit comments

Comments
 (0)