ronantakizawa/codereview-bench huggingface dataset:
Repository: grpc/grpc-go
File: interop/xds/client/client.go (Go)

Code before:
	s := grpc.NewServer()
	defer s.Stop()
	testgrpc.RegisterLoadBalancerStatsServiceServer(s, &statsService{})
	testgrpc.RegisterXdsUpdateClientConfigureServiceServer(s, &configureService{})
	reflection.Register(s)
	cleanup, err := admin.Register(s)
	if err != nil {
		logger.Fatalf("Failed to register admin: %v", err)
	}
	defer cleanup()
	go s.Serve(lis)

	creds := insecure.NewCredentials()
	if *secureMode {
		var err error
		creds, err = xds.NewClientCredentials(xds.ClientOptions{FallbackCreds: insecure.NewCredentials()})
		if err != nil {
			logger.Fatalf("Failed to create xDS credentials: %v", err)
		}
	}

	clients := make([]testgrpc.TestServiceClient, *numChannels)
	for i := 0; i < *numChannels; i++ {
		conn, err := grpc.NewClient(*server, grpc.WithTransportCredentials(creds))
		if err != nil {
			logger.Fatalf("NewClient() failed: %v", err)
		}
		defer conn.Close()
		clients[i] = testgrpc.NewTestServiceClient(conn)
	}
	ticker := time.NewTicker(time.Second / time.Duration(*qps**numChannels))
	defer ticker.Stop()
	sendRPCs(clients, ticker)
}

func makeOneRPC(c testgrpc.TestServiceClient, cfg *rpcConfig) (*peer.Peer, *rpcInfo, error) {
	timeout := *rpcTimeout
	if cfg.timeout != 0 {
		timeout = time.Duration(cfg.timeout) * time.Second
	}
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	if len(cfg.md) != 0 {
		ctx = metadata.NewOutgoingContext(ctx, cfg.md)
	}
	info := rpcInfo{typ: cfg.typ}

	var (
		p      peer.Peer
		header metadata.MD

Review comment (bug):
```suggestion
logger.Fatalf("Failed to create a client: %v", err)
```

Code after:
	s := grpc.NewServer()
	defer s.Stop()
	testgrpc.RegisterLoadBalancerStatsServiceServer(s, &statsService{})
	testgrpc.RegisterXdsUpdateClientConfigureServiceServer(s, &configureService{})
	reflection.Register(s)
	cleanup, err := admin.Register(s)
	if err != nil {
		logger.Fatalf("Failed to register admin: %v", err)
	}
	defer cleanup()
	go s.Serve(lis)

	creds := insecure.NewCredentials()
	if *secureMode {
		var err error
		creds, err = xds.NewClientCredentials(xds.ClientOptions{FallbackCreds: insecure.NewCredentials()})
		if err != nil {
			logger.Fatalf("Failed to create xDS credentials: %v", err)
		}
	}

	clients := make([]testgrpc.TestServiceClient, *numChannels)
	for i := 0; i < *numChannels; i++ {
		conn, err := grpc.NewClient(*server, grpc.WithTransportCredentials(creds))
		if err != nil {
			logger.Fatalf("grpc.NewClient(%q) = %v", *server, err)
		}
		defer conn.Close()
		clients[i] = testgrpc.NewTestServiceClient(conn)
	}
	ticker := time.NewTicker(time.Second / time.Duration(*qps**numChannels))
	defer ticker.Stop()
	sendRPCs(clients, ticker)
}

func makeOneRPC(c testgrpc.TestServiceClient, cfg *rpcConfig) (*peer.Peer, *rpcInfo, error) {
	timeout := *rpcTimeout
	if cfg.timeout != 0 {
		timeout = time.Duration(cfg.timeout) * time.Second
	}
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	if len(cfg.md) != 0 {
		ctx = metadata.NewOutgoingContext(ctx, cfg.md)
	}
	info := rpcInfo{typ: cfg.typ}

	var (
		p      peer.Peer
		header metadata.MD
Repository: grpc/grpc-go
File: profiling/cmd/remote.go (Go)

Code before:
	defer file.Close()

	logger.Infof("encoding data and writing to snapshot file %s", f)
	encoder := gob.NewEncoder(file)
	err = encoder.Encode(s)
	if err != nil {
		logger.Infof("error encoding: %v", err)
		return err
	}

	logger.Infof("successfully wrote profiling snapshot to %s", f)
	return nil
}

func remoteCommand() error {
	ctx := context.Background()
	if *flagTimeout > 0 {
		var cancel func()
		ctx, cancel = context.WithTimeout(context.Background(), time.Duration(*flagTimeout)*time.Second)
		defer cancel()
	}

	logger.Infof("dialing %s", *flagAddress)
	cc, err := grpc.NewClient(*flagAddress, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		logger.Errorf("NewClient() failed %s: %v", *flagAddress, err)
		return err
	}
	defer cc.Close()

	c := ppb.NewProfilingClient(cc)

	if *flagEnableProfiling || *flagDisableProfiling {
		return setEnabled(ctx, c, *flagEnableProfiling)
	}
	if *flagRetrieveSnapshot {
		return retrieveSnapshot(ctx, c, *flagSnapshot)
	}
	return fmt.Errorf("what should I do with the remote target?")
}


Review comment (bug):
```suggestion
logger.Fatalf("Failed to create a client for server %q: %v", *flagAddress, err)
```

Code after:
	defer file.Close()

	logger.Infof("encoding data and writing to snapshot file %s", f)
	encoder := gob.NewEncoder(file)
	err = encoder.Encode(s)
	if err != nil {
		logger.Infof("error encoding: %v", err)
		return err
	}

	logger.Infof("successfully wrote profiling snapshot to %s", f)
	return nil
}

func remoteCommand() error {
	ctx := context.Background()
	if *flagTimeout > 0 {
		var cancel func()
		ctx, cancel = context.WithTimeout(context.Background(), time.Duration(*flagTimeout)*time.Second)
		defer cancel()
	}

	logger.Infof("dialing %s", *flagAddress)
	cc, err := grpc.NewClient(*flagAddress, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		logger.Fatalf("grpc.NewClient(%q) = %v", *flagAddress, err)
		return err
	}
	defer cc.Close()

	c := ppb.NewProfilingClient(cc)

	if *flagEnableProfiling || *flagDisableProfiling {
		return setEnabled(ctx, c, *flagEnableProfiling)
	}
	if *flagRetrieveSnapshot {
		return retrieveSnapshot(ctx, c, *flagSnapshot)
	}
	return fmt.Errorf("what should I do with the remote target?")
}
Repository: grpc/grpc-go
File: resolver/manual/manual.go (Go)

Code before:
}

