func (bg *BalancerGroup) ResolverError(err error) {
	bg.outgoingMu.Lock()
	for _, config := range bg.idToBalancerConfig {
		config.resolverError(err)
	}
	bg.outgoingMu.Unlock()
}
ResolverError forwards resolver errors to all sub-balancers.
package balancergroup
import (
	"fmt"
	"sync"
	"time"
	orcapb "github.com/cncf/xds/go/xds/data/orca/v3"
	"google.golang.org/grpc/xds/internal/xdsclient/load"
	"google.golang.org/grpc/balancer"
	"google.golang.org/grpc/connectivity"
	"google.golang.org/grpc/internal/cache"
	"google.golang.org/grpc/internal/grpclog"
	"google.golang.org/grpc/resolver"
)
type subBalancerWrapper struct {
	balancer.ClientConn
	id    string
	group *BalancerGroup
	mu    sync.Mutex
	state balancer.State
	builder balancer.Builder
	buildOpts balancer.BuildOptions
	ccState *balancer.ClientConnState
	balancer balancer.Balancer
}
func (sbc *subBalancerWrapper) UpdateState(state balancer.State) {
	sbc.mu.Lock()
	sbc.state = state
	sbc.group.updateBalancerState(sbc.id, state)
	sbc.mu.Unlock()
}
func (sbc *subBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
	return sbc.group.newSubConn(sbc, addrs, opts)
}
func (sbc *subBalancerWrapper) updateBalancerStateWithCachedPicker() {
	sbc.mu.Lock()
	if sbc.state.Picker != nil {
		sbc.group.updateBalancerState(sbc.id, sbc.state)
	}
	sbc.mu.Unlock()
}
func (sbc *subBalancerWrapper) startBalancer() {
	b := sbc.builder.Build(sbc, sbc.buildOpts)
	sbc.group.logger.Infof("Created child policy %p of type %v", b, sbc.builder.Name())
	sbc.balancer = b
	if sbc.ccState != nil {
		b.UpdateClientConnState(*sbc.ccState)
	}
}
func (sbc *subBalancerWrapper) exitIdle() {
	b := sbc.balancer
	if b == nil {
		return
	}
	if ei, ok := b.(balancer.ExitIdler); ok {
		ei.ExitIdle()
		return
	}
	for sc, b := range sbc.group.scToSubBalancer {
		if b == sbc {
			sc.Connect()
		}
	}
}
func (sbc *subBalancerWrapper) updateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
	b := sbc.balancer
	if b == nil {
		return
	}
	b.UpdateSubConnState(sc, state)
}
func (sbc *subBalancerWrapper) updateClientConnState(s balancer.ClientConnState) error {
	sbc.ccState = &s
	b := sbc.balancer
	if b == nil {
		return nil
	}
	return b.UpdateClientConnState(s)
}
func (sbc *subBalancerWrapper) resolverError(err error) {
	b := sbc.balancer
	if b == nil {
		return
	}
	b.ResolverError(err)
}
func (sbc *subBalancerWrapper) stopBalancer() {
	sbc.balancer.Close()
	sbc.balancer = nil
}
type BalancerGroup struct {
	cc        balancer.ClientConn
	buildOpts balancer.BuildOptions
	logger    *grpclog.PrefixLogger
	loadStore load.PerClusterReporter  
	stateAggregator BalancerStateAggregator
	outgoingMu         sync.Mutex
	outgoingStarted    bool
	idToBalancerConfig map[string]*subBalancerWrapper
	balancerCache *cache.TimeoutCache
	incomingMu      sync.Mutex
	incomingStarted bool  
	scToSubBalancer map[balancer.SubConn]*subBalancerWrapper
}
var DefaultSubBalancerCloseTimeout = 15 * time.Minute
func New(cc balancer.ClientConn, bOpts balancer.BuildOptions, stateAggregator BalancerStateAggregator, loadStore load.PerClusterReporter, logger *grpclog.PrefixLogger) *BalancerGroup {
	return &BalancerGroup{
		cc:        cc,
		buildOpts: bOpts,
		logger:    logger,
		loadStore: loadStore,
		stateAggregator: stateAggregator,
		idToBalancerConfig: make(map[string]*subBalancerWrapper),
		balancerCache:      cache.NewTimeoutCache(DefaultSubBalancerCloseTimeout),
		scToSubBalancer:    make(map[balancer.SubConn]*subBalancerWrapper),
	}
}
func (bg *BalancerGroup) Start() {
	bg.incomingMu.Lock()
	bg.incomingStarted = true
	bg.incomingMu.Unlock()
	bg.outgoingMu.Lock()
	if bg.outgoingStarted {
		bg.outgoingMu.Unlock()
		return
	}
	for _, config := range bg.idToBalancerConfig {
		config.startBalancer()
	}
	bg.outgoingStarted = true
	bg.outgoingMu.Unlock()
}
func (bg *BalancerGroup) Add(id string, builder balancer.Builder) {
	bg.outgoingMu.Lock()
	var sbc *subBalancerWrapper
	if bg.outgoingStarted {
		if old, ok := bg.balancerCache.Remove(id); ok {
			sbc, _ = old.(*subBalancerWrapper)
			if sbc != nil && sbc.builder != builder {
				sbc.stopBalancer()
				bg.cleanupSubConns(sbc)
				sbc = nil
			}
		}
	}
	if sbc == nil {
		sbc = &subBalancerWrapper{
			ClientConn: bg.cc,
			id:         id,
			group:      bg,
			builder:    builder,
			buildOpts:  bg.buildOpts,
		}
		if bg.outgoingStarted {
			sbc.startBalancer()
		}
	} else {
		sbc.updateBalancerStateWithCachedPicker()
	}
	bg.idToBalancerConfig[id] = sbc
	bg.outgoingMu.Unlock()
}
func (bg *BalancerGroup) Remove(id string) {
	bg.outgoingMu.Lock()
	if sbToRemove, ok := bg.idToBalancerConfig[id]; ok {
		if bg.outgoingStarted {
			bg.balancerCache.Add(id, sbToRemove, func() {
				bg.outgoingMu.Lock()
				if bg.outgoingStarted {
					sbToRemove.stopBalancer()
				}
				bg.outgoingMu.Unlock()
				bg.cleanupSubConns(sbToRemove)
			})
		}
		delete(bg.idToBalancerConfig, id)
	} else {
		bg.logger.Infof("balancer group: trying to remove a non-existing locality from balancer group: %v", id)
	}
	bg.outgoingMu.Unlock()
}
func (bg *BalancerGroup) cleanupSubConns(config *subBalancerWrapper) {
	bg.incomingMu.Lock()
	for sc, b := range bg.scToSubBalancer {
		if b == config {
			bg.cc.RemoveSubConn(sc)
			delete(bg.scToSubBalancer, sc)
		}
	}
	bg.incomingMu.Unlock()
}
func (bg *BalancerGroup) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
	bg.incomingMu.Lock()
	config, ok := bg.scToSubBalancer[sc]
	if !ok {
		bg.incomingMu.Unlock()
		return
	}
	if state.ConnectivityState == connectivity.Shutdown {
		delete(bg.scToSubBalancer, sc)
	}
	bg.incomingMu.Unlock()
	bg.outgoingMu.Lock()
	config.updateSubConnState(sc, state)
	bg.outgoingMu.Unlock()
}
func (bg *BalancerGroup) UpdateClientConnState(id string, s balancer.ClientConnState) error {
	bg.outgoingMu.Lock()
	defer bg.outgoingMu.Unlock()
	if config, ok := bg.idToBalancerConfig[id]; ok {
		return config.updateClientConnState(s)
	}
	return nil
}