2424package randomsubsetting
2525
2626import (
27+ "cmp"
2728 "encoding/json"
28- "errors"
2929 "fmt"
30- "sort "
30+ "slices "
3131 "time"
3232
3333 "github.com/cespare/xxhash/v2"
@@ -72,28 +72,38 @@ func (bb) Build(cc balancer.ClientConn, bOpts balancer.BuildOptions) balancer.Ba
7272 return b
7373}
7474
75- // LBConfig is the config for the outlier detection balancer.
75+ // LBConfig is the config for the random subsetting balancer.
7676type LBConfig struct {
7777 serviceconfig.LoadBalancingConfig `json:"-"`
7878
79- SubsetSize uint64 `json:"subset_size,omitempty"`
80-
79+ SubsetSize uint64 `json:"subset_size,omitempty"`
8180 ChildPolicy * iserviceconfig.BalancerConfig `json:"child_policy,omitempty"`
8281}
8382
8483func (bb ) ParseConfig (s json.RawMessage ) (serviceconfig.LoadBalancingConfig , error ) {
8584 lbCfg := & LBConfig {
86- // Default top layer values.
87- SubsetSize : 10 ,
85+ SubsetSize : 2 , // default value
86+ ChildPolicy : & iserviceconfig. BalancerConfig { Name : "round_robin" } ,
8887 }
8988
9089 if err := json .Unmarshal (s , lbCfg ); err != nil { // Validates child config if present as well.
91- return nil , fmt .Errorf ("subsetting : unable to unmarshal LBconfig : %s, error: %v" , string (s ), err )
90+ return nil , fmt .Errorf ("randomsubsetting : unable to unmarshal LBConfig : %s, error: %v" , string (s ), err )
9291 }
9392
94- // if someonw needs subsetSize == 1, he should use pick_first instead
93+ // if someone needs SubsetSize == 1, he should use pick_first instead
9594 if lbCfg .SubsetSize < 2 {
96- return nil , errors .New ("subsetting: subsetSize must be >= 2" )
95+ return nil , fmt .Errorf ("randomsubsetting: SubsetSize must be >= 2" )
96+ }
97+
98+ if lbCfg .ChildPolicy == nil {
99+ return nil , fmt .Errorf ("randomsubsetting: child policy field must be set" )
100+ }
101+
102+ // Reject whole config if child policy doesn't exist, don't persist it for
103+ // later.
104+ bb := balancer .Get (lbCfg .ChildPolicy .Name )
105+ if bb == nil {
106+ return nil , fmt .Errorf ("randomsubsetting: child balancer %q not registered" , lbCfg .ChildPolicy .Name )
97107 }
98108
99109 return lbCfg , nil
@@ -114,73 +124,68 @@ type subsettingBalancer struct {
114124func (b * subsettingBalancer ) UpdateClientConnState (s balancer.ClientConnState ) error {
115125 lbCfg , ok := s .BalancerConfig .(* LBConfig )
116126 if ! ok {
117- b .logger .Errorf ("received config with unexpected type %T: %v" , s .BalancerConfig , s .BalancerConfig )
127+ b .logger .Errorf ("randomsubsetting: received config with unexpected type %T: %v" , s .BalancerConfig , s .BalancerConfig )
118128 return balancer .ErrBadResolverState
119129 }
120130
121- // Reject whole config if child policy doesn't exist, don't persist it for
122- // later.
123- bb := balancer .Get (lbCfg .ChildPolicy .Name )
124- if bb == nil {
125- return fmt .Errorf ("subsetting: child balancer %q not registered" , lbCfg .ChildPolicy .Name )
126- }
127-
128131 if b .cfg == nil || b .cfg .ChildPolicy .Name != lbCfg .ChildPolicy .Name {
129- err := b . child . SwitchTo ( bb )
130- if err != nil {
131- return fmt .Errorf ("subsetting : error switching to child of type %q: %v" , lbCfg .ChildPolicy .Name , err )
132+
133+ if err := b . child . SwitchTo ( balancer . Get ( lbCfg . ChildPolicy . Name )); err != nil {
134+ return fmt .Errorf ("randomsubsetting : error switching to child of type %q: %v" , lbCfg .ChildPolicy .Name , err )
132135 }
133136 }
134137 b .cfg = lbCfg
135138
136- err := b .child .UpdateClientConnState (balancer.ClientConnState {
137- ResolverState : b .prepareChildResolverState (s . ResolverState ),
139+ return b .child .UpdateClientConnState (balancer.ClientConnState {
140+ ResolverState : b .prepareChildResolverState (s ),
138141 BalancerConfig : b .cfg .ChildPolicy .Config ,
139142 })
140-
141- return err
142143}
143144
144- type AddressWithHash struct {
145+ type endpointWithHash struct {
145146 hash uint64
146- addr resolver.Address
147+ ep resolver.Endpoint
147148}
148149
149- // implements the subsetting algorithm, as described in A68: https://github.com/grpc/proposal/pull/423
150- func (b * subsettingBalancer ) prepareChildResolverState (s resolver.State ) resolver.State {
151- addresses := s .Addresses
152- backendCount := len (addresses )
153- if backendCount <= int (b .cfg .SubsetSize ) {
154- return s
150+ // implements the subsetting algorithm,
151+ // as described in A68: https://github.com/grpc/proposal/blob/master/A68-random-subsetting.md
152+ func (b * subsettingBalancer ) prepareChildResolverState (s balancer.ClientConnState ) resolver.State {
153+ subsetSize := b .cfg .SubsetSize
154+ endPoints := s .ResolverState .Endpoints
155+ backendCount := len (endPoints )
156+ if backendCount <= int (subsetSize ) || subsetSize < 2 {
157+ return s .ResolverState
155158 }
156159
157- addressesSet := make ([]AddressWithHash , backendCount )
158160 // calculate hash for each endpoint
159- for i , endpoint := range addresses {
160-
161- b .hashf .Write ([]byte (s .Addresses [0 ].String ()))
162- addressesSet [i ] = AddressWithHash {
161+ endpointSet := make ([] endpointWithHash , backendCount )
162+ for i , endpoint := range endPoints {
163+ b .hashf .Write ([]byte (endpoint .Addresses [0 ].String ()))
164+ endpointSet [i ] = endpointWithHash {
163165 hash : b .hashf .Sum64 (),
164- addr : endpoint ,
166+ ep : endpoint ,
165167 }
166168 }
167- // sort addresses by hash
168- sort .Slice (addressesSet , func (i , j int ) bool {
169- return addressesSet [i ].hash < addressesSet [j ].hash
169+
170+ // sort endpoint by hash
171+ slices .SortFunc (endpointSet , func (a , b endpointWithHash ) int {
172+ return cmp .Compare (a .hash , b .hash )
170173 })
171174
172- b .logger .Infof ("resulting subset: %v" , addressesSet [:b .cfg .SubsetSize ])
175+ if b .logger .V (2 ) {
176+ b .logger .Infof ("randomsubsetting: resulting subset: %v" , endpointSet [:subsetSize ])
177+ }
173178
174- // Convert back to resolver.addresses
175- addressesSubset := make ([]resolver.Address , b . cfg . SubsetSize )
176- for _ , eh := range addressesSet [: b . cfg . SubsetSize ] {
177- addressesSubset = append ( addressesSubset , eh . addr )
179+ // Convert back to resolver.Endpoints
180+ endpointSubset := make ([]resolver.Endpoint , subsetSize )
181+ for i , endpoint := range endpointSet [: subsetSize ] {
182+ endpointSubset [ i ] = endpoint . ep
178183 }
179184
180185 return resolver.State {
181- Addresses : addressesSubset ,
182- ServiceConfig : s .ServiceConfig ,
183- Attributes : s .Attributes ,
186+ Endpoints : endpointSubset ,
187+ ServiceConfig : s .ResolverState . ServiceConfig ,
188+ Attributes : s .ResolverState . Attributes ,
184189 }
185190}
186191
0 commit comments