// Close is a noop for Resolver.
func (r *Resolver) Close() {
	r.CloseCallback()
}

// UpdateState calls CC.UpdateState.
func (r *Resolver) UpdateState(s resolver.State) {
	r.mu.Lock()
	defer r.mu.Unlock()
	var err error
	if r.CC == nil {
		panic("cannot update state as channel has not exited IDLE state")
	}
	err = r.CC.UpdateState(s)
	r.lastSeenState = &s
	r.UpdateStateCallback(err)
}

// ReportError calls CC.ReportError.
func (r *Resolver) ReportError(err error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if r.CC == nil {
		panic("cannot report error as grpc.NewClient with resolver has not been called")
	}
	r.CC.ReportError(err)
}


Review comment (suggestion):
This needs to be updated similar to line 113. NewClient doesn't trigger the creation of the resolver. The resolver is created when `Connect` is called or an RPC is made using the channel.

Code after:
}

// Close is a noop for Resolver.
func (r *Resolver) Close() {
	r.CloseCallback()
}

// UpdateState calls CC.UpdateState.
func (r *Resolver) UpdateState(s resolver.State) {
	r.mu.Lock()
	defer r.mu.Unlock()
	var err error
	if r.CC == nil {
		panic("cannot update state as channel has not exited IDLE state")
	}
	err = r.CC.UpdateState(s)
	r.lastSeenState = &s
	r.UpdateStateCallback(err)
}

// ReportError calls CC.ReportError.
func (r *Resolver) ReportError(err error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if r.CC == nil {
		panic("cannot report error as channel has not exited IDLE state")
	}
	r.CC.ReportError(err)
}
Repository: grpc/grpc-go
File: clientconn.go (Go)

