-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
item.go
105 lines (89 loc) · 2.6 KB
/
item.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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package hot
import (
"math"
"math/rand/v2"
"time"
"github.com/DmitriyVTitov/size"
"github.com/samber/hot/internal"
)
func newItem[V any](v V, hasValue bool, ttlMicro int64, staleMicro int64) *item[V] {
if hasValue {
return newItemWithValue(v, ttlMicro, staleMicro)
}
return newItemNoValue[V](ttlMicro, staleMicro)
}
func newItemWithValue[V any](v V, ttlMicro int64, staleMicro int64) *item[V] {
var expiryMicro int64
var staleExpiryMicro int64
if ttlMicro != 0 {
// @TODO: current time should be passed as an argument to make it faster in batch operations
expiryMicro = int64(internal.NowMicro()) + ttlMicro
staleExpiryMicro = expiryMicro + staleMicro
}
return &item[V]{
hasValue: true,
value: v,
bytes: uint(size.Of(v)),
expiryMicro: expiryMicro,
staleExpiryMicro: staleExpiryMicro,
}
}
func newItemNoValue[V any](ttlMicro int64, staleMicro int64) *item[V] {
var expiryMicro int64
var staleExpiryMicro int64
if ttlMicro != 0 {
// @TODO: current time should be passed as an argument to make it faster in batch operations
expiryMicro = int64(internal.NowMicro()) + ttlMicro
staleExpiryMicro = expiryMicro + staleMicro
}
return &item[V]{
hasValue: false,
expiryMicro: expiryMicro,
staleExpiryMicro: staleExpiryMicro,
}
}
type item[V any] struct {
hasValue bool
value V
bytes uint
// Better store int64 microseconds instead of time.Time (benchmark resulted in 10x speedup).
expiryMicro int64
staleExpiryMicro int64
}
func (i *item[V]) isExpired(nowMicro int64) bool {
return i.expiryMicro > 0 && nowMicro > i.staleExpiryMicro
}
func (i *item[V]) shouldRevalidate(nowMicro int64) bool {
return i.expiryMicro > 0 && nowMicro > i.expiryMicro && nowMicro < i.staleExpiryMicro
}
func zero[V any]() V {
var v V
return v
}
func itemMapsToValues[K comparable, V any](copyOnRead func(V) V, maps ...map[K]*item[V]) (found map[K]V, missing []K) {
found = map[K]V{}
missing = []K{}
for _, m := range maps {
for k, v := range m {
if v.hasValue {
if copyOnRead != nil {
found[k] = copyOnRead(v.value)
} else {
found[k] = v.value
}
} else if _, ok := found[k]; !ok {
// do not append to missing slice if already present in `found`
missing = append(missing, k)
}
}
}
return
}
func applyJitter(ttlMicro int64, jitterLambda float64, jitterUpperBound time.Duration) int64 {
if jitterLambda == 0 || jitterUpperBound == 0 {
return ttlMicro
}
u := float64(jitterUpperBound.Microseconds()) * rand.Float64()
variation := 1 - math.Exp(-jitterLambda*u)
return int64(float64(ttlMicro) * variation)
}