-
Notifications
You must be signed in to change notification settings - Fork 199
/
timeCache.go
125 lines (101 loc) · 3.03 KB
/
timeCache.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
package timecache
import (
"sync"
"time"
"github.com/ElrondNetwork/elrond-go/dataRetriever"
"github.com/ElrondNetwork/elrond-go/storage"
)
var _ dataRetriever.RequestedItemsHandler = (*TimeCache)(nil)
type span struct {
timestamp time.Time
span time.Duration
}
// TimeCache can retain an amount of string keys for a defined period of time
// sweeping (clean-up) is triggered each time a new item is added or a key is present in the time cache
// This data structure is concurrent safe.
type TimeCache struct {
mut sync.RWMutex
data map[string]*span
defaultSpan time.Duration
}
// NewTimeCache creates a new time cache data structure instance
func NewTimeCache(defaultSpan time.Duration) *TimeCache {
return &TimeCache{
data: make(map[string]*span),
defaultSpan: defaultSpan,
}
}
// Add will store the key in the time cache
// Double adding the key is not permitted by the time cache. Also, add will trigger sweeping.
func (tc *TimeCache) Add(key string) error {
return tc.add(key, tc.defaultSpan)
}
func (tc *TimeCache) add(key string, duration time.Duration) error {
if len(key) == 0 {
return storage.ErrEmptyKey
}
tc.mut.Lock()
defer tc.mut.Unlock()
tc.data[key] = &span{
timestamp: time.Now(),
span: duration,
}
return nil
}
// AddWithSpan will store the key in the time cache with the provided span duration
// Double adding the key is not permitted by the time cache. Also, add will trigger sweeping.
func (tc *TimeCache) AddWithSpan(key string, duration time.Duration) error {
return tc.add(key, duration)
}
// Upsert will add the key and provided duration if not exists
// If the record exists, will update the duration if the provided duration is larger than existing
// Also, it will reset the contained timestamp to time.Now
func (tc *TimeCache) Upsert(key string, duration time.Duration) error {
if len(key) == 0 {
return storage.ErrEmptyKey
}
tc.mut.Lock()
defer tc.mut.Unlock()
existing, found := tc.data[key]
if found {
if existing.span < duration {
existing.span = duration
}
existing.timestamp = time.Now()
return nil
}
tc.data[key] = &span{
timestamp: time.Now(),
span: duration,
}
return nil
}
// Sweep starts from the oldest element and will search each element if it is still valid to be kept. Sweep ends when
// it finds an element that is still valid
func (tc *TimeCache) Sweep() {
tc.mut.Lock()
defer tc.mut.Unlock()
for key, element := range tc.data {
isOldElement := time.Since(element.timestamp) > element.span
if isOldElement {
delete(tc.data, key)
}
}
}
// Has returns if the key is still found in the time cache
func (tc *TimeCache) Has(key string) bool {
tc.mut.RLock()
defer tc.mut.RUnlock()
_, ok := tc.data[key]
return ok
}
// Len returns the number of elements which are still stored in the time cache
func (tc *TimeCache) Len() int {
tc.mut.RLock()
defer tc.mut.RUnlock()
return len(tc.data)
}
// IsInterfaceNil returns true if there is no value under the interface
func (tc *TimeCache) IsInterfaceNil() bool {
return tc == nil
}