Code before:
		if _, ok := opt.(*disableGlobalDialOptions); ok {
			disableGlobalOpts = true
			break
		}
	}

	if !disableGlobalOpts {
		for _, opt := range globalDialOptions {
			opt.apply(&cc.dopts)
		}
	}

	for _, opt := range opts {
		opt.apply(&cc.dopts)
	}

	// Determine the resolver to use.
	if err := cc.initParsedTargetAndResolverBuilder(); err != nil {
		return nil, err
	}

	// validation for the google-c2p scheme.
	if cc.parsedTarget.URL.Scheme == "google-c2p" && cc.parsedTarget.URL.Host != "" {
		return nil, fmt.Errorf("google-c2p URI scheme does not support authorities")
	}

	for _, opt := range globalPerTargetDialOptions {
		opt.DialOptionForTarget(cc.parsedTarget.URL).apply(&cc.dopts)
	}

	chainUnaryClientInterceptors(cc)
	chainStreamClientInterceptors(cc)

	if err := cc.validateTransportCredentials(); err != nil {
		return nil, err
	}

	if cc.dopts.defaultServiceConfigRawJSON != nil {
		scpr := parseServiceConfig(*cc.dopts.defaultServiceConfigRawJSON, cc.dopts.maxCallAttempts)
		if scpr.Err != nil {
			return nil, fmt.Errorf("%s: %v", invalidDefaultServiceConfigErrPrefix, scpr.Err)
		}
		cc.dopts.defaultServiceConfig, _ = scpr.Config.(*ServiceConfig)
	}
	cc.mkp = cc.dopts.copts.KeepaliveParams

	if err = cc.initAuthority(); err != nil {
		return nil, err
	}

	// Register ClientConn with channelz. Note that this is only done after

Review comment (suggestion):
This is not how you get the test to pass :b. The validation is done by the resolver and not the gRPC channel. The gRPC channel is generic and doesn't care about the `google-c2p` scheme.

Code after:
		if _, ok := opt.(*disableGlobalDialOptions); ok {
			disableGlobalOpts = true
			break
		}
	}

	if !disableGlobalOpts {
		for _, opt := range globalDialOptions {
			opt.apply(&cc.dopts)
		}
	}

	for _, opt := range opts {
		opt.apply(&cc.dopts)
	}

	// Determine the resolver to use.
	if err := cc.initParsedTargetAndResolverBuilder(); err != nil {
		return nil, err
	}

	for _, opt := range globalPerTargetDialOptions {
		opt.DialOptionForTarget(cc.parsedTarget.URL).apply(&cc.dopts)
	}

	chainUnaryClientInterceptors(cc)
	chainStreamClientInterceptors(cc)

	if err := cc.validateTransportCredentials(); err != nil {
		return nil, err
	}

	if cc.dopts.defaultServiceConfigRawJSON != nil {
		scpr := parseServiceConfig(*cc.dopts.defaultServiceConfigRawJSON, cc.dopts.maxCallAttempts)
		if scpr.Err != nil {
			return nil, fmt.Errorf("%s: %v", invalidDefaultServiceConfigErrPrefix, scpr.Err)
		}
		cc.dopts.defaultServiceConfig, _ = scpr.Config.(*ServiceConfig)
	}
	cc.mkp = cc.dopts.copts.KeepaliveParams

	if err = cc.initAuthority(); err != nil {
		return nil, err
	}

	// Register ClientConn with channelz. Note that this is only done after
	// channel creation cannot fail.
	cc.channelzRegistration(target)
	channelz.Infof(logger, cc.channelz, "parsed dial target is: %#v", cc.parsedTarget)
	channelz.Infof(logger, cc.channelz, "Channel authority set to %q", cc.authority)
Repository: grpc/grpc-go
File: xds/internal/xdsclient/tests/client_custom_dialopts_test.go (Go)

Code before:
package xdsclient_test

