blob: f194d14a081673d079bd330547f8ee8d3de17cde [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 (
22 "errors"
23 "fmt"
24
25 "google.golang.org/grpc/balancer"
26 "google.golang.org/grpc/connectivity"
27)
28
29// PickFirstBalancerName is the name of the pick_first balancer.
30const PickFirstBalancerName = "pick_first"
31
32func newPickfirstBuilder() balancer.Builder {
33 return &pickfirstBuilder{}
34}
35
36type pickfirstBuilder struct{}
37
38func (*pickfirstBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer {
39 return &pickfirstBalancer{cc: cc}
40}
41
42func (*pickfirstBuilder) Name() string {
43 return PickFirstBalancerName
44}
45
46type pickfirstBalancer struct {
47 state connectivity.State
48 cc balancer.ClientConn
49 sc balancer.SubConn
50}
51
52func (b *pickfirstBalancer) ResolverError(err error) {
53 switch b.state {
54 case connectivity.TransientFailure, connectivity.Idle, connectivity.Connecting:
55 // Set a failing picker if we don't have a good picker.
56 b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.TransientFailure,
57 Picker: &picker{err: fmt.Errorf("name resolver error: %v", err)},
58 })
59 }
60 if logger.V(2) {
61 logger.Infof("pickfirstBalancer: ResolverError called with error %v", err)
62 }
63}
64
65func (b *pickfirstBalancer) UpdateClientConnState(cs balancer.ClientConnState) error {
66 if len(cs.ResolverState.Addresses) == 0 {
67 b.ResolverError(errors.New("produced zero addresses"))
68 return balancer.ErrBadResolverState
69 }
70 if b.sc == nil {
71 var err error
72 b.sc, err = b.cc.NewSubConn(cs.ResolverState.Addresses, balancer.NewSubConnOptions{})
73 if err != nil {
74 if logger.V(2) {
75 logger.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err)
76 }
77 b.state = connectivity.TransientFailure
78 b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.TransientFailure,
79 Picker: &picker{err: fmt.Errorf("error creating connection: %v", err)},
80 })
81 return balancer.ErrBadResolverState
82 }
83 b.state = connectivity.Idle
84 b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.Idle, Picker: &picker{result: balancer.PickResult{SubConn: b.sc}}})
85 b.sc.Connect()
86 } else {
87 b.cc.UpdateAddresses(b.sc, cs.ResolverState.Addresses)
88 b.sc.Connect()
89 }
90 return nil
91}
92
93func (b *pickfirstBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.SubConnState) {
94 if logger.V(2) {
95 logger.Infof("pickfirstBalancer: UpdateSubConnState: %p, %v", sc, s)
96 }
97 if b.sc != sc {
98 if logger.V(2) {
99 logger.Infof("pickfirstBalancer: ignored state change because sc is not recognized")
100 }
101 return
102 }
103 b.state = s.ConnectivityState
104 if s.ConnectivityState == connectivity.Shutdown {
105 b.sc = nil
106 return
107 }
108
109 switch s.ConnectivityState {
110 case connectivity.Ready:
111 b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{result: balancer.PickResult{SubConn: sc}}})
112 case connectivity.Connecting:
113 b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{err: balancer.ErrNoSubConnAvailable}})
114 case connectivity.Idle:
115 b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &idlePicker{sc: sc}})
116 case connectivity.TransientFailure:
117 b.cc.UpdateState(balancer.State{
118 ConnectivityState: s.ConnectivityState,
119 Picker: &picker{err: s.ConnectionError},
120 })
121 }
122}
123
124func (b *pickfirstBalancer) Close() {
125}
126
127func (b *pickfirstBalancer) ExitIdle() {
128 if b.state == connectivity.Idle {
129 b.sc.Connect()
130 }
131}
132
133type picker struct {
134 result balancer.PickResult
135 err error
136}
137
138func (p *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
139 return p.result, p.err
140}
141
142// idlePicker is used when the SubConn is IDLE and kicks the SubConn into
143// CONNECTING when Pick is called.
144type idlePicker struct {
145 sc balancer.SubConn
146}
147
148func (i *idlePicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
149 i.sc.Connect()
150 return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
151}
152
153func init() {
154 balancer.Register(newPickfirstBuilder())
155}