google.golang.org/grpc@v1.72.2/balancer/weightedroundrobin/metrics_test.go (about)

     1  /*
     2   *
     3   * Copyright 2024 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 weightedroundrobin
    20  
    21  import (
    22  	"testing"
    23  	"time"
    24  
    25  	"google.golang.org/grpc/internal/grpctest"
    26  	iserviceconfig "google.golang.org/grpc/internal/serviceconfig"
    27  	"google.golang.org/grpc/internal/testutils/stats"
    28  )
    29  
    30  type s struct {
    31  	grpctest.Tester
    32  }
    33  
    34  func Test(t *testing.T) {
    35  	grpctest.RunSubTests(t, s{})
    36  }
    37  
    38  // TestWRR_Metrics_SubConnWeight tests different scenarios for the weight call
    39  // on a weighted SubConn, and expects certain metrics for each of these
    40  // scenarios.
    41  func (s) TestWRR_Metrics_SubConnWeight(t *testing.T) {
    42  	tests := []struct {
    43  		name                           string
    44  		weightExpirationPeriod         time.Duration
    45  		blackoutPeriod                 time.Duration
    46  		lastUpdated                    time.Time
    47  		nonEmpty                       time.Time
    48  		nowTime                        time.Time
    49  		endpointWeightStaleWant        float64
    50  		endpointWeightNotYetUsableWant float64
    51  		endpointWeightWant             float64
    52  	}{
    53  		// The weighted SubConn's lastUpdated field hasn't been set, so this
    54  		// SubConn's weight is not yet usable. Thus, should emit that endpoint
    55  		// weight is not yet usable, and 0 for weight.
    56  		{
    57  			name:                           "no weight set",
    58  			weightExpirationPeriod:         time.Second,
    59  			blackoutPeriod:                 time.Second,
    60  			nowTime:                        time.Now(),
    61  			endpointWeightStaleWant:        0,
    62  			endpointWeightNotYetUsableWant: 1,
    63  			endpointWeightWant:             0,
    64  		},
    65  		{
    66  			name:                           "weight expiration",
    67  			lastUpdated:                    time.Now(),
    68  			weightExpirationPeriod:         2 * time.Second,
    69  			blackoutPeriod:                 time.Second,
    70  			nowTime:                        time.Now().Add(100 * time.Second),
    71  			endpointWeightStaleWant:        1,
    72  			endpointWeightNotYetUsableWant: 0,
    73  			endpointWeightWant:             0,
    74  		},
    75  		{
    76  			name:                           "in blackout period",
    77  			lastUpdated:                    time.Now(),
    78  			weightExpirationPeriod:         time.Minute,
    79  			blackoutPeriod:                 10 * time.Second,
    80  			nowTime:                        time.Now(),
    81  			endpointWeightStaleWant:        0,
    82  			endpointWeightNotYetUsableWant: 1,
    83  			endpointWeightWant:             0,
    84  		},
    85  		{
    86  			name:                           "normal weight",
    87  			lastUpdated:                    time.Now(),
    88  			nonEmpty:                       time.Now(),
    89  			weightExpirationPeriod:         time.Minute,
    90  			blackoutPeriod:                 time.Second,
    91  			nowTime:                        time.Now().Add(10 * time.Second),
    92  			endpointWeightStaleWant:        0,
    93  			endpointWeightNotYetUsableWant: 0,
    94  			endpointWeightWant:             3,
    95  		},
    96  		{
    97  			name:                           "weight expiration takes precdedence over blackout",
    98  			lastUpdated:                    time.Now(),
    99  			nonEmpty:                       time.Now(),
   100  			weightExpirationPeriod:         time.Second,
   101  			blackoutPeriod:                 time.Minute,
   102  			nowTime:                        time.Now().Add(10 * time.Second),
   103  			endpointWeightStaleWant:        1,
   104  			endpointWeightNotYetUsableWant: 0,
   105  			endpointWeightWant:             0,
   106  		},
   107  	}
   108  
   109  	for _, test := range tests {
   110  		t.Run(test.name, func(t *testing.T) {
   111  			tmr := stats.NewTestMetricsRecorder()
   112  			wsc := &endpointWeight{
   113  				metricsRecorder: tmr,
   114  				weightVal:       3,
   115  				lastUpdated:     test.lastUpdated,
   116  				nonEmptySince:   test.nonEmpty,
   117  			}
   118  			wsc.weight(test.nowTime, test.weightExpirationPeriod, test.blackoutPeriod, true)
   119  
   120  			if got, _ := tmr.Metric("grpc.lb.wrr.endpoint_weight_stale"); got != test.endpointWeightStaleWant {
   121  				t.Fatalf("Unexpected data for metric %v, got: %v, want: %v", "grpc.lb.wrr.endpoint_weight_stale", got, test.endpointWeightStaleWant)
   122  			}
   123  			if got, _ := tmr.Metric("grpc.lb.wrr.endpoint_weight_not_yet_usable"); got != test.endpointWeightNotYetUsableWant {
   124  				t.Fatalf("Unexpected data for metric %v, got: %v, want: %v", "grpc.lb.wrr.endpoint_weight_not_yet_usable", got, test.endpointWeightNotYetUsableWant)
   125  			}
   126  			if got, _ := tmr.Metric("grpc.lb.wrr.endpoint_weight_stale"); got != test.endpointWeightStaleWant {
   127  				t.Fatalf("Unexpected data for metric %v, got: %v, want: %v", "grpc.lb.wrr.endpoint_weight_stale", got, test.endpointWeightStaleWant)
   128  			}
   129  		})
   130  	}
   131  
   132  }
   133  
   134  // TestWRR_Metrics_Scheduler_RR_Fallback tests the round robin fallback metric
   135  // for scheduler updates. It tests the case with one SubConn, and two SubConns
   136  // with no weights. Both of these should emit a count metric for round robin
   137  // fallback.
   138  func (s) TestWRR_Metrics_Scheduler_RR_Fallback(t *testing.T) {
   139  	tmr := stats.NewTestMetricsRecorder()
   140  	ew := &endpointWeight{
   141  		metricsRecorder: tmr,
   142  		weightVal:       0,
   143  	}
   144  
   145  	p := &picker{
   146  		cfg: &lbConfig{
   147  			BlackoutPeriod:         iserviceconfig.Duration(10 * time.Second),
   148  			WeightExpirationPeriod: iserviceconfig.Duration(3 * time.Minute),
   149  		},
   150  		weightedPickers: []pickerWeightedEndpoint{{weightedEndpoint: ew}},
   151  		metricsRecorder: tmr,
   152  	}
   153  	// There is only one SubConn, so no matter if the SubConn has a weight or
   154  	// not will fallback to round robin.
   155  	p.regenerateScheduler()
   156  	if got, _ := tmr.Metric("grpc.lb.wrr.rr_fallback"); got != 1 {
   157  		t.Fatalf("Unexpected data for metric %v, got: %v, want: %v", "grpc.lb.wrr.rr_fallback", got, 1)
   158  	}
   159  	tmr.ClearMetrics()
   160  
   161  	// With two SubConns, if neither of them have weights, it will also fallback
   162  	// to round robin.
   163  	ew2 := &endpointWeight{
   164  		target:          "target",
   165  		metricsRecorder: tmr,
   166  		weightVal:       0,
   167  	}
   168  	p.weightedPickers = append(p.weightedPickers, pickerWeightedEndpoint{weightedEndpoint: ew2})
   169  	p.regenerateScheduler()
   170  	if got, _ := tmr.Metric("grpc.lb.wrr.rr_fallback"); got != 1 {
   171  		t.Fatalf("Unexpected data for metric %v, got: %v, want: %v", "grpc.lb.wrr.rr_fallback", got, 1)
   172  	}
   173  }