github.com/honeycombio/honeytail@v1.9.0/sample/deterministic_sampler.go (about)

     1  package sample
     2  
     3  import (
     4  	"crypto/sha1"
     5  	"errors"
     6  	"math"
     7  )
     8  
     9  var (
    10  	ErrInvalidSampleRate = errors.New("sample rate must be >= 1")
    11  )
    12  
    13  func init() {
    14  }
    15  
    16  // DeterministicSampler allows for distributed sampling based on a common field
    17  // such as a request or trace ID. It accepts a sample rate N and will
    18  // deterministically sample 1/N events based on the target field. Hence, two or
    19  // more programs can decide whether or not to sample related events without
    20  // communication.
    21  type DeterministicSampler struct {
    22  	sampleRate int
    23  	upperBound uint32
    24  }
    25  
    26  func NewDeterministicSampler(sampleRate uint) (*DeterministicSampler, error) {
    27  	if sampleRate < 1 {
    28  		return nil, ErrInvalidSampleRate
    29  	}
    30  
    31  	// Get the actual upper bound - the largest possible value divided by
    32  	// the sample rate. In the case where the sample rate is 1, this should
    33  	// sample every value.
    34  	upperBound := math.MaxUint32 / uint32(sampleRate)
    35  	return &DeterministicSampler{
    36  		sampleRate: int(sampleRate),
    37  		upperBound: upperBound,
    38  	}, nil
    39  }
    40  
    41  // bytesToUint32 takes a slice of 4 bytes representing a big endian 32 bit
    42  // unsigned value and returns the equivalent uint32.
    43  func bytesToUint32be(b []byte) uint32 {
    44  	return uint32(b[3]) | (uint32(b[2]) << 8) | (uint32(b[1]) << 16) | (uint32(b[0]) << 24)
    45  }
    46  
    47  func (ds *DeterministicSampler) Sample(determinant string) bool {
    48  	sum := sha1.Sum([]byte(determinant))
    49  	v := bytesToUint32be(sum[:4])
    50  	return v <= ds.upperBound
    51  }