import (
	"context"
	"encoding/json"
	"fmt"
	"reflect"
	"testing"

	"github.com/google/uuid"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/internal"
	"google.golang.org/grpc/internal/stubserver"
	"google.golang.org/grpc/internal/testutils"
	"google.golang.org/grpc/internal/testutils/xds/e2e"
	internalbootstrap "google.golang.org/grpc/internal/xds/bootstrap"
	testgrpc "google.golang.org/grpc/interop/grpc_testing"
	testpb "google.golang.org/grpc/interop/grpc_testing"
	"google.golang.org/grpc/resolver"
	"google.golang.org/grpc/xds/bootstrap"
	xci "google.golang.org/grpc/xds/internal/xdsclient/internal"
)

// mockDialOption is a no-op grpc.DialOption with a name.
type mockDialOption struct {
	grpc.EmptyDialOption
	name string
}

const testCredsBuilderName = "test_dialer_creds"

var testDialOptNames = []string{"opt1", "opt2", "opt3"}

// testCredsBundle implements `credentials.Bundle`  and `extraDialOptions`.
type testCredsBundle struct {
	credentials.Bundle
	dialOpts []grpc.DialOption
}

func (t *testCredsBundle) DialOptions() []grpc.DialOption {
	return t.dialOpts
}

type testCredsBuilder struct{}

func (t *testCredsBuilder) Build(config json.RawMessage) (credentials.Bundle, func(), error) {
	return &testCredsBundle{insecure.NewBundle(),
		func() []grpc.DialOption {
			var opts []grpc.DialOption

Review comment (suggestion):
Since there is actually no mocking going on here, please rename this to something else. Maybe `nopDialOption` is fine.

Code after:
package xdsclient_test

import (
	"context"
	"encoding/json"
	"fmt"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/google/uuid"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/internal"
	"google.golang.org/grpc/internal/stubserver"
	"google.golang.org/grpc/internal/testutils"
	"google.golang.org/grpc/internal/testutils/xds/e2e"
	internalbootstrap "google.golang.org/grpc/internal/xds/bootstrap"
	testgrpc "google.golang.org/grpc/interop/grpc_testing"
	testpb "google.golang.org/grpc/interop/grpc_testing"
	"google.golang.org/grpc/resolver"
	"google.golang.org/grpc/xds/bootstrap"
	xci "google.golang.org/grpc/xds/internal/xdsclient/internal"
)

// nopDialOption is a no-op grpc.DialOption with a name.
type nopDialOption struct {
	grpc.EmptyDialOption
	name string
}

// testCredsBundle implements `credentials.Bundle` and `extraDialOptions`.
type testCredsBundle struct {
	credentials.Bundle
	testDialOptNames []string
}

func (t *testCredsBundle) DialOptions() []grpc.DialOption {
	var opts []grpc.DialOption
	for _, name := range t.testDialOptNames {
		opts = append(opts, &nopDialOption{name: name})
	}
	return opts
}

type testCredsBuilder struct {
	testDialOptNames []string
}

func (t *testCredsBuilder) Build(config json.RawMessage) (credentials.Bundle, func(), error) {
	return &testCredsBundle{
Repository: grpc/grpc-go
File: xds/internal/xdsclient/tests/client_custom_dialopts_test.go (Go)

Code before:
	"google.golang.org/grpc/resolver"
	"google.golang.org/grpc/xds/bootstrap"
	xci "google.golang.org/grpc/xds/internal/xdsclient/internal"
)

// mockDialOption is a no-op grpc.DialOption with a name.
type mockDialOption struct {
	grpc.EmptyDialOption
	name string
}

const testCredsBuilderName = "test_dialer_creds"

var testDialOptNames = []string{"opt1", "opt2", "opt3"}

// testCredsBundle implements `credentials.Bundle`  and `extraDialOptions`.
type testCredsBundle struct {
	credentials.Bundle
	dialOpts []grpc.DialOption
}

func (t *testCredsBundle) DialOptions() []grpc.DialOption {
	return t.dialOpts
}

type testCredsBuilder struct{}

func (t *testCredsBuilder) Build(config json.RawMessage) (credentials.Bundle, func(), error) {
	return &testCredsBundle{insecure.NewBundle(),
		func() []grpc.DialOption {
			var opts []grpc.DialOption
			for _, name := range testDialOptNames {
				opts = append(opts, &mockDialOption{name: name})
			}
			return opts
		}(),
	}, func() {}, nil
}

func (t *testCredsBuilder) Name() string {
	return testCredsBuilderName
}

func (s) TestClientCustomDialOptsFromCredentialsBundle(t *testing.T) {
	// Create and register the credentials bundle builder.
	credsBuilder := &testCredsBuilder{}
	bootstrap.RegisterCredentials(credsBuilder)

	// Start an xDS management server.
	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})


Review comment (suggestion):
Please consider making the `testDialOptNames` a field in this struct. That way, the test can control exactly the dial option names and we can get rid of the global.

Code after:
	"google.golang.org/grpc/resolver"
	"google.golang.org/grpc/xds/bootstrap"
	xci "google.golang.org/grpc/xds/internal/xdsclient/internal"
)

// nopDialOption is a no-op grpc.DialOption with a name.
type nopDialOption struct {
	grpc.EmptyDialOption
	name string
}

// testCredsBundle implements `credentials.Bundle` and `extraDialOptions`.
type testCredsBundle struct {
	credentials.Bundle
	testDialOptNames []string
}

func (t *testCredsBundle) DialOptions() []grpc.DialOption {
	var opts []grpc.DialOption
	for _, name := range t.testDialOptNames {
		opts = append(opts, &nopDialOption{name: name})
	}
	return opts
}

type testCredsBuilder struct {
	testDialOptNames []string
}

func (t *testCredsBuilder) Build(config json.RawMessage) (credentials.Bundle, func(), error) {
	return &testCredsBundle{
		Bundle:           insecure.NewBundle(),
		testDialOptNames: t.testDialOptNames,
	}, func() {}, nil
}

func (t *testCredsBuilder) Name() string {
	return "test_dialer_creds"
}

func (s) TestClientCustomDialOptsFromCredentialsBundle(t *testing.T) {
	// Create and register the credentials bundle builder.
	credsBuilder := &testCredsBuilder{
		testDialOptNames: []string{"opt1", "opt2", "opt3"},
	}
	bootstrap.RegisterCredentials(credsBuilder)

	// Start an xDS management server.
	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})

	// Create bootstrap configuration pointing to the above management server.
Repository: grpc/grpc-go
File: credentials/alts/alts_test.go (Go)

Code before:
	listener, err := testutils.LocalTCPListener()
	if err != nil {
		t.Fatalf("LocalTCPListener() failed: %v", err)
	}
	s := grpc.NewServer()
	altsgrpc.RegisterHandshakerServiceServer(s, &testutil.FakeHandshaker{})
	wait.Add(1)
	go func() {
		defer wait.Done()
		if err := s.Serve(listener); err != nil {
			t.Errorf("failed to serve: %v", err)
		}
	}()
	return func() { s.Stop() }, listener.Addr().String()
}

