vitess.io/vitess@v0.16.2/go/stats/counters_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package stats 18 19 import ( 20 "expvar" 21 "math/rand" 22 "reflect" 23 "sort" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/stretchr/testify/assert" 29 ) 30 31 func TestCounters(t *testing.T) { 32 clear() 33 c := NewCountersWithSingleLabel("counter1", "help", "label") 34 c.Add("c1", 1) 35 c.Add("c2", 1) 36 c.Add("c2", 1) 37 want1 := `{"c1": 1, "c2": 2}` 38 want2 := `{"c2": 2, "c1": 1}` 39 if s := c.String(); s != want1 && s != want2 { 40 t.Errorf("want %s or %s, got %s", want1, want2, s) 41 } 42 counts := c.Counts() 43 if counts["c1"] != 1 { 44 t.Errorf("want 1, got %d", counts["c1"]) 45 } 46 if counts["c2"] != 2 { 47 t.Errorf("want 2, got %d", counts["c2"]) 48 } 49 } 50 51 func TestCountersTags(t *testing.T) { 52 clear() 53 c := NewCountersWithSingleLabel("counterTag1", "help", "label") 54 want := map[string]int64{} 55 got := c.Counts() 56 if !reflect.DeepEqual(got, want) { 57 t.Errorf("want %v, got %v", want, got) 58 } 59 60 c = NewCountersWithSingleLabel("counterTag2", "help", "label", "tag1", "tag2") 61 want = map[string]int64{"tag1": 0, "tag2": 0} 62 got = c.Counts() 63 if !reflect.DeepEqual(got, want) { 64 t.Errorf("want %v, got %v", want, got) 65 } 66 } 67 68 func TestMultiCounters(t *testing.T) { 69 clear() 70 c := NewCountersWithMultiLabels("mapCounter1", "help", []string{"aaa", "bbb"}) 71 c.Add([]string{"c1a", "c1b"}, 1) 72 c.Add([]string{"c2a", "c2b"}, 1) 73 c.Add([]string{"c2a", "c2b"}, 1) 74 want1 := `{"c1a.c1b": 1, "c2a.c2b": 2}` 75 want2 := `{"c2a.c2b": 2, "c1a.c1b": 1}` 76 if s := c.String(); s != want1 && s != want2 { 77 t.Errorf("want %s or %s, got %s", want1, want2, s) 78 } 79 counts := c.Counts() 80 if counts["c1a.c1b"] != 1 { 81 t.Errorf("want 1, got %d", counts["c1a.c1b"]) 82 } 83 if counts["c2a.c2b"] != 2 { 84 t.Errorf("want 2, got %d", counts["c2a.c2b"]) 85 } 86 f := NewCountersFuncWithMultiLabels("", "help", []string{"aaa", "bbb"}, func() map[string]int64 { 87 return map[string]int64{ 88 "c1a.c1b": 1, 89 "c2a.c2b": 2, 90 } 91 }) 92 if s := f.String(); s != want1 && s != want2 { 93 t.Errorf("want %s or %s, got %s", want1, want2, s) 94 } 95 } 96 97 func TestMultiCountersDot(t *testing.T) { 98 clear() 99 c := NewCountersWithMultiLabels("mapCounter2", "help", []string{"aaa", "bbb"}) 100 c.Add([]string{"c1.a", "c1b"}, 1) 101 c.Add([]string{"c2a", "c2.b"}, 1) 102 c.Add([]string{"c2a", "c2.b"}, 1) 103 c1a := safeLabel("c1.a") 104 c1aJSON := strings.Replace(c1a, "\\", "\\\\", -1) 105 c2b := safeLabel("c2.b") 106 c2bJSON := strings.Replace(c2b, "\\", "\\\\", -1) 107 want1 := `{"` + c1aJSON + `.c1b": 1, "c2a.` + c2bJSON + `": 2}` 108 want2 := `{"c2a.` + c2bJSON + `": 2, "` + c1aJSON + `.c1b": 1}` 109 if s := c.String(); s != want1 && s != want2 { 110 t.Errorf("want %s or %s, got %s", want1, want2, s) 111 } 112 counts := c.Counts() 113 if counts[c1a+".c1b"] != 1 { 114 t.Errorf("want 1, got %d", counts[c1a+".c1b"]) 115 } 116 if counts["c2a."+c2b] != 2 { 117 t.Errorf("want 2, got %d", counts["c2a."+c2b]) 118 } 119 } 120 121 func TestCountersHook(t *testing.T) { 122 var gotname string 123 var gotv *CountersWithSingleLabel 124 clear() 125 Register(func(name string, v expvar.Var) { 126 gotname = name 127 gotv = v.(*CountersWithSingleLabel) 128 }) 129 130 v := NewCountersWithSingleLabel("counter2", "help", "label") 131 if gotname != "counter2" { 132 t.Errorf("want counter2, got %s", gotname) 133 } 134 if gotv != v { 135 t.Errorf("want %#v, got %#v", v, gotv) 136 } 137 } 138 139 var benchCounter = NewCountersWithSingleLabel("bench", "help", "label") 140 141 func BenchmarkCounters(b *testing.B) { 142 clear() 143 benchCounter.Add("c1", 1) 144 b.ResetTimer() 145 146 b.RunParallel(func(pb *testing.PB) { 147 for pb.Next() { 148 benchCounter.Add("c1", 1) 149 } 150 }) 151 } 152 153 var benchMultiCounter = NewCountersWithMultiLabels("benchMulti", "help", []string{"call", "keyspace", "dbtype"}) 154 155 func BenchmarkMultiCounters(b *testing.B) { 156 clear() 157 key := []string{"execute-key-ranges", "keyspacename", "replica"} 158 benchMultiCounter.Add(key, 1) 159 b.ResetTimer() 160 161 b.RunParallel(func(pb *testing.PB) { 162 for pb.Next() { 163 benchMultiCounter.Add(key, 1) 164 } 165 }) 166 } 167 168 func BenchmarkCountersTailLatency(b *testing.B) { 169 // For this one, ignore the time reported by 'go test'. 170 // The 99th Percentile log line is all that matters. 171 // (Cmd: go test -bench=BenchmarkCountersTailLatency -benchtime=30s -cpu=10) 172 clear() 173 benchCounter.Add("c1", 1) 174 c := make(chan time.Duration, 100) 175 done := make(chan struct{}) 176 go func() { 177 all := make([]int, b.N) 178 i := 0 179 for dur := range c { 180 all[i] = int(dur) 181 i++ 182 } 183 sort.Ints(all) 184 p99 := time.Duration(all[b.N*99/100]) 185 b.Logf("99th Percentile (for N=%v): %v", b.N, p99) 186 close(done) 187 }() 188 189 b.ResetTimer() 190 b.SetParallelism(100) // The actual number of goroutines is 100*GOMAXPROCS 191 b.RunParallel(func(pb *testing.PB) { 192 r := rand.New(rand.NewSource(time.Now().UnixNano())) 193 194 var start time.Time 195 196 for pb.Next() { 197 // sleep between 0~200ms to simulate 10 QPS per goroutine. 198 time.Sleep(time.Duration(r.Int63n(200)) * time.Millisecond) 199 start = time.Now() 200 benchCounter.Add("c1", 1) 201 c <- time.Since(start) 202 } 203 }) 204 b.StopTimer() 205 206 close(c) 207 <-done 208 } 209 210 func TestCountersFuncWithMultiLabels(t *testing.T) { 211 clear() 212 f := NewCountersFuncWithMultiLabels("TestCountersFuncWithMultiLabels", "help", []string{"label1"}, func() map[string]int64 { 213 return map[string]int64{ 214 "c1": 1, 215 "c2": 2, 216 } 217 }) 218 219 want1 := `{"c1": 1, "c2": 2}` 220 want2 := `{"c2": 2, "c1": 1}` 221 if s := f.String(); s != want1 && s != want2 { 222 t.Errorf("want %s or %s, got %s", want1, want2, s) 223 } 224 } 225 226 func TestCountersFuncWithMultiLabels_Hook(t *testing.T) { 227 var gotname string 228 var gotv *CountersFuncWithMultiLabels 229 clear() 230 Register(func(name string, v expvar.Var) { 231 gotname = name 232 gotv = v.(*CountersFuncWithMultiLabels) 233 }) 234 235 v := NewCountersFuncWithMultiLabels("TestCountersFuncWithMultiLabels_Hook", "help", []string{"label1"}, func() map[string]int64 { 236 return map[string]int64{} 237 }) 238 if gotname != "TestCountersFuncWithMultiLabels_Hook" { 239 t.Errorf("want TestCountersFuncWithMultiLabels_Hook, got %s", gotname) 240 } 241 if gotv != v { 242 t.Errorf("want %#v, got %#v", v, gotv) 243 } 244 } 245 246 func TestCountersCombineDimension(t *testing.T) { 247 clear() 248 // Empty labels shouldn't be combined. 249 c0 := NewCountersWithSingleLabel("counter_combine_dim0", "help", "") 250 c0.Add("c1", 1) 251 assert.Equal(t, `{"c1": 1}`, c0.String()) 252 253 clear() 254 combineDimensions = "a,c" 255 256 c1 := NewCountersWithSingleLabel("counter_combine_dim1", "help", "label") 257 c1.Add("c1", 1) 258 assert.Equal(t, `{"c1": 1}`, c1.String()) 259 260 c2 := NewCountersWithSingleLabel("counter_combine_dim2", "help", "a") 261 c2.Add("c1", 1) 262 assert.Equal(t, `{"all": 1}`, c2.String()) 263 264 c3 := NewCountersWithSingleLabel("counter_combine_dim3", "help", "a") 265 assert.Equal(t, `{"all": 0}`, c3.String()) 266 267 // Anything under "a" and "c" should get reported under a consolidated "all" value 268 // instead of the specific supplied values. 269 c4 := NewCountersWithMultiLabels("counter_combine_dim4", "help", []string{"a", "b", "c"}) 270 c4.Add([]string{"c1", "c2", "c3"}, 1) 271 c4.Add([]string{"c4", "c2", "c5"}, 1) 272 assert.Equal(t, `{"all.c2.all": 2}`, c4.String()) 273 }