From 83f98613e9934792c42c3d1c2694688d5bc9dccb Mon Sep 17 00:00:00 2001 From: brawndou <112038567+brawndou@users.noreply.github.com> Date: Thu, 25 Jan 2024 11:35:49 -0800 Subject: [PATCH] Backport #221 and #222 to master branch (#243) * [bug] Ensure that parent scopes do not return closed subscopes (#221) * TestScope: don't prune from registry when closed (#222) * TestScope: don't prune from registry when closed (#222) * lint fix * move comment * update go.mod and go.sum * add license header --------- Co-authored-by: Matt Way --- generate.go | 26 ++++++ go.mod | 1 + go.sum | 21 ++++- scope.go | 12 ++- scope_registry.go | 60 ++++++++++--- scope_registry_external_test.go | 144 ++++++++++++++++++++++++++++++++ scope_registry_test.go | 2 +- tallymock/stats_reporter.go | 122 +++++++++++++++++++++++++++ 8 files changed, 371 insertions(+), 17 deletions(-) create mode 100644 generate.go create mode 100644 scope_registry_external_test.go create mode 100644 tallymock/stats_reporter.go diff --git a/generate.go b/generate.go new file mode 100644 index 00000000..30192d7c --- /dev/null +++ b/generate.go @@ -0,0 +1,26 @@ +// Copyright (c) 2024 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tally + +import ( + //go:generate mockgen -package tallymock -destination tallymock/stats_reporter.go -imports github.com/uber-go/tally github.com/uber-go/tally StatsReporter + _ "github.com/golang/mock/mockgen/model" +) diff --git a/go.mod b/go.mod index 2888b55b..81abcb44 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.15 require ( github.com/cactus/go-statsd-client/v5 v5.0.0 + github.com/golang/mock v1.6.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.11.0 github.com/prometheus/client_model v0.2.0 diff --git a/go.sum b/go.sum index 3bb9f6a5..c3ff3d90 100644 --- a/go.sum +++ b/go.sum @@ -23,6 +23,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -102,24 +104,31 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/twmb/murmur3 v1.1.5 h1:i9OLS9fkuLzBXjt6dptlAEyk58fJsSTXbRg3SgVyqgk= github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -129,14 +138,24 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/scope.go b/scope.go index 9a6ee673..733cd4ea 100644 --- a/scope.go +++ b/scope.go @@ -92,6 +92,7 @@ type scope struct { done chan struct{} wg sync.WaitGroup root bool + testScope bool } // ScopeOptions is a set of options to construct a scope. @@ -103,9 +104,11 @@ type ScopeOptions struct { Separator string DefaultBuckets Buckets SanitizeOptions *SanitizeOptions - registryShardCount uint OmitCardinalityMetrics bool CardinalityMetricsTags map[string]string + + testScope bool + registryShardCount uint } // NewRootScope creates a new root Scope with a set of options and @@ -129,7 +132,11 @@ func NewTestScope( prefix string, tags map[string]string, ) TestScope { - return newRootScope(ScopeOptions{Prefix: prefix, Tags: tags}, 0) + return newRootScope(ScopeOptions{ + Prefix: prefix, + Tags: tags, + testScope: true, + }, 0) } func newRootScope(opts ScopeOptions, interval time.Duration) *scope { @@ -174,6 +181,7 @@ func newRootScope(opts ScopeOptions, interval time.Duration) *scope { separator: sanitizer.Name(opts.Separator), timers: make(map[string]*timer), root: true, + testScope: opts.testScope, } // NB(r): Take a copy of the tags on creation diff --git a/scope_registry.go b/scope_registry.go index ffa341da..d1481dac 100644 --- a/scope_registry.go +++ b/scope_registry.go @@ -181,23 +181,56 @@ func (r *scopeRegistry) Subscope(parent *scope, prefix string, tags map[string]s // as the memory layout of []byte is a superset of string the below casting is safe and does not do any alloc // However it cannot be used outside of the stack; a heap allocation is needed if that string needs to be stored // in the map as a key - if s, ok := r.lockedLookup(subscopeBucket, *(*string)(unsafe.Pointer(&buf))); ok { - subscopeBucket.mu.RUnlock() - return s + var ( + unsanitizedKey = *(*string)(unsafe.Pointer(&buf)) + sanitizedKey string + ) + + s, ok := r.lockedLookup(subscopeBucket, unsanitizedKey) + if ok { + // If this subscope isn't closed or is a test scope, return it. + // Otherwise, report it immediately and delete it so that a new + // (functional) scope can be returned instead. + if !s.closed.Load() || s.testScope { + subscopeBucket.mu.RUnlock() + return s + } + + switch { + case parent.reporter != nil: + s.report(parent.reporter) + case parent.cachedReporter != nil: + s.cachedReport() + } } - subscopeBucket.mu.RUnlock() - // heap allocating the buf as a string to keep the key in the subscopes map - preSanitizeKey := string(buf) tags = parent.copyAndSanitizeMap(tags) - key := scopeRegistryKey(prefix, parent.tags, tags) + sanitizedKey = scopeRegistryKey(prefix, parent.tags, tags) + + // If a scope was found above but we didn't return, we need to remove the + // scope from both keys. + if ok { + r.removeWithRLock(subscopeBucket, unsanitizedKey) + r.removeWithRLock(subscopeBucket, sanitizedKey) + s.clearMetrics() + } + + subscopeBucket.mu.RUnlock() + + // Force-allocate the unsafe string as a safe string. Note that neither + // string(x) nor x+"" will have the desired effect (the former is a nop, + // and the latter will likely be elided), so append a new character and + // truncate instead. + // + // ref: https://go.dev/play/p/sxhExUKSxCw + unsanitizedKey = (unsanitizedKey + ".")[:len(unsanitizedKey)] subscopeBucket.mu.Lock() defer subscopeBucket.mu.Unlock() - if s, ok := r.lockedLookup(subscopeBucket, key); ok { - if _, ok = r.lockedLookup(subscopeBucket, preSanitizeKey); !ok { - subscopeBucket.s[preSanitizeKey] = s + if s, ok := r.lockedLookup(subscopeBucket, sanitizedKey); ok { + if _, ok = r.lockedLookup(subscopeBucket, unsanitizedKey); !ok { + subscopeBucket.s[unsanitizedKey] = s } return s } @@ -225,10 +258,11 @@ func (r *scopeRegistry) Subscope(parent *scope, prefix string, tags map[string]s timers: make(map[string]*timer), bucketCache: parent.bucketCache, done: make(chan struct{}), + testScope: parent.testScope, } - subscopeBucket.s[key] = subscope - if _, ok := r.lockedLookup(subscopeBucket, preSanitizeKey); !ok { - subscopeBucket.s[preSanitizeKey] = subscope + subscopeBucket.s[sanitizedKey] = subscope + if _, ok := r.lockedLookup(subscopeBucket, unsanitizedKey); !ok { + subscopeBucket.s[unsanitizedKey] = subscope } return subscope } diff --git a/scope_registry_external_test.go b/scope_registry_external_test.go new file mode 100644 index 00000000..dca52d10 --- /dev/null +++ b/scope_registry_external_test.go @@ -0,0 +1,144 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tally_test + +import ( + "io" + "sync" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + "github.com/uber-go/tally/v4" + "github.com/uber-go/tally/v4/tallymock" + "go.uber.org/atomic" +) + +func TestTestScopesNotPruned(t *testing.T) { + var ( + root = tally.NewTestScope("", nil) + subscope = root.SubScope("foo") + counter = subscope.Counter("bar") + ) + + counter.Inc(123) + + closer, ok := subscope.(io.Closer) + require.True(t, ok) + require.NoError(t, closer.Close()) + + subscope = root.SubScope("foo") + counter = subscope.Counter("bar") + counter.Inc(123) + + var ( + snapshot = root.Snapshot() + counters = snapshot.Counters() + ) + require.Len(t, counters, 1) + require.Len(t, snapshot.Gauges(), 0) + require.Len(t, snapshot.Timers(), 0) + require.Len(t, snapshot.Histograms(), 0) + + val, ok := counters["foo.bar+"] + require.True(t, ok) + require.Equal(t, "foo.bar", val.Name()) + require.EqualValues(t, 246, val.Value()) +} + +func TestNoDefunctSubscopes(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + var ( + tags = map[string]string{ + "hello": "world", + } + mockreporter = tallymock.NewMockStatsReporter(ctrl) + ready = make(chan struct{}) + closed atomic.Bool + wg sync.WaitGroup + ) + wg.Add(2) + + mockreporter.EXPECT(). + ReportCounter("a", gomock.Any(), int64(123)). + Do(func(_ string, _ map[string]string, _ int64) { + wg.Done() + }). + Times(1) + mockreporter.EXPECT(). + ReportCounter("b", gomock.Any(), int64(456)). + Do(func(_ string, _ map[string]string, _ int64) { + wg.Done() + }). + Times(1) + + // Use flushing as a signal to determine if/when a closed scope + // would be removed from the registry's cache. + mockreporter.EXPECT(). + Flush(). + Do(func() { + // Don't unblock the ready channel until we've explicitly + // closed the scope. + if !closed.Load() { + return + } + + select { + case <-ready: + default: + close(ready) + } + }). + MinTimes(1) + + root, _ := tally.NewRootScope(tally.ScopeOptions{ + Reporter: mockreporter, + OmitCardinalityMetrics: true, + }, time.Millisecond) + + subscope := root.Tagged(tags) + requireClose(t, subscope) + subscope = root.Tagged(tags) + + // Signal and wait for the next flush to ensure that subscope can + // be a closed scope. + closed.Store(true) + <-ready + + // Use the maybe-closed subscope for counter A. + subscope.Counter("a").Inc(123) + + // Guarantee that counter B will not use a closed subscope. + subscope = root.Tagged(tags) + subscope.Counter("b").Inc(456) + + requireClose(t, root) + wg.Wait() +} + +func requireClose(t *testing.T, scope tally.Scope) { + x, ok := scope.(io.Closer) + require.True(t, ok) + require.NoError(t, x.Close()) +} diff --git a/scope_registry_test.go b/scope_registry_test.go index 0a36d0c1..5339fc23 100644 --- a/scope_registry_test.go +++ b/scope_registry_test.go @@ -49,7 +49,7 @@ func TestVerifyCachedTaggedScopesAlloc(t *testing.T) { "qux": "quux", }) }) - expected := 2.0 + expected := 3.0 assert.True(t, allocs <= expected, "the cached tagged scopes should allocate at most %.0f allocations, but did allocate %.0f", expected, allocs) } diff --git a/tallymock/stats_reporter.go b/tallymock/stats_reporter.go new file mode 100644 index 00000000..8e238e71 --- /dev/null +++ b/tallymock/stats_reporter.go @@ -0,0 +1,122 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/uber-go/tally (interfaces: StatsReporter) + +// Package tallymock is a generated GoMock package. +package tallymock + +import ( + reflect "reflect" + time "time" + + gomock "github.com/golang/mock/gomock" + tally "github.com/uber-go/tally/v4" +) + +// MockStatsReporter is a mock of StatsReporter interface. +type MockStatsReporter struct { + ctrl *gomock.Controller + recorder *MockStatsReporterMockRecorder +} + +// MockStatsReporterMockRecorder is the mock recorder for MockStatsReporter. +type MockStatsReporterMockRecorder struct { + mock *MockStatsReporter +} + +// NewMockStatsReporter creates a new mock instance. +func NewMockStatsReporter(ctrl *gomock.Controller) *MockStatsReporter { + mock := &MockStatsReporter{ctrl: ctrl} + mock.recorder = &MockStatsReporterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockStatsReporter) EXPECT() *MockStatsReporterMockRecorder { + return m.recorder +} + +// Capabilities mocks base method. +func (m *MockStatsReporter) Capabilities() tally.Capabilities { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Capabilities") + ret0, _ := ret[0].(tally.Capabilities) + return ret0 +} + +// Capabilities indicates an expected call of Capabilities. +func (mr *MockStatsReporterMockRecorder) Capabilities() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Capabilities", reflect.TypeOf((*MockStatsReporter)(nil).Capabilities)) +} + +// Flush mocks base method. +func (m *MockStatsReporter) Flush() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Flush") +} + +// Flush indicates an expected call of Flush. +func (mr *MockStatsReporterMockRecorder) Flush() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Flush", reflect.TypeOf((*MockStatsReporter)(nil).Flush)) +} + +// ReportCounter mocks base method. +func (m *MockStatsReporter) ReportCounter(arg0 string, arg1 map[string]string, arg2 int64) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReportCounter", arg0, arg1, arg2) +} + +// ReportCounter indicates an expected call of ReportCounter. +func (mr *MockStatsReporterMockRecorder) ReportCounter(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportCounter", reflect.TypeOf((*MockStatsReporter)(nil).ReportCounter), arg0, arg1, arg2) +} + +// ReportGauge mocks base method. +func (m *MockStatsReporter) ReportGauge(arg0 string, arg1 map[string]string, arg2 float64) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReportGauge", arg0, arg1, arg2) +} + +// ReportGauge indicates an expected call of ReportGauge. +func (mr *MockStatsReporterMockRecorder) ReportGauge(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportGauge", reflect.TypeOf((*MockStatsReporter)(nil).ReportGauge), arg0, arg1, arg2) +} + +// ReportHistogramDurationSamples mocks base method. +func (m *MockStatsReporter) ReportHistogramDurationSamples(arg0 string, arg1 map[string]string, arg2 tally.Buckets, arg3, arg4 time.Duration, arg5 int64) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReportHistogramDurationSamples", arg0, arg1, arg2, arg3, arg4, arg5) +} + +// ReportHistogramDurationSamples indicates an expected call of ReportHistogramDurationSamples. +func (mr *MockStatsReporterMockRecorder) ReportHistogramDurationSamples(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportHistogramDurationSamples", reflect.TypeOf((*MockStatsReporter)(nil).ReportHistogramDurationSamples), arg0, arg1, arg2, arg3, arg4, arg5) +} + +// ReportHistogramValueSamples mocks base method. +func (m *MockStatsReporter) ReportHistogramValueSamples(arg0 string, arg1 map[string]string, arg2 tally.Buckets, arg3, arg4 float64, arg5 int64) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReportHistogramValueSamples", arg0, arg1, arg2, arg3, arg4, arg5) +} + +// ReportHistogramValueSamples indicates an expected call of ReportHistogramValueSamples. +func (mr *MockStatsReporterMockRecorder) ReportHistogramValueSamples(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportHistogramValueSamples", reflect.TypeOf((*MockStatsReporter)(nil).ReportHistogramValueSamples), arg0, arg1, arg2, arg3, arg4, arg5) +} + +// ReportTimer mocks base method. +func (m *MockStatsReporter) ReportTimer(arg0 string, arg1 map[string]string, arg2 time.Duration) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReportTimer", arg0, arg1, arg2) +} + +// ReportTimer indicates an expected call of ReportTimer. +func (mr *MockStatsReporterMockRecorder) ReportTimer(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportTimer", reflect.TypeOf((*MockStatsReporter)(nil).ReportTimer), arg0, arg1, arg2) +}