-
Notifications
You must be signed in to change notification settings - Fork 1
/
inMemoryBackend.go
144 lines (113 loc) · 3.04 KB
/
inMemoryBackend.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
package httpcache
import (
"fmt"
"time"
lru "github.com/hashicorp/golang-lru/v2"
)
const defaultLurkerPeriod = 1 * time.Minute
type (
// MemoryBackend implements the cache backend interface with an "in memory" solution
MemoryBackend struct {
cacheMetrics Metrics
pool *lru.TwoQueueCache[string, inMemoryCacheEntry]
lurkerPeriod time.Duration
}
// MemoryBackendConfig config
MemoryBackendConfig struct {
Size int
}
// InMemoryBackendFactory factory
InMemoryBackendFactory struct {
config MemoryBackendConfig
frontendName string
lurkerPeriod time.Duration
}
inMemoryCacheEntry struct {
valid time.Time
data interface{}
}
)
var _ Backend = new(MemoryBackend)
// SetConfig for factory
func (f *InMemoryBackendFactory) SetConfig(config MemoryBackendConfig) *InMemoryBackendFactory {
f.config = config
return f
}
// SetLurkerPeriod sets the timeframe how often expired cache entries should be checked/cleaned up, if 0 is provided the default period of 1 minute is taken
func (f *InMemoryBackendFactory) SetLurkerPeriod(period time.Duration) *InMemoryBackendFactory {
f.lurkerPeriod = period
return f
}
// SetFrontendName used in Metrics
func (f *InMemoryBackendFactory) SetFrontendName(frontendName string) *InMemoryBackendFactory {
f.frontendName = frontendName
return f
}
// Build the instance
func (f *InMemoryBackendFactory) Build() (Backend, error) {
cache, _ := lru.New2Q[string, inMemoryCacheEntry](f.config.Size)
lurkerPeriod := defaultLurkerPeriod
if f.lurkerPeriod > 0 {
lurkerPeriod = f.lurkerPeriod
}
memoryBackend := &MemoryBackend{
pool: cache,
cacheMetrics: NewCacheMetrics("memory", f.frontendName),
lurkerPeriod: lurkerPeriod,
}
go memoryBackend.lurker()
return memoryBackend, nil
}
// SetSize creates a new underlying cache of the given size
func (m *MemoryBackend) SetSize(size int) error {
cache, err := lru.New2Q[string, inMemoryCacheEntry](size)
if err != nil {
return fmt.Errorf("lru cant create new TwoQueueCache: %w", err)
}
m.pool = cache
return nil
}
// Get tries to get an object from cache
func (m *MemoryBackend) Get(key string) (Entry, bool) {
entry, found := m.pool.Get(key)
if !found {
m.cacheMetrics.countMiss()
return Entry{}, false
}
m.cacheMetrics.countHit()
data, ok := entry.data.(Entry)
if !ok {
return Entry{}, false
}
return data, true
}
// Set a cache entry with a key
func (m *MemoryBackend) Set(key string, entry Entry) error {
m.pool.Add(key, inMemoryCacheEntry{
data: entry,
valid: entry.Meta.GraceTime,
})
return nil
}
// Purge a cache key
func (m *MemoryBackend) Purge(key string) error {
m.pool.Remove(key)
return nil
}
// Flush purges all entries in the cache
func (m *MemoryBackend) Flush() error {
m.pool.Purge()
return nil
}
func (m *MemoryBackend) lurker() {
for range time.Tick(m.lurkerPeriod) {
m.cacheMetrics.recordEntries(int64(m.pool.Len()))
for _, key := range m.pool.Keys() {
entry, found := m.pool.Peek(key)
if found && entry.valid.Before(time.Now()) {
m.pool.Remove(key)
break
}
}
}
}