go.etcd.io/etcd@v3.3.27+incompatible/clientv3/balancer/connectivity/connectivity.go (about)

     1  // Copyright 2019 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package connectivity implements client connectivity operations.
    16  package connectivity
    17  
    18  import (
    19  	"sync"
    20  
    21  	"go.uber.org/zap"
    22  	"google.golang.org/grpc/connectivity"
    23  )
    24  
    25  // Recorder records gRPC connectivity.
    26  type Recorder interface {
    27  	GetCurrentState() connectivity.State
    28  	RecordTransition(oldState, newState connectivity.State)
    29  }
    30  
    31  // New returns a new Recorder.
    32  func New(lg *zap.Logger) Recorder {
    33  	return &recorder{lg: lg}
    34  }
    35  
    36  // recorder takes the connectivity states of multiple SubConns
    37  // and returns one aggregated connectivity state.
    38  // ref. https://github.com/grpc/grpc-go/blob/master/balancer/balancer.go
    39  type recorder struct {
    40  	lg *zap.Logger
    41  
    42  	mu sync.RWMutex
    43  
    44  	cur connectivity.State
    45  
    46  	numReady            uint64 // Number of addrConns in ready state.
    47  	numConnecting       uint64 // Number of addrConns in connecting state.
    48  	numTransientFailure uint64 // Number of addrConns in transientFailure.
    49  }
    50  
    51  func (rc *recorder) GetCurrentState() (state connectivity.State) {
    52  	rc.mu.RLock()
    53  	defer rc.mu.RUnlock()
    54  	return rc.cur
    55  }
    56  
    57  // RecordTransition records state change happening in subConn and based on that
    58  // it evaluates what aggregated state should be.
    59  //
    60  //  - If at least one SubConn in Ready, the aggregated state is Ready;
    61  //  - Else if at least one SubConn in Connecting, the aggregated state is Connecting;
    62  //  - Else the aggregated state is TransientFailure.
    63  //
    64  // Idle and Shutdown are not considered.
    65  //
    66  // ref. https://github.com/grpc/grpc-go/blob/master/balancer/balancer.go
    67  func (rc *recorder) RecordTransition(oldState, newState connectivity.State) {
    68  	rc.mu.Lock()
    69  	defer rc.mu.Unlock()
    70  
    71  	for idx, state := range []connectivity.State{oldState, newState} {
    72  		updateVal := 2*uint64(idx) - 1 // -1 for oldState and +1 for new.
    73  		switch state {
    74  		case connectivity.Ready:
    75  			rc.numReady += updateVal
    76  		case connectivity.Connecting:
    77  			rc.numConnecting += updateVal
    78  		case connectivity.TransientFailure:
    79  			rc.numTransientFailure += updateVal
    80  		default:
    81  			rc.lg.Warn("connectivity recorder received unknown state", zap.String("connectivity-state", state.String()))
    82  		}
    83  	}
    84  
    85  	switch { // must be exclusive, no overlap
    86  	case rc.numReady > 0:
    87  		rc.cur = connectivity.Ready
    88  	case rc.numConnecting > 0:
    89  		rc.cur = connectivity.Connecting
    90  	default:
    91  		rc.cur = connectivity.TransientFailure
    92  	}
    93  }