Skip to content
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

clusterimpl: use gsb.UpdateClientConnState instead of switchTo, on receipt of config update #7567

Merged
merged 5 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions xds/internal/balancer/clusterimpl/balancer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package clusterimpl

import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
Expand All @@ -40,6 +41,7 @@ import (
"google.golang.org/grpc/internal/xds"
"google.golang.org/grpc/internal/xds/bootstrap"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/serviceconfig"
xdsinternal "google.golang.org/grpc/xds/internal"
"google.golang.org/grpc/xds/internal/testutils/fakeclient"
"google.golang.org/grpc/xds/internal/xdsclient"
Expand Down Expand Up @@ -839,6 +841,108 @@ func (s) TestUpdateLRSServer(t *testing.T) {
}
}

// Test verifies that child policies was updated on receipt of
easwars marked this conversation as resolved.
Show resolved Hide resolved
// configuration update.
func (s) TestChildPolicyUpdatedOnConfigUpdate(t *testing.T) {
xdsC := fakeclient.NewClient()

builder := balancer.Get(Name)
cc := testutils.NewBalancerClientConn(t)
b := builder.Build(cc, balancer.BuildOptions{})
defer b.Close()

// Keep track of which child policy was updated
easwars marked this conversation as resolved.
Show resolved Hide resolved
updatedChildPolicy := ""

// Create stub balancers to track config updates
const (
childPolicyName1 = "stubBalancer1"
childPolicyName2 = "stubBalancer2"
)

stub.Register(childPolicyName1, stub.BalancerFuncs{
UpdateClientConnState: func(_ *stub.BalancerData, _ balancer.ClientConnState) error {
updatedChildPolicy = childPolicyName1
return nil
},
})

stub.Register(childPolicyName2, stub.BalancerFuncs{
UpdateClientConnState: func(_ *stub.BalancerData, _ balancer.ClientConnState) error {
updatedChildPolicy = childPolicyName2
return nil
},
})

// Initial config update with childPolicyName1
if err := b.UpdateClientConnState(balancer.ClientConnState{
ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
BalancerConfig: &LBConfig{
Cluster: testClusterName,
ChildPolicy: &internalserviceconfig.BalancerConfig{
Name: childPolicyName1,
},
},
}); err != nil {
t.Fatalf("Error updating the config: %v", err)
}

if updatedChildPolicy != childPolicyName1 {
t.Fatal("Child policy 1 was not updated on initial configuration update.")
}

// Second config update with childPolicyName2
if err := b.UpdateClientConnState(balancer.ClientConnState{
easwars marked this conversation as resolved.
Show resolved Hide resolved
ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
BalancerConfig: &LBConfig{
Cluster: testClusterName,
ChildPolicy: &internalserviceconfig.BalancerConfig{
Name: childPolicyName2,
},
},
}); err != nil {
t.Fatalf("Error updating the config: %v", err)
}

if updatedChildPolicy != childPolicyName2 {
t.Fatal("Child policy 2 was not updated after child policy name change.")
}
}

// Test verifies that config update fails if child policy config
// failed to parse.
func (s) TestFailedToParseChildPolicyConfig(t *testing.T) {
easwars marked this conversation as resolved.
Show resolved Hide resolved
xdsC := fakeclient.NewClient()

builder := balancer.Get(Name)
cc := testutils.NewBalancerClientConn(t)
b := builder.Build(cc, balancer.BuildOptions{})
defer b.Close()

// Create a stub balancer which fails to ParseConfig.
const parseConfigError = "failed to parse config"
const childPolicyName = "stubBalancer-FailedToParseChildPolicyConfig"
stub.Register(childPolicyName, stub.BalancerFuncs{
ParseConfig: func(_ json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
return nil, errors.New(parseConfigError)
},
})

err := b.UpdateClientConnState(balancer.ClientConnState{
ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
BalancerConfig: &LBConfig{
Cluster: testClusterName,
ChildPolicy: &internalserviceconfig.BalancerConfig{
Name: childPolicyName,
},
},
})

if err == nil || !strings.Contains(err.Error(), parseConfigError) {
t.Fatalf("Got error: %v, want error: %s", err, parseConfigError)
}
}

func assertString(f func() (string, error)) string {
s, err := f()
if err != nil {
Expand Down
16 changes: 10 additions & 6 deletions xds/internal/balancer/clusterimpl/clusterimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,6 @@ func (b *clusterImplBalancer) updateClientConnState(s balancer.ClientConnState)
return err
}

if b.config == nil || b.config.ChildPolicy.Name != newConfig.ChildPolicy.Name {
if err := b.child.SwitchTo(bb); err != nil {
return fmt.Errorf("error switching to child of type %q: %v", newConfig.ChildPolicy.Name, err)
}
}
b.config = newConfig

b.telemetryLabels = newConfig.TelemetryLabels
Expand All @@ -247,10 +242,19 @@ func (b *clusterImplBalancer) updateClientConnState(s balancer.ClientConnState)
})
}

// Build config for the gracefulswitch balancer. It is safe to ignore JSON
// marshaling errors here, since the config was already validated as part of
// ParseConfig().
cfg := []map[string]any{{newConfig.ChildPolicy.Name: newConfig.ChildPolicy.Config}}
cfgJSON, _ := json.Marshal(cfg)
parsedCfg, err := gracefulswitch.ParseConfig(cfgJSON)
if err != nil {
return err
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... actually what is the reason for moving this block to be done after handling telemetry labels and drop/request counts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, imo it won't matter when are we building the chilld config for gracefulswitch balancer. Does it?
Anyways, I have reverted that and updated the PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, it is a good idea to not make a change without a need for it.

// Addresses and sub-balancer config are sent to sub-balancer.
return b.child.UpdateClientConnState(balancer.ClientConnState{
ResolverState: s.ResolverState,
BalancerConfig: b.config.ChildPolicy.Config,
BalancerConfig: parsedCfg,
})
}

Expand Down