func startServer(t *testing.T, handshakerServiceAddress string, _ *sync.WaitGroup) (stop func(), address string) {
	listener, err := testutils.LocalTCPListener()
	if err != nil {
		t.Fatalf("LocalTCPListener() failed: %v", err)
	}
	serverOpts := &ServerOptions{HandshakerServiceAddress: handshakerServiceAddress}
	creds := NewServerCreds(serverOpts)
	stub := &stubserver.StubServer{
		Listener: listener,
		UnaryCallF: func(_ context.Context, _ *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
			return &testpb.SimpleResponse{
				Payload: &testpb.Payload{},
			}, nil
		},
		S: grpc.NewServer(grpc.Creds(creds)),
	}
	stubserver.StartTestService(t, stub)
	return func() { stub.S.Stop() }, listener.Addr().String()
}


Review comment (suggestion):
Since both parameters are unused, you can even omit the blank identifiers (`_`). So, this can be:
```golang
UnaryCallF: func(context.Context, *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { ... }
```

Code after:
	listener, err := testutils.LocalTCPListener()
	if err != nil {
		t.Fatalf("LocalTCPListener() failed: %v", err)
	}
	s := grpc.NewServer()
	altsgrpc.RegisterHandshakerServiceServer(s, &testutil.FakeHandshaker{})
	wait.Add(1)
	go func() {
		defer wait.Done()
		if err := s.Serve(listener); err != nil {
			t.Errorf("failed to serve: %v", err)
		}
	}()
	return func() { s.Stop() }, listener.Addr().String()
}

