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  }