go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/internal/tracesampler_test.go (about)

     1  // Copyright 2019 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package internal
    16  
    17  import (
    18  	"math/rand"
    19  	"sync/atomic"
    20  	"testing"
    21  	"time"
    22  
    23  	"go.opentelemetry.io/otel/sdk/trace"
    24  
    25  	. "github.com/smartystreets/goconvey/convey"
    26  	. "go.chromium.org/luci/common/testing/assertions"
    27  )
    28  
    29  func TestSamplerParsing(t *testing.T) {
    30  	t.Parallel()
    31  
    32  	Convey("Percent", t, func() {
    33  		s, err := BaseSampler("0.1%")
    34  		So(err, ShouldBeNil)
    35  		So(s, ShouldNotBeNil)
    36  
    37  		_, err = BaseSampler("abc%")
    38  		So(err, ShouldErrLike, `not a float percent "abc%"`)
    39  	})
    40  
    41  	Convey("QPS", t, func() {
    42  		s, err := BaseSampler("0.1qps")
    43  		So(err, ShouldBeNil)
    44  		So(s, ShouldNotBeNil)
    45  
    46  		_, err = BaseSampler("abcqps")
    47  		So(err, ShouldErrLike, `not a float QPS "abcqps"`)
    48  	})
    49  
    50  	Convey("Unrecognized", t, func() {
    51  		_, err := BaseSampler("huh")
    52  		So(err, ShouldErrLike, "unrecognized sampling spec string")
    53  	})
    54  }
    55  
    56  func TestQPSSampler(t *testing.T) {
    57  	t.Parallel()
    58  
    59  	Convey("Works", t, func() {
    60  		now := atomic.Value{}
    61  		now.Store(time.Now()) // the absolute value doesn't matter
    62  		tick := func(dt time.Duration) { now.Store(now.Load().(time.Time).Add(dt)) }
    63  
    64  		sampler := qpsSampler{
    65  			period: time.Second, // sample one request per second
    66  			now:    func() time.Time { return now.Load().(time.Time) },
    67  			rnd:    rand.New(rand.NewSource(0)),
    68  		}
    69  
    70  		sampled := 0
    71  		for i := 0; i < 10000; i++ {
    72  			// Note: TraceID is not used in the current implementation, but we supply
    73  			// it nonetheless to make the test also work with other implementations.
    74  			params := trace.SamplingParameters{}
    75  			if _, err := rand.Read(params.TraceID[:]); err != nil {
    76  				panic(err)
    77  			}
    78  			if sampler.ShouldSample(params).Decision == trace.RecordAndSample {
    79  				sampled++
    80  			}
    81  			tick(10 * time.Millisecond) // 100 QPS
    82  		}
    83  		So(sampled, ShouldEqual, 10000/100+1) // '+1' is due to randomization
    84  	})
    85  }