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