-
Notifications
You must be signed in to change notification settings - Fork 2
/
localized_strategy.go
87 lines (80 loc) · 2.15 KB
/
localized_strategy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package sampling
import (
crand "crypto/rand"
"encoding/binary"
"math/rand"
"sync"
"time"
)
// LocalizedStrategy makes trace sampling decisions based on
// a set of rules provided in a local JSON file. Trace sampling
// decisions are made by the root node in the trace. If a
// sampling decision is made by the root service, it will be passed
// to downstream services through the trace header.
type LocalizedStrategy struct {
manifest *Manifest
reservoirs []*reservoir
defaultReservoir *reservoir
mu sync.Mutex
randFunc func() float64
}
// NewLocalizedStrategy returns new LocalizedStrategy.
func NewLocalizedStrategy(manifest *Manifest) (*LocalizedStrategy, error) {
if manifest == nil {
manifest = DefaultSamplingRule
}
if err := manifest.Validate(); err != nil {
return nil, err
}
cp := manifest.Copy()
cp.normalize()
reservoirs := make([]*reservoir, 0, len(cp.Rules))
for _, r := range cp.Rules {
reservoirs = append(reservoirs, &reservoir{
capacity: r.FixedTarget,
})
}
defaultReservoir := &reservoir{
capacity: cp.Default.FixedTarget,
}
return &LocalizedStrategy{
manifest: cp,
reservoirs: reservoirs,
defaultReservoir: defaultReservoir,
}, nil
}
// ShouldTrace implements Strategy.
func (s *LocalizedStrategy) ShouldTrace(req *Request) *Decision {
for i, r := range s.manifest.Rules {
if r.Match(req) {
return s.sampling(s.reservoirs[i], r.Rate)
}
}
return s.sampling(s.defaultReservoir, s.manifest.Default.Rate)
}
func (s *LocalizedStrategy) sampling(r *reservoir, rate float64) *Decision {
if r.Take() {
return &Decision{
Sample: true,
}
}
s.mu.Lock()
defer s.mu.Unlock()
return &Decision{
Sample: s.randLocked() < rate,
}
}
// returns a pseudo-random number in [0.0,1.0). s.mu should be locked.
func (s *LocalizedStrategy) randLocked() float64 {
if s.randFunc != nil {
return s.randFunc()
}
// lazy initialize of random generator
var seed int64
if err := binary.Read(crand.Reader, binary.BigEndian, &seed); err != nil {
// fallback to timestamp
seed = time.Now().UnixNano()
}
s.randFunc = rand.New(rand.NewSource(seed)).Float64
return s.randFunc()
}