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 }