func startServer(t *testing.T, handshakerServiceAddress string) (stop func(), address string) {
	listener, err := testutils.LocalTCPListener()
	if err != nil {
		t.Fatalf("LocalTCPListener() failed: %v", err)
	}
	serverOpts := &ServerOptions{HandshakerServiceAddress: handshakerServiceAddress}
	creds := NewServerCreds(serverOpts)
	stub := &stubserver.StubServer{
		Listener: listener,
		UnaryCallF: func(context.Context, *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
			return &testpb.SimpleResponse{
				Payload: &testpb.Payload{},
			}, nil
		},
		S: grpc.NewServer(grpc.Creds(creds)),
	}
	stubserver.StartTestService(t, stub)
	return func() { stub.S.Stop() }, listener.Addr().String()
}
Repository: grpc/grpc-go
File: balancer/endpointsharding/endpointsharding.go (Go)

Code before:
	bw.es.mu.Unlock()
	if state.ConnectivityState == connectivity.Idle && bw.es.enableAutoReconnect {
		bw.ExitIdle()
	}
	bw.es.updateState()
}

func (bw *balancerWrapper) Close() {
	bw.Balancer.Close()
	bw.isClosed = true
}

// ExitIdle pings an IDLE child balancer to exit idle in a new goroutine to
// avoid deadlocks due to synchronous balancer state updates.
func (bw *balancerWrapper) ExitIdle() {
	if ei, ok := bw.Balancer.(balancer.ExitIdler); ok {
		go func() {
			bw.es.childMu.Lock()
			if !bw.isClosed {
				ei.ExitIdle()
			}
			bw.es.childMu.Unlock()
		}()
	}
}

// ParseConfig parses a child config list and returns an LB config to use with
// the endpointsharding balancer.
//
// cfg is expected to be a JSON array of LB policy names + configs as the
// format of the loadBalancingConfig field in ServiceConfig.
func ParseConfig(cfg json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
	return gracefulswitch.ParseConfig(cfg)
}


Review comment (bug):
Because we're calling into the child from a goroutine here:

- _All calls_ into the child need to grab `bw.es.childMu` to avoid concurrent calls in,
- That means `Close()` needs it (and also needed it before you added the method explicitly here, so good that we caught it), and
- That means we shouldn't embed the child `Balancer`, since directly calling it is wrong.


Code after:
	balancer.ClientConn // embed to intercept UpdateState, doesn't deal with SubConns

	es *endpointSharding

	// Access to the following fields is guarded by es.mu.

	childState ChildState
	isClosed   bool
}

func (bw *balancerWrapper) UpdateState(state balancer.State) {
	bw.es.mu.Lock()
	bw.childState.State = state
	bw.es.mu.Unlock()
	if state.ConnectivityState == connectivity.Idle && bw.es.enableAutoReconnect {
		bw.ExitIdle()
	}
	bw.es.updateState()
}

// ExitIdle pings an IDLE child balancer to exit idle in a new goroutine to
// avoid deadlocks due to synchronous balancer state updates.
func (bw *balancerWrapper) ExitIdle() {
	if ei, ok := bw.child.(balancer.ExitIdler); ok {
		go func() {
			bw.es.childMu.Lock()
			if !bw.isClosed {
				ei.ExitIdle()
			}
			bw.es.childMu.Unlock()
		}()
	}
}

// updateClientConnStateLocked delivers the ClientConnState to the child
// balancer. Callers must hold the child mutex of the parent endpointsharding
// balancer.
func (bw *balancerWrapper) updateClientConnStateLocked(ccs balancer.ClientConnState) error {
	return bw.child.UpdateClientConnState(ccs)
}

// closeLocked closes the child balancer. Callers must hold the child mutext of
// the parent endpointsharding balancer.
func (bw *balancerWrapper) closeLocked() {
	if bw.isClosed {
		return
	}
	bw.child.Close()
	bw.isClosed = true
}