...

Source file src/google.golang.org/grpc/balancer/conn_state_evaluator_test.go

Documentation: google.golang.org/grpc/balancer

     1  /*
     2   *
     3   * Copyright 2022 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  
    19  package balancer
    20  
    21  import (
    22  	"testing"
    23  
    24  	"google.golang.org/grpc/connectivity"
    25  	"google.golang.org/grpc/internal/grpctest"
    26  )
    27  
    28  type s struct {
    29  	grpctest.Tester
    30  }
    31  
    32  func Test(t *testing.T) {
    33  	grpctest.RunSubTests(t, s{})
    34  }
    35  
    36  // TestRecordTransition_FirstStateChange tests the first call to
    37  // RecordTransition where the `oldState` is usually set to `Shutdown` (a state
    38  // that the ConnectivityStateEvaluator is set to ignore).
    39  func (s) TestRecordTransition_FirstStateChange(t *testing.T) {
    40  	tests := []struct {
    41  		newState  connectivity.State
    42  		wantState connectivity.State
    43  	}{
    44  		{
    45  			newState:  connectivity.Idle,
    46  			wantState: connectivity.Idle,
    47  		},
    48  		{
    49  			newState:  connectivity.Connecting,
    50  			wantState: connectivity.Connecting,
    51  		},
    52  		{
    53  			newState:  connectivity.Ready,
    54  			wantState: connectivity.Ready,
    55  		},
    56  		{
    57  			newState:  connectivity.TransientFailure,
    58  			wantState: connectivity.TransientFailure,
    59  		},
    60  		{
    61  			newState:  connectivity.Shutdown,
    62  			wantState: connectivity.TransientFailure,
    63  		},
    64  	}
    65  	for _, test := range tests {
    66  		cse := &ConnectivityStateEvaluator{}
    67  		if gotState := cse.RecordTransition(connectivity.Shutdown, test.newState); gotState != test.wantState {
    68  			t.Fatalf("RecordTransition(%v, %v) = %v, want %v", connectivity.Shutdown, test.newState, gotState, test.wantState)
    69  		}
    70  	}
    71  }
    72  
    73  // TestRecordTransition_SameState tests the scenario where state transitions to
    74  // the same state are recorded multiple times.
    75  func (s) TestRecordTransition_SameState(t *testing.T) {
    76  	tests := []struct {
    77  		newState  connectivity.State
    78  		wantState connectivity.State
    79  	}{
    80  		{
    81  			newState:  connectivity.Idle,
    82  			wantState: connectivity.Idle,
    83  		},
    84  		{
    85  			newState:  connectivity.Connecting,
    86  			wantState: connectivity.Connecting,
    87  		},
    88  		{
    89  			newState:  connectivity.Ready,
    90  			wantState: connectivity.Ready,
    91  		},
    92  		{
    93  			newState:  connectivity.TransientFailure,
    94  			wantState: connectivity.TransientFailure,
    95  		},
    96  		{
    97  			newState:  connectivity.Shutdown,
    98  			wantState: connectivity.TransientFailure,
    99  		},
   100  	}
   101  	const numStateChanges = 5
   102  	for _, test := range tests {
   103  		cse := &ConnectivityStateEvaluator{}
   104  		var prevState, gotState connectivity.State
   105  		prevState = connectivity.Shutdown
   106  		for i := 0; i < numStateChanges; i++ {
   107  			gotState = cse.RecordTransition(prevState, test.newState)
   108  			prevState = test.newState
   109  		}
   110  		if gotState != test.wantState {
   111  			t.Fatalf("RecordTransition() = %v, want %v", gotState, test.wantState)
   112  		}
   113  	}
   114  }
   115  
   116  // TestRecordTransition_SingleSubConn_DifferentStates tests some common
   117  // connectivity state change scenarios, on a single subConn.
   118  func (s) TestRecordTransition_SingleSubConn_DifferentStates(t *testing.T) {
   119  	tests := []struct {
   120  		name      string
   121  		states    []connectivity.State
   122  		wantState connectivity.State
   123  	}{
   124  		{
   125  			name:      "regular transition to ready",
   126  			states:    []connectivity.State{connectivity.Idle, connectivity.Connecting, connectivity.Ready},
   127  			wantState: connectivity.Ready,
   128  		},
   129  		{
   130  			name:      "regular transition to transient failure",
   131  			states:    []connectivity.State{connectivity.Idle, connectivity.Connecting, connectivity.TransientFailure},
   132  			wantState: connectivity.TransientFailure,
   133  		},
   134  		{
   135  			name:      "regular transition to ready",
   136  			states:    []connectivity.State{connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.Idle},
   137  			wantState: connectivity.Idle,
   138  		},
   139  		{
   140  			name:      "transition from ready to transient failure",
   141  			states:    []connectivity.State{connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.TransientFailure},
   142  			wantState: connectivity.TransientFailure,
   143  		},
   144  		{
   145  			name:      "transition from transient failure back to ready",
   146  			states:    []connectivity.State{connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.TransientFailure, connectivity.Ready},
   147  			wantState: connectivity.Ready,
   148  		},
   149  		{
   150  			// This state transition is usually suppressed at the LB policy level, by
   151  			// not calling RecordTransition.
   152  			name:      "transition from transient failure back to idle",
   153  			states:    []connectivity.State{connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.TransientFailure, connectivity.Idle},
   154  			wantState: connectivity.Idle,
   155  		},
   156  		{
   157  			// This state transition is usually suppressed at the LB policy level, by
   158  			// not calling RecordTransition.
   159  			name:      "transition from transient failure back to connecting",
   160  			states:    []connectivity.State{connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.TransientFailure, connectivity.Connecting},
   161  			wantState: connectivity.Connecting,
   162  		},
   163  	}
   164  
   165  	for _, test := range tests {
   166  		t.Run(test.name, func(t *testing.T) {
   167  			cse := &ConnectivityStateEvaluator{}
   168  			var prevState, gotState connectivity.State
   169  			prevState = connectivity.Shutdown
   170  			for _, newState := range test.states {
   171  				gotState = cse.RecordTransition(prevState, newState)
   172  				prevState = newState
   173  			}
   174  			if gotState != test.wantState {
   175  				t.Fatalf("RecordTransition() = %v, want %v", gotState, test.wantState)
   176  			}
   177  		})
   178  	}
   179  }
   180  
   181  // TestRecordTransition_MultipleSubConns_DifferentStates tests state transitions
   182  // among multiple subConns, and verifies that the connectivity state aggregation
   183  // algorithm produces the expected aggregate connectivity state.
   184  func (s) TestRecordTransition_MultipleSubConns_DifferentStates(t *testing.T) {
   185  	tests := []struct {
   186  		name string
   187  		// Each entry in this slice corresponds to the state changes happening on an
   188  		// individual subConn.
   189  		subConnStates [][]connectivity.State
   190  		wantState     connectivity.State
   191  	}{
   192  		{
   193  			name: "atleast one ready",
   194  			subConnStates: [][]connectivity.State{
   195  				{connectivity.Idle, connectivity.Connecting, connectivity.Ready},
   196  				{connectivity.Idle},
   197  				{connectivity.Idle, connectivity.Connecting},
   198  				{connectivity.Idle, connectivity.Connecting, connectivity.TransientFailure},
   199  			},
   200  			wantState: connectivity.Ready,
   201  		},
   202  		{
   203  			name: "atleast one connecting",
   204  			subConnStates: [][]connectivity.State{
   205  				{connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.Connecting},
   206  				{connectivity.Idle},
   207  				{connectivity.Idle, connectivity.Connecting, connectivity.TransientFailure},
   208  			},
   209  			wantState: connectivity.Connecting,
   210  		},
   211  		{
   212  			name: "atleast one idle",
   213  			subConnStates: [][]connectivity.State{
   214  				{connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.Idle},
   215  				{connectivity.Idle, connectivity.Connecting, connectivity.TransientFailure},
   216  			},
   217  			wantState: connectivity.Idle,
   218  		},
   219  		{
   220  			name: "atleast one transient failure",
   221  			subConnStates: [][]connectivity.State{
   222  				{connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.TransientFailure},
   223  				{connectivity.TransientFailure},
   224  			},
   225  			wantState: connectivity.TransientFailure,
   226  		},
   227  	}
   228  
   229  	for _, test := range tests {
   230  		t.Run(test.name, func(t *testing.T) {
   231  			cse := &ConnectivityStateEvaluator{}
   232  			var prevState, gotState connectivity.State
   233  			for _, scStates := range test.subConnStates {
   234  				prevState = connectivity.Shutdown
   235  				for _, newState := range scStates {
   236  					gotState = cse.RecordTransition(prevState, newState)
   237  					prevState = newState
   238  				}
   239  			}
   240  			if gotState != test.wantState {
   241  				t.Fatalf("RecordTransition() = %v, want %v", gotState, test.wantState)
   242  			}
   243  		})
   244  	}
   245  }
   246  

View as plain text