Repository navigation
Expand file tree
/
Copy pathcache.go
More file actions
91 lines (81 loc) · 2.08 KB
/
Copy pathcache.go
File metadata and controls
91 lines (81 loc) · 2.08 KB
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
package ipgeolocation
import (
"sync"
"time"
)
// cacheEntry is one memoized lookup.
type cacheEntry struct {
fields map[string]string
found bool
expires time.Time
}
// resultCache is a bounded, time-limited cache. It keeps two generations of
// entries: writes go to the current generation, reads fall back to the
// previous one. When the current generation fills up it becomes the previous
// generation and a fresh map takes its place, which bounds memory at roughly
// twice maxEntries without the bookkeeping of a linked list.
type resultCache struct {
mu sync.RWMutex
current map[string]cacheEntry
previous map[string]cacheEntry
maxEntries int
ttl time.Duration
}
func newResultCache(maxEntries int, ttl time.Duration) *resultCache {
if maxEntries <= 0 || ttl <= 0 {
return nil
}
return &resultCache{
current: make(map[string]cacheEntry, 64),
maxEntries: maxEntries,
ttl: ttl,
}
}
func (c *resultCache) get(key string) (cacheEntry, bool) {
if c == nil {
return cacheEntry{}, false
}
c.mu.RLock()
entry, ok := c.current[key]
if !ok && c.previous != nil {
entry, ok = c.previous[key]
}
c.mu.RUnlock()
if !ok || time.Now().After(entry.expires) {
return cacheEntry{}, false
}
return entry, true
}
func (c *resultCache) put(key string, fields map[string]string, found bool) {
if c == nil {
return
}
entry := cacheEntry{fields: fields, found: found, expires: time.Now().Add(c.ttl)}
c.mu.Lock()
if len(c.current) >= c.maxEntries {
c.previous = c.current
c.current = make(map[string]cacheEntry, c.maxEntries/4+1)
}
c.current[key] = entry
c.mu.Unlock()
}
// reset empties the cache. It is called after a database refresh so that
// stale answers cannot outlive the data they came from.
func (c *resultCache) reset() {
if c == nil {
return
}
c.mu.Lock()
c.current = make(map[string]cacheEntry, 64)
c.previous = nil
c.mu.Unlock()
}
// len reports the number of live entries, used by the tests.
func (c *resultCache) len() int {
if c == nil {
return 0
}
c.mu.RLock()
defer c.mu.RUnlock()
return len(c.current) + len(c.previous)
}