-
Notifications
You must be signed in to change notification settings - Fork 120
/
item.go
161 lines (128 loc) · 3.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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package ttlcache
import (
"sync"
"time"
)
const (
// NoTTL indicates that an item should never expire.
NoTTL time.Duration = -1
// PreviousOrDefaultTTL indicates that existing TTL of item should be used
// default TTL will be used as fallback if item doesn't exist
PreviousOrDefaultTTL time.Duration = -2
// DefaultTTL indicates that the default TTL value of the cache
// instance should be used.
DefaultTTL time.Duration = 0
)
// Item holds all the information that is associated with a single
// cache value.
type Item[K comparable, V any] struct {
// the mutex needs to be locked only when:
// - data fields are being read inside accessor methods
// - data fields are being updated
// when data fields are being read in one of the cache's
// methods, we can be sure that these fields are not modified
// concurrently since the item list is locked by its own mutex as
// well, so locking this mutex would be redundant.
// In other words, this mutex is only useful when these fields
// are being read from the outside (e.g. in event functions).
mu sync.RWMutex
key K
value V
ttl time.Duration
expiresAt time.Time
queueIndex int
version int64
}
// NewItem creates a new cache item.
func NewItem[K comparable, V any](key K, value V, ttl time.Duration, enableVersionTracking bool) *Item[K, V] {
item := &Item[K, V]{
key: key,
value: value,
ttl: ttl,
}
if !enableVersionTracking {
item.version = -1
}
item.touch()
return item
}
// update modifies the item's value, TTL, and version.
func (item *Item[K, V]) update(value V, ttl time.Duration) {
item.mu.Lock()
defer item.mu.Unlock()
item.value = value
// update version if enabled
if item.version > -1 {
item.version++
}
// no need to update ttl or expiry in this case
if ttl == PreviousOrDefaultTTL {
return
}
item.ttl = ttl
// reset expiration timestamp because the new TTL may be
// 0 or below
item.expiresAt = time.Time{}
item.touchUnsafe()
}
// touch updates the item's expiration timestamp.
func (item *Item[K, V]) touch() {
item.mu.Lock()
defer item.mu.Unlock()
item.touchUnsafe()
}
// touchUnsafe updates the item's expiration timestamp without
// locking the mutex.
func (item *Item[K, V]) touchUnsafe() {
if item.ttl <= 0 {
return
}
item.expiresAt = time.Now().Add(item.ttl)
}
// IsExpired returns a bool value that indicates whether the item
// is expired.
func (item *Item[K, V]) IsExpired() bool {
item.mu.RLock()
defer item.mu.RUnlock()
return item.isExpiredUnsafe()
}
// isExpiredUnsafe returns a bool value that indicates whether the
// the item is expired without locking the mutex
func (item *Item[K, V]) isExpiredUnsafe() bool {
if item.ttl <= 0 {
return false
}
return item.expiresAt.Before(time.Now())
}
// Key returns the key of the item.
func (item *Item[K, V]) Key() K {
item.mu.RLock()
defer item.mu.RUnlock()
return item.key
}
// Value returns the value of the item.
func (item *Item[K, V]) Value() V {
item.mu.RLock()
defer item.mu.RUnlock()
return item.value
}
// TTL returns the TTL value of the item.
func (item *Item[K, V]) TTL() time.Duration {
item.mu.RLock()
defer item.mu.RUnlock()
return item.ttl
}
// ExpiresAt returns the expiration timestamp of the item.
func (item *Item[K, V]) ExpiresAt() time.Time {
item.mu.RLock()
defer item.mu.RUnlock()
return item.expiresAt
}
// Version returns the version of the item. It shows the total number of
// changes made to the item.
// If version tracking is disabled, the return value is always -1.
func (item *Item[K, V]) Version() int64 {
item.mu.RLock()
defer item.mu.RUnlock()
return item.version
}