github.com/uber-go/tally/v4@v4.1.16/scope_registry_test.go (about)

     1  // Copyright (c) 2024 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package tally
    22  
    23  import (
    24  	"fmt"
    25  	"testing"
    26  
    27  	"github.com/stretchr/testify/assert"
    28  )
    29  
    30  var (
    31  	numInternalMetrics = 4
    32  )
    33  
    34  func TestVerifyCachedTaggedScopesAlloc(t *testing.T) {
    35  	root, _ := NewRootScope(ScopeOptions{
    36  		Prefix:   "funkytown",
    37  		Reporter: NullStatsReporter,
    38  		Tags: map[string]string{
    39  			"style":     "funky",
    40  			"hair":      "wavy",
    41  			"jefferson": "starship",
    42  		},
    43  	}, 0)
    44  
    45  	allocs := testing.AllocsPerRun(1000, func() {
    46  		root.Tagged(map[string]string{
    47  			"foo": "bar",
    48  			"baz": "qux",
    49  			"qux": "quux",
    50  		})
    51  	})
    52  	expected := 3.0
    53  	assert.True(t, allocs <= expected, "the cached tagged scopes should allocate at most %.0f allocations, but did allocate %.0f", expected, allocs)
    54  }
    55  
    56  func TestVerifyOmitCardinalityMetricsTags(t *testing.T) {
    57  	r := newTestStatsReporter()
    58  	_, closer := NewRootScope(ScopeOptions{
    59  		Reporter:               r,
    60  		OmitCardinalityMetrics: false,
    61  		CardinalityMetricsTags: map[string]string{
    62  			"cardinality_tag_key": "cardinality_tag_value",
    63  		},
    64  	}, 0)
    65  	wantOmitCardinalityMetricsTags := map[string]string{
    66  		"cardinality_tag_key": "cardinality_tag_value",
    67  		"version":             Version,
    68  		"host":                "global",
    69  		"instance":            "global",
    70  	}
    71  
    72  	r.gg.Add(numInternalMetrics)
    73  	closer.Close()
    74  	r.WaitAll()
    75  
    76  	assert.NotNil(t, r.gauges[counterCardinalityName], "counter cardinality should not be nil")
    77  	assert.Equal(
    78  		t, wantOmitCardinalityMetricsTags, r.gauges[counterCardinalityName].tags, "expected tags %v, got tags %v",
    79  		wantOmitCardinalityMetricsTags, r.gauges[counterCardinalityName].tags,
    80  	)
    81  }
    82  
    83  func TestNewTestStatsReporterOneScope(t *testing.T) {
    84  	r := newTestStatsReporter()
    85  	root, closer := NewRootScope(ScopeOptions{Reporter: r, OmitCardinalityMetrics: false}, 0)
    86  	s := root.(*scope)
    87  
    88  	numFakeCounters := 3
    89  	numFakeGauges := 5
    90  	numFakeHistograms := 11
    91  	numScopes := 1
    92  
    93  	r.cg.Add(numFakeCounters)
    94  	for c := 1; c <= numFakeCounters; c++ {
    95  		s.Counter(fmt.Sprintf("counter-%d", c)).Inc(int64(c))
    96  	}
    97  
    98  	r.gg.Add(numFakeGauges + numInternalMetrics)
    99  	for g := 1; g <= numFakeGauges; g++ {
   100  		s.Gauge(fmt.Sprintf("gauge_%d", g)).Update(float64(g))
   101  	}
   102  
   103  	r.hg.Add(numFakeHistograms)
   104  	for h := 1; h <= numFakeHistograms; h++ {
   105  		s.Histogram(fmt.Sprintf("histogram_%d", h), MustMakeLinearValueBuckets(0, 1, 10)).RecordValue(float64(h))
   106  	}
   107  
   108  	closer.Close()
   109  	r.WaitAll()
   110  
   111  	assert.NotNil(t, r.gauges[counterCardinalityName], "counter cardinality should not be nil")
   112  	assert.Equal(
   113  		t, numFakeCounters, int(r.gauges[counterCardinalityName].val), "expected %d counters, got %d counters",
   114  		numFakeCounters, r.gauges[counterCardinalityName].val,
   115  	)
   116  
   117  	assert.NotNil(t, r.gauges[gaugeCardinalityName], "gauge cardinality should not be nil")
   118  	assert.Equal(
   119  		t, numFakeGauges, int(r.gauges[gaugeCardinalityName].val), "expected %d gauges, got %d gauges",
   120  		numFakeGauges, r.gauges[gaugeCardinalityName].val,
   121  	)
   122  
   123  	assert.NotNil(t, r.gauges[histogramCardinalityName], "histogram cardinality should not be nil")
   124  	assert.Equal(
   125  		t, numFakeHistograms, int(r.gauges[histogramCardinalityName].val),
   126  		"expected %d histograms, got %d histograms", numFakeHistograms, r.gauges[histogramCardinalityName].val,
   127  	)
   128  
   129  	assert.NotNil(t, r.gauges[scopeCardinalityName], "scope cardinality should not be nil")
   130  	assert.Equal(
   131  		t, numScopes, int(r.gauges[scopeCardinalityName].val), "expected %d scopes, got %d scopes",
   132  		numScopes, r.gauges[scopeCardinalityName].val,
   133  	)
   134  }
   135  
   136  func TestNewTestStatsReporterManyScopes(t *testing.T) {
   137  	r := newTestStatsReporter()
   138  	root, closer := NewRootScope(ScopeOptions{Reporter: r, OmitCardinalityMetrics: false}, 0)
   139  	wantCounters, wantGauges, wantHistograms, wantScopes := 3, 2, 1, 2
   140  
   141  	s := root.(*scope)
   142  	r.cg.Add(2)
   143  	s.Counter("counter-foo").Inc(1)
   144  	s.Counter("counter-bar").Inc(2)
   145  	r.gg.Add(1 + numInternalMetrics)
   146  	s.Gauge("gauge-foo").Update(3)
   147  	r.hg.Add(1)
   148  	s.Histogram("histogram-foo", MustMakeLinearValueBuckets(0, 1, 10)).RecordValue(4)
   149  
   150  	ss := root.SubScope("sub-scope").(*scope)
   151  	r.cg.Add(1)
   152  	ss.Counter("counter-baz").Inc(5)
   153  	r.gg.Add(1)
   154  	ss.Gauge("gauge-bar").Update(6)
   155  
   156  	closer.Close()
   157  	r.WaitAll()
   158  
   159  	assert.NotNil(t, r.gauges[counterCardinalityName], "counter cardinality should not be nil")
   160  	assert.Equal(
   161  		t, wantCounters, int(r.gauges[counterCardinalityName].val), "expected %d counters, got %d counters", wantCounters,
   162  		r.gauges[counterCardinalityName].val,
   163  	)
   164  
   165  	assert.NotNil(t, r.gauges[gaugeCardinalityName], "gauge cardinality should not be nil")
   166  	assert.Equal(
   167  		t, wantGauges, int(r.gauges[gaugeCardinalityName].val), "expected %d gauges, got %d gauges", wantGauges,
   168  		r.gauges[gaugeCardinalityName].val,
   169  	)
   170  
   171  	assert.NotNil(t, r.gauges[histogramCardinalityName], "histogram cardinality should not be nil")
   172  	assert.Equal(
   173  		t, wantHistograms, int(r.gauges[histogramCardinalityName].val), "expected %d histograms, got %d histograms",
   174  		wantHistograms, r.gauges[histogramCardinalityName].val,
   175  	)
   176  
   177  	assert.NotNil(t, r.gauges[scopeCardinalityName], "scope cardinality should not be nil")
   178  	assert.Equal(
   179  		t, wantScopes, int(r.gauges[scopeCardinalityName].val), "expected %d scopes, got %d scopes",
   180  		wantScopes, r.gauges[scopeCardinalityName].val,
   181  	)
   182  }
   183  
   184  func TestForEachScopeConcurrent(t *testing.T) {
   185  	var (
   186  		root = newRootScope(ScopeOptions{Prefix: "", Tags: nil}, 0)
   187  		quit = make(chan struct{})
   188  		done = make(chan struct{})
   189  	)
   190  
   191  	go func() {
   192  		defer close(done)
   193  		for {
   194  			select {
   195  			case <-quit:
   196  				return
   197  			default:
   198  				hello := root.Tagged(map[string]string{"a": "b"}).Counter("hello")
   199  				hello.Inc(1)
   200  			}
   201  		}
   202  	}()
   203  
   204  	var c Counter = nil
   205  	for {
   206  		// Keep poking at the subscopes until the counter is written.
   207  		root.registry.ForEachScope(
   208  			func(ss *scope) {
   209  				ss.cm.RLock()
   210  				if ss.counters["hello"] != nil {
   211  					c = ss.counters["hello"]
   212  				}
   213  				ss.cm.RUnlock()
   214  			},
   215  		)
   216  		if c != nil {
   217  			quit <- struct{}{}
   218  			break
   219  		}
   220  	}
   221  
   222  	<-done
   223  }
   224  
   225  func TestCachedReporterInternalMetricsAlloc(t *testing.T) {
   226  	tests := []struct {
   227  		name                   string
   228  		omitCardinalityMetrics bool
   229  		wantGauges             int
   230  	}{
   231  		{
   232  			name:                   "omit metrics",
   233  			omitCardinalityMetrics: true,
   234  			wantGauges:             1,
   235  		},
   236  		{
   237  			name:                   "include metrics",
   238  			omitCardinalityMetrics: false,
   239  			wantGauges:             1 + numInternalMetrics,
   240  		},
   241  	}
   242  
   243  	for _, tt := range tests {
   244  		r := newTestStatsReporter()
   245  		root, closer := NewRootScope(ScopeOptions{CachedReporter: r, OmitCardinalityMetrics: tt.omitCardinalityMetrics}, 0)
   246  		s := root.(*scope)
   247  
   248  		r.gg.Add(tt.wantGauges)
   249  		s.Gauge("gauge-foo").Update(3)
   250  
   251  		closer.Close()
   252  		r.WaitAll()
   253  
   254  		assert.Equal(
   255  			t, tt.wantGauges, len(r.gauges), "%n: expected %d gauges, got %d gauges", tt.name, tt.wantGauges,
   256  			len(r.gauges),
   257  		)
   258  	}
   259  }