-
Notifications
You must be signed in to change notification settings - Fork 0
/
expiry-map.go
134 lines (108 loc) · 2.66 KB
/
expiry-map.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
package expirymap
import (
"iter"
"sync"
"time"
)
// Map is a map of K key and V values with a builtin garbage cleaner that automatically
// deletes entries after a given expiry delay.
type Map[K comparable, V any] struct {
storedMap map[K]*Content[V]
expiryDelay time.Duration
gargabeCleanInterval time.Duration
mutex sync.Mutex
stop chan bool
}
// Content is the value stored for a given key in the ExpiryMap.
type Content[V any] struct {
Data V
lastUpdated time.Time
}
// New returns a new ExpiryMap.
// It also starts a goroutine that periodically cleans up expired entries
// according to the expiryDelay every gargabeCleanInterval.
func New[K comparable, V any](expiryDelay, gargabeCleanInterval time.Duration) *Map[K, V] {
s := &Map[K, V]{
storedMap: make(map[K]*Content[V]),
expiryDelay: expiryDelay,
gargabeCleanInterval: gargabeCleanInterval,
stop: make(chan bool),
}
s.start()
return s
}
// Get returns the value for a given key.
func (s *Map[K, V]) Get(key K) (V, bool) {
s.mutex.Lock()
defer s.mutex.Unlock()
content, found := s.storedMap[key]
if !found {
content = &Content[V]{}
}
return content.Data, found
}
// Set sets the value for a given key and reset its expiry time.
func (s *Map[K, V]) Set(key K, data V) {
s.mutex.Lock()
defer s.mutex.Unlock()
content := &Content[V]{}
content.lastUpdated = time.Now()
content.Data = data
s.storedMap[key] = content
}
// Delete deletes the value for a given key.
func (s *Map[K, V]) Delete(key K) {
s.mutex.Lock()
defer s.mutex.Unlock()
delete(s.storedMap, key)
}
// Len returns the number of stored entries.
func (s *Map[K, V]) Len() int {
s.mutex.Lock()
defer s.mutex.Unlock()
return len(s.storedMap)
}
// Iterate returns an iterator to loop over the stored entries.
func (s *Map[K, V]) Iterate() iter.Seq2[K, V] {
return func(next func(K, V) bool) {
s.mutex.Lock()
defer s.mutex.Unlock()
for k, v := range s.storedMap {
if !next(k, v.Data) {
return
}
}
}
}
// Clear deletes all stored entries.
func (s *Map[K, V]) Clear() {
s.mutex.Lock()
defer s.mutex.Unlock()
clear(s.storedMap)
}
// Stop stops the garbage cleaner goroutine.
func (s *Map[K, V]) Stop() {
s.stop <- true
}
func (s *Map[K, V]) start() {
go func() {
for {
select {
case <-s.stop:
return
case <-time.Tick(s.gargabeCleanInterval):
s.gargabeClean()
}
}
}()
}
func (s *Map[K, V]) gargabeClean() {
s.mutex.Lock()
defer s.mutex.Unlock()
expiredTime := time.Now().Add(-s.expiryDelay)
for key, u := range s.storedMap {
if u.lastUpdated.Before(expiredTime) {
delete(s.storedMap, key)
}
}
}