mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 06:12:25 -05:00 
			
		
		
		
	
		
			
	
	
		
			184 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			184 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | /* | ||
|  |  * | ||
|  |  * Copyright 2017 gRPC authors. | ||
|  |  * | ||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||
|  |  * you may not use this file except in compliance with the License. | ||
|  |  * You may obtain a copy of the License at | ||
|  |  * | ||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||
|  |  * | ||
|  |  * Unless required by applicable law or agreed to in writing, software | ||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
|  |  * See the License for the specific language governing permissions and | ||
|  |  * limitations under the License. | ||
|  |  * | ||
|  |  */ | ||
|  | 
 | ||
|  | package grpc | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"errors" | ||
|  | 	"fmt" | ||
|  | 
 | ||
|  | 	"google.golang.org/grpc/balancer" | ||
|  | 	"google.golang.org/grpc/connectivity" | ||
|  | ) | ||
|  | 
 | ||
|  | // PickFirstBalancerName is the name of the pick_first balancer. | ||
|  | const PickFirstBalancerName = "pick_first" | ||
|  | 
 | ||
|  | func newPickfirstBuilder() balancer.Builder { | ||
|  | 	return &pickfirstBuilder{} | ||
|  | } | ||
|  | 
 | ||
|  | type pickfirstBuilder struct{} | ||
|  | 
 | ||
