github.com/XiaoMi/Gaea@v1.2.5/stats/counters_test.go (about) 1 /* 2 Copyright 2017 Google Inc. 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 29 func TestCounters(t *testing.T) { 30 clear() 31 c := NewCountersWithSingleLabel("counter1", "help", "label") 32 c.Add("c1", 1) 33 c.Add("c2", 1) 34 c.Add("c2", 1) 35 want1 := `{"c1": 1, "c2": 2}` 36 want2 := `{"c2": 2, "c1": 1}` 37 if s := c.String(); s != want1 && s != want2 { 38 t.Errorf("want %s or %s, got %s", want1, want2, s) 39 } 40 counts := c.Counts() 41 if counts["c1"] != 1 { 42 t.Errorf("want 1, got %d", counts["c1"]) 43 } 44 if counts["c2"] != 2 { 45 t.Errorf("want 2, got %d", counts["c2"]) 46 } 47 } 48 49 func TestCountersTags(t *testing.T) { 50 clear() 51 c := NewCountersWithSingleLabel("counterTag1", "help", "label") 52 want := map[string]int64{} 53 got := c.Counts() 54 if !reflect.DeepEqual(got, want) { 55 t.Errorf("want %v, got %v", want, got) 56 } 57 58 c = NewCountersWithSingleLabel("counterTag2", "help", "label", "tag1", "tag2") 59 want = map[string]int64{"tag1": 0, "tag2": 0} 60 got = c.Counts() 61 if !reflect.DeepEqual(got, want) { 62 t.Errorf("want %v, got %v", want, got) 63 } 64 } 65 66 func TestMultiCounters(t *testing.T) { 67 clear() 68 c := NewCountersWithMultiLabels("mapCounter1", "help", []string{"aaa", "bbb"}) 69 c.Add([]string{"c1a", "c1b"}, 1) 70 c.Add([]string{"c2a", "c2b"}, 1) 71 c.Add([]string{"c2a", "c2b"}, 1) 72 want1 := `{"c1a.c1b": 1, "c2a.c2b": 2}` 73 want2 := `{"c2a.c2b": 2, "c1a.c1b": 1}` 74 if s := c.String(); s != want1 && s != want2 { 75 t.Errorf("want %s or %s, got %s", want1, want2, s) 76 } 77 counts := c.Counts() 78 if counts["c1a.c1b"] != 1 { 79 t.Errorf("want 1, got %d", counts["c1a.c1b"]) 80 } 81 if counts["c2a.c2b"] != 2 { 82 t.Errorf("want 2, got %d", counts["c2a.c2b"]) 83 } 84 f := NewCountersFuncWithMultiLabels("", "help", []string{"aaa", "bbb"}, func() map[string]int64 { 85 return map[string]int64{ 86 "c1a.c1b": 1, 87 "c2a.c2b": 2, 88 } 89 }) 90 if s := f.String(); s != want1 && s != want2 { 91 t.Errorf("want %s or %s, got %s", want1, want2, s) 92 } 93 } 94 95 func TestMultiCountersDot(t *testing.T) { 96 clear() 97 c := NewCountersWithMultiLabels("mapCounter2", "help", []string{"aaa", "bbb"}) 98 c.Add([]string{"c1.a", "c1b"}, 1) 99 c.Add([]string{"c2a", "c2.b"}, 1) 100 c.Add([]string{"c2a", "c2.b"}, 1) 101 c1a := safeLabel("c1.a") 102 c1aJSON := strings.Replace(c1a, "\\", "\\\\", -1) 103 c2b := safeLabel("c2.b") 104 c2bJSON := strings.Replace(c2b, "\\", "\\\\", -1) 105 want1 := `{"` + c1aJSON + `.c1b": 1, "c2a.` + c2bJSON + `": 2}` 106 want2 := `{"c2a.` + c2bJSON + `": 2, "` + c1aJSON + `.c1b": 1}` 107 if s := c.String(); s != want1 && s != want2 { 108 t.Errorf("want %s or %s, got %s", want1, want2, s) 109 } 110 counts := c.Counts() 111 if counts[c1a+".c1b"] != 1 { 112 t.Errorf("want 1, got %d", counts[c1a+".c1b"]) 113 } 114 if counts["c2a."+c2b] != 2 { 115 t.Errorf("want 2, got %d", counts["c2a."+c2b]) 116 } 117 } 118 119 func TestCountersHook(t *testing.T) { 120 var gotname string 121 var gotv *CountersWithSingleLabel 122 clear() 123 Register(func(name string, v expvar.Var) { 124 gotname = name 125 gotv = v.(*CountersWithSingleLabel) 126 }) 127 128 v := NewCountersWithSingleLabel("counter2", "help", "label") 129 if gotname != "counter2" { 130 t.Errorf("want counter2, got %s", gotname) 131 } 132 if gotv != v { 133 t.Errorf("want %#v, got %#v", v, gotv) 134 } 135 } 136 137 var benchCounter = NewCountersWithSingleLabel("bench", "help", "label") 138 139 func BenchmarkCounters(b *testing.B) { 140 clear() 141 benchCounter.Add("c1", 1) 142 b.ResetTimer() 143 144 b.RunParallel(func(pb *testing.PB) { 145 for pb.Next() { 146 benchCounter.Add("c1", 1) 147 } 148 }) 149 } 150 151 var benchMultiCounter = NewCountersWithMultiLabels("benchMulti", "help", []string{"call", "keyspace", "dbtype"}) 152 153 func BenchmarkMultiCounters(b *testing.B) { 154 clear() 155 key := []string{"execute-key-ranges", "keyspacename", "replica"} 156 benchMultiCounter.Add(key, 1) 157 b.ResetTimer() 158 159 b.RunParallel(func(pb *testing.PB) { 160 for pb.Next() { 161 benchMultiCounter.Add(key, 1) 162 } 163 }) 164 } 165 166 func BenchmarkCountersTailLatency(b *testing.B) { 167 // For this one, ignore the time reported by 'go test'. 168 // The 99th Percentile log line is all that matters. 169 // (Cmd: go test -bench=BenchmarkCountersTailLatency -benchtime=30s -cpu=10) 170 clear() 171 benchCounter.Add("c1", 1) 172 c := make(chan time.Duration, 100) 173 done := make(chan struct{}) 174 go func() { 175 all := make([]int, b.N) 176 i := 0 177 for dur := range c { 178 all[i] = int(dur) 179 i++ 180 } 181 sort.Ints(all) 182 p99 := time.Duration(all[b.N*99/100]) 183 b.Logf("99th Percentile (for N=%v): %v", b.N, p99) 184 close(done) 185 }() 186 187 b.ResetTimer() 188 b.SetParallelism(100) // The actual number of goroutines is 100*GOMAXPROCS 189 b.RunParallel(func(pb *testing.PB) { 190 r := rand.New(rand.NewSource(time.Now().UnixNano())) 191 192 var start time.Time 193 194 for pb.Next() { 195 // sleep between 0~200ms to simulate 10 QPS per goroutine. 196 time.Sleep(time.Duration(r.Int63n(200)) * time.Millisecond) 197 start = time.Now() 198 benchCounter.Add("c1", 1) 199 c <- time.Since(start) 200 } 201 }) 202 b.StopTimer() 203 204 close(c) 205 <-done 206 } 207 208 func TestCountersFuncWithMultiLabels(t *testing.T) { 209 clear() 210 f := NewCountersFuncWithMultiLabels("TestCountersFuncWithMultiLabels", "help", []string{"label1"}, func() map[string]int64 { 211 return map[string]int64{ 212 "c1": 1, 213 "c2": 2, 214 } 215 }) 216 217 want1 := `{"c1": 1, "c2": 2}` 218 want2 := `{"c2": 2, "c1": 1}` 219 if s := f.String(); s != want1 && s != want2 { 220 t.Errorf("want %s or %s, got %s", want1, want2, s) 221 } 222 } 223 224 func TestCountersFuncWithMultiLabels_Hook(t *testing.T) { 225 var gotname string 226 var gotv *CountersFuncWithMultiLabels 227 clear() 228 Register(func(name string, v expvar.Var) { 229 gotname = name 230 gotv = v.(*CountersFuncWithMultiLabels) 231 }) 232 233 v := NewCountersFuncWithMultiLabels("TestCountersFuncWithMultiLabels_Hook", "help", []string{"label1"}, func() map[string]int64 { 234 return map[string]int64{} 235 }) 236 if gotname != "TestCountersFuncWithMultiLabels_Hook" { 237 t.Errorf("want TestCountersFuncWithMultiLabels_Hook, got %s", gotname) 238 } 239 if gotv != v { 240 t.Errorf("want %#v, got %#v", v, gotv) 241 } 242 }