blob: abe266b021d2e536c32a982b6facab6938f44d2e [file] [log] [blame]
khenaidoo5fc5cea2021-08-11 17:39:16 -04001/*
2 *
3 * Copyright 2017 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19package grpc
20
21import (
Akash Kankanala761955c2024-02-21 19:32:20 +053022 "encoding/json"
khenaidoo5fc5cea2021-08-11 17:39:16 -040023 "errors"
24 "fmt"
25
26 "google.golang.org/grpc/balancer"
27 "google.golang.org/grpc/connectivity"
Akash Kankanala761955c2024-02-21 19:32:20 +053028 "google.golang.org/grpc/internal/envconfig"
29 "google.golang.org/grpc/internal/grpcrand"
30 "google.golang.org/grpc/serviceconfig"
khenaidoo5fc5cea2021-08-11 17:39:16 -040031)
32
33// PickFirstBalancerName is the name of the pick_first balancer.
34const PickFirstBalancerName = "pick_first"
35
36func newPickfirstBuilder() balancer.Builder {
37 return &pickfirstBuilder{}
38}
39
40type pickfirstBuilder struct{}
41
42func (*pickfirstBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer {
43 return &pickfirstBalancer{cc: cc}
44}
45
46func (*pickfirstBuilder) Name() string {
47 return PickFirstBalancerName
48}
49
Akash Kankanala761955c2024-02-21 19:32:20 +053050type pfConfig struct {
51 serviceconfig.LoadBalancingConfig `json:"-"`
52
53 // If set to true, instructs the LB policy to shuffle the order of the list
54 // of addresses received from the name resolver before attempting to
55 // connect to them.
56 ShuffleAddressList bool `json:"shuffleAddressList"`
57}
58
59func (*pickfirstBuilder) ParseConfig(js json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
60 cfg := &pfConfig{}
61 if err := json.Unmarshal(js, cfg); err != nil {
62 return nil, fmt.Errorf("pickfirst: unable to unmarshal LB policy config: %s, error: %v", string(js), err)
63 }
64 return cfg, nil
65}
66
khenaidoo5fc5cea2021-08-11 17:39:16 -040067type pickfirstBalancer struct {
Akash Kankanala761955c2024-02-21 19:32:20 +053068 state connectivity.State
69 cc balancer.ClientConn
70 subConn balancer.SubConn
71 cfg *pfConfig
khenaidoo5fc5cea2021-08-11 17:39:16 -040072}
73
74func (b *pickfirstBalancer) ResolverError(err error) {
khenaidoo5fc5cea2021-08-11 17:39:16 -040075 if logger.V(2) {
Akash Kankanala761955c2024-02-21 19:32:20 +053076 logger.Infof("pickfirstBalancer: ResolverError called with error: %v", err)
khenaidoo5fc5cea2021-08-11 17:39:16 -040077 }
Akash Kankanala761955c2024-02-21 19:32:20 +053078 if b.subConn == nil {
79 b.state = connectivity.TransientFailure
80 }
81
82 if b.state != connectivity.TransientFailure {
83 // The picker will not change since the balancer does not currently
84 // report an error.
85 return
86 }
87 b.cc.UpdateState(balancer.State{
88 ConnectivityState: connectivity.TransientFailure,
89 Picker: &picker{err: fmt.Errorf("name resolver error: %v", err)},
90 })
khenaidoo5fc5cea2021-08-11 17:39:16 -040091}
92
Akash Kankanala761955c2024-02-21 19:32:20 +053093func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState) error {
94 addrs := state.ResolverState.Addresses
95 if len(addrs) == 0 {
96 // The resolver reported an empty address list. Treat it like an error by
97 // calling b.ResolverError.
98 if b.subConn != nil {
99 // Remove the old subConn. All addresses were removed, so it is no longer
100 // valid.
101 b.cc.RemoveSubConn(b.subConn)
102 b.subConn = nil
103 }
khenaidoo5fc5cea2021-08-11 17:39:16 -0400104 b.ResolverError(errors.New("produced zero addresses"))
105 return balancer.ErrBadResolverState
106 }
Akash Kankanala761955c2024-02-21 19:32:20 +0530107
108 if state.BalancerConfig != nil {
109 cfg, ok := state.BalancerConfig.(*pfConfig)
110 if !ok {
111 return fmt.Errorf("pickfirstBalancer: received nil or illegal BalancerConfig (type %T): %v", state.BalancerConfig, state.BalancerConfig)
khenaidoo5fc5cea2021-08-11 17:39:16 -0400112 }
Akash Kankanala761955c2024-02-21 19:32:20 +0530113 b.cfg = cfg
khenaidoo5fc5cea2021-08-11 17:39:16 -0400114 }
Akash Kankanala761955c2024-02-21 19:32:20 +0530115
116 if envconfig.PickFirstLBConfig && b.cfg != nil && b.cfg.ShuffleAddressList {
117 grpcrand.Shuffle(len(addrs), func(i, j int) { addrs[i], addrs[j] = addrs[j], addrs[i] })
118 }
119 if b.subConn != nil {
120 b.cc.UpdateAddresses(b.subConn, addrs)
121 return nil
122 }
123
124 subConn, err := b.cc.NewSubConn(addrs, balancer.NewSubConnOptions{})
125 if err != nil {
126 if logger.V(2) {
127 logger.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err)
128 }
129 b.state = connectivity.TransientFailure
130 b.cc.UpdateState(balancer.State{
131 ConnectivityState: connectivity.TransientFailure,
132 Picker: &picker{err: fmt.Errorf("error creating connection: %v", err)},
133 })
134 return balancer.ErrBadResolverState
135 }
136 b.subConn = subConn
137 b.state = connectivity.Idle
138 b.cc.UpdateState(balancer.State{
139 ConnectivityState: connectivity.Connecting,
140 Picker: &picker{err: balancer.ErrNoSubConnAvailable},
141 })
142 b.subConn.Connect()
khenaidoo5fc5cea2021-08-11 17:39:16 -0400143 return nil
144}
145
Akash Kankanala761955c2024-02-21 19:32:20 +0530146func (b *pickfirstBalancer) UpdateSubConnState(subConn balancer.SubConn, state balancer.SubConnState) {
khenaidoo5fc5cea2021-08-11 17:39:16 -0400147 if logger.V(2) {
Akash Kankanala761955c2024-02-21 19:32:20 +0530148 logger.Infof("pickfirstBalancer: UpdateSubConnState: %p, %v", subConn, state)
khenaidoo5fc5cea2021-08-11 17:39:16 -0400149 }
Akash Kankanala761955c2024-02-21 19:32:20 +0530150 if b.subConn != subConn {
khenaidoo5fc5cea2021-08-11 17:39:16 -0400151 if logger.V(2) {
Akash Kankanala761955c2024-02-21 19:32:20 +0530152 logger.Infof("pickfirstBalancer: ignored state change because subConn is not recognized")
khenaidoo5fc5cea2021-08-11 17:39:16 -0400153 }
154 return
155 }
Akash Kankanala761955c2024-02-21 19:32:20 +0530156 if state.ConnectivityState == connectivity.Shutdown {
157 b.subConn = nil
khenaidoo5fc5cea2021-08-11 17:39:16 -0400158 return
159 }
160
Akash Kankanala761955c2024-02-21 19:32:20 +0530161 switch state.ConnectivityState {
khenaidoo5fc5cea2021-08-11 17:39:16 -0400162 case connectivity.Ready:
Akash Kankanala761955c2024-02-21 19:32:20 +0530163 b.cc.UpdateState(balancer.State{
164 ConnectivityState: state.ConnectivityState,
165 Picker: &picker{result: balancer.PickResult{SubConn: subConn}},
166 })
khenaidoo5fc5cea2021-08-11 17:39:16 -0400167 case connectivity.Connecting:
Akash Kankanala761955c2024-02-21 19:32:20 +0530168 if b.state == connectivity.TransientFailure {
169 // We stay in TransientFailure until we are Ready. See A62.
170 return
171 }
172 b.cc.UpdateState(balancer.State{
173 ConnectivityState: state.ConnectivityState,
174 Picker: &picker{err: balancer.ErrNoSubConnAvailable},
175 })
khenaidoo5fc5cea2021-08-11 17:39:16 -0400176 case connectivity.Idle:
Akash Kankanala761955c2024-02-21 19:32:20 +0530177 if b.state == connectivity.TransientFailure {
178 // We stay in TransientFailure until we are Ready. Also kick the
179 // subConn out of Idle into Connecting. See A62.
180 b.subConn.Connect()
181 return
182 }
183 b.cc.UpdateState(balancer.State{
184 ConnectivityState: state.ConnectivityState,
185 Picker: &idlePicker{subConn: subConn},
186 })
khenaidoo5fc5cea2021-08-11 17:39:16 -0400187 case connectivity.TransientFailure:
188 b.cc.UpdateState(balancer.State{
Akash Kankanala761955c2024-02-21 19:32:20 +0530189 ConnectivityState: state.ConnectivityState,
190 Picker: &picker{err: state.ConnectionError},
khenaidoo5fc5cea2021-08-11 17:39:16 -0400191 })
192 }
Akash Kankanala761955c2024-02-21 19:32:20 +0530193 b.state = state.ConnectivityState
khenaidoo5fc5cea2021-08-11 17:39:16 -0400194}
195
196func (b *pickfirstBalancer) Close() {
197}
198
199func (b *pickfirstBalancer) ExitIdle() {
Akash Kankanala761955c2024-02-21 19:32:20 +0530200 if b.subConn != nil && b.state == connectivity.Idle {
201 b.subConn.Connect()
khenaidoo5fc5cea2021-08-11 17:39:16 -0400202 }
203}
204
205type picker struct {
206 result balancer.PickResult
207 err error
208}
209
Akash Kankanala761955c2024-02-21 19:32:20 +0530210func (p *picker) Pick(balancer.PickInfo) (balancer.PickResult, error) {
khenaidoo5fc5cea2021-08-11 17:39:16 -0400211 return p.result, p.err
212}
213
214// idlePicker is used when the SubConn is IDLE and kicks the SubConn into
215// CONNECTING when Pick is called.
216type idlePicker struct {
Akash Kankanala761955c2024-02-21 19:32:20 +0530217 subConn balancer.SubConn
khenaidoo5fc5cea2021-08-11 17:39:16 -0400218}
219
Akash Kankanala761955c2024-02-21 19:32:20 +0530220func (i *idlePicker) Pick(balancer.PickInfo) (balancer.PickResult, error) {
221 i.subConn.Connect()
khenaidoo5fc5cea2021-08-11 17:39:16 -0400222 return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
223}
224
225func init() {
226 balancer.Register(newPickfirstBuilder())
227}