|  | func (*pickfirstBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer { | ||
|  | 	return &pickfirstBalancer{cc: cc} | ||
|  | } | ||
|  | 
 | ||
|  | func (*pickfirstBuilder) Name() string { | ||
|  | 	return PickFirstBalancerName | ||
|  | } | ||
|  | 
 | ||
|  | type pickfirstBalancer struct { | ||
|  | 	state   connectivity.State | ||
|  | 	cc      balancer.ClientConn | ||
|  | 	subConn balancer.SubConn | ||
|  | } | ||
|  | 
 | ||
|  | func (b *pickfirstBalancer) ResolverError(err error) { | ||
|  | 	if logger.V(2) { | ||
|  | 		logger.Infof("pickfirstBalancer: ResolverError called with error: %v", err) | ||
|  | 	} | ||
|  | 	if b.subConn == nil { | ||
|  | 		b.state = connectivity.TransientFailure | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if b.state != connectivity.TransientFailure { | ||
|  | 		// The picker will not change since the balancer does not currently | ||
|  | 		// report an error. | ||
|  | 		return | ||
|  | 	} | ||
|  | 	b.cc.UpdateState(balancer.State{ | ||
|  | 		ConnectivityState: connectivity.TransientFailure, | ||
|  | 		Picker:            &picker{err: fmt.Errorf("name resolver error: %v", err)}, | ||
|  | 	}) | ||
|  | } | ||
|  | 
 | ||
|  | func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState) error { | ||
|  | 	if len(state.ResolverState.Addresses) == 0 { | ||
|  | 		// The resolver reported an empty address list. Treat it like an error by | ||
|  | 		// calling b.ResolverError. | ||
|  | 		if b.subConn != nil { | ||
|  | 			// Remove the old subConn. All addresses were removed, so it is no longer | ||
|  | 			// valid. | ||
|  | 			b.cc.RemoveSubConn(b.subConn) | ||
|  | 			b.subConn = nil | ||
|  | 		} | ||
|  | 		b.ResolverError(errors.New("produced zero addresses")) | ||
|  | 		return balancer.ErrBadResolverState | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if b.subConn != nil { | ||
|  | 		b.cc.UpdateAddresses(b.subConn, state.ResolverState.Addresses) | ||
|  | 		return nil | ||
|  | 	} | ||
|  | 
 | ||
|  | 	subConn, err := b.cc.NewSubConn(state.ResolverState.Addresses, balancer.NewSubConnOptions{}) | ||
|  | 	if err != nil { | ||
|  | 		if logger.V(2) { | ||
|  | 			logger.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err) | ||
|  | 		} | ||
|  | 		b.state = connectivity.TransientFailure | ||
|  | 		b.cc.UpdateState(balancer.State{ | ||
|  | 			ConnectivityState: connectivity.TransientFailure, | ||
|  | 			Picker:            &picker{err: fmt.Errorf("error creating connection: %v", err)}, | ||
|  | 		}) | ||
|  | 		return balancer.ErrBadResolverState | ||
|  | 	} | ||
|  | 	b.subConn = subConn | ||
|  | 	b.state = connectivity.Idle | ||
|  | 	b.cc.UpdateState(balancer.State{ | ||
|  | 		ConnectivityState: connectivity.Connecting, | ||
|  | 		Picker:            &picker{err: balancer.ErrNoSubConnAvailable}, | ||
|  | 	}) | ||
|  | 	b.subConn.Connect() | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | func (b *pickfirstBalancer) UpdateSubConnState(subConn balancer.SubConn, state balancer.SubConnState) { | ||
|  | 	if logger.V(2) { | ||
|  | 		logger.Infof("pickfirstBalancer: UpdateSubConnState: %p, %v", subConn, state) | ||
|  | 	} | ||
|  | 	if b.subConn != subConn { | ||
|  | 		if logger.V(2) { | ||
|  | 			logger.Infof("pickfirstBalancer: ignored state change because subConn is not recognized") | ||
|  | 		} | ||
|  | 		return | ||
|  | 	} | ||
|  | 	b.state = state.ConnectivityState | ||
|  | 	if state.ConnectivityState == connectivity.Shutdown { | ||
|  | 		b.subConn = nil | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	switch state.ConnectivityState { | ||
|  | 	case connectivity.Ready: | ||
|  | 		b.cc.UpdateState(balancer.State{ | ||
|  | 			ConnectivityState: state.ConnectivityState, | ||
|  | 			Picker:            &picker{result: balancer.PickResult{SubConn: subConn}}, | ||
|  | 		}) | ||
|  | 	case connectivity.Connecting: | ||
|  | 		b.cc.UpdateState(balancer.State{ | ||
|  | 			ConnectivityState: state.ConnectivityState, | ||
|  | 			Picker:            &picker{err: balancer.ErrNoSubConnAvailable}, | ||
|  | 		}) | ||
|  | 	case connectivity.Idle: | ||
|  | 		b.cc.UpdateState(balancer.State{ | ||
|  | 			ConnectivityState: state.ConnectivityState, | ||
|  | 			Picker:            &idlePicker{subConn: subConn}, | ||
|  | 		}) | ||
|  | 	case connectivity.TransientFailure: | ||
|  | 		b.cc.UpdateState(balancer.State{ | ||
|  | 			ConnectivityState: state.ConnectivityState, | ||
|  | 			Picker:            &picker{err: state.ConnectionError}, | ||
|  | 		}) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (b *pickfirstBalancer) Close() { | ||
|  | } | ||
|  | 
 | ||
|  | func (b *pickfirstBalancer) ExitIdle() { | ||
|  | 	if b.subConn != nil && b.state == connectivity.Idle { | ||
|  | 		b.subConn.Connect() | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | type picker struct { | ||
|  | 	result balancer.PickResult | ||
|  | 	err    error | ||
|  | } | ||
|  | 
 | ||
|  | func (p *picker) Pick(balancer.PickInfo) (balancer.PickResult, error) { | ||
|  | 	return p.result, p.err | ||
|  | } | ||
|  | 
 | ||
|  | // idlePicker is used when the SubConn is IDLE and kicks the SubConn into | ||
|  | // CONNECTING when Pick is called. | ||
|  | type idlePicker struct { | ||
|  | 	subConn balancer.SubConn | ||
|  | } | ||
|  | 
 | ||
|  | func (i *idlePicker) Pick(balancer.PickInfo) (balancer.PickResult, error) { | ||
|  | 	i.subConn.Connect() | ||
|  | 	return balancer.PickResult{}, balancer.ErrNoSubConnAvailable | ||
|  | } | ||
|  | 
 | ||
|  | func init() { | ||
|  | 	balancer.Register(newPickfirstBuilder()) | ||
|  | } |