-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwarmcache.go
95 lines (85 loc) · 2.07 KB
/
warmcache.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
package warmcache
import (
"sync"
"time"
)
// FetchCacheFunc is a function for fetching origin data and then caching it.
type FetchCacheFunc func() (interface{}, error)
// CacheMgr caches origin data and updates it.
type CacheMgr struct {
mu sync.RWMutex
cacheData interface{}
isStale bool
fetchCacheFunc FetchCacheFunc
fetchInterval time.Duration
fetchErr error
fetchStopChan chan struct{}
elapsedChan chan<- time.Duration
}
// NewCacheMgr creates cachemgr.
// If elapsedChan is nil, send no messages to elapsedChan.
func NewCacheMgr(cacheFunc FetchCacheFunc, interval time.Duration, elapsedChan chan<- time.Duration) (*CacheMgr, error) {
cacheData, err := cacheFunc()
if err != nil {
return nil, err
}
c := &CacheMgr{
cacheData: cacheData,
isStale: false,
fetchCacheFunc: cacheFunc,
fetchInterval: interval,
fetchStopChan: make(chan struct{}),
elapsedChan: elapsedChan,
}
go func() {
<-time.After(c.fetchInterval)
c.fetchLoop()
}()
return c, nil
}
// CacheData returns cached data.
// If the given FetchCacheFunc returned err, CacheData returns staled data and the error.
func (c *CacheMgr) CacheData() (interface{}, bool, error) {
c.mu.RLock()
defer c.mu.RUnlock()
return c.cacheData, c.isStale, c.fetchErr
}
// Stop stops auto fetching cache.
func (c *CacheMgr) Stop() {
close(c.fetchStopChan)
}
func (c *CacheMgr) fetchLoop() {
for {
timerChan := time.After(c.fetchInterval)
data, isStale, err := c.fetch()
c.mu.Lock()
c.cacheData, c.isStale, c.fetchErr = data, isStale, err
c.mu.Unlock()
select {
case <-timerChan:
case <-c.fetchStopChan:
close(c.elapsedChan)
return
}
}
}
func (c *CacheMgr) fetch() (data interface{}, isStale bool, err error) {
startTime := time.Now()
defer func() {
elapsed := time.Since(startTime)
if c.fetchInterval < elapsed {
isStale = true
} else {
isStale = false
}
if c.elapsedChan != nil {
c.elapsedChan <- elapsed
}
}()
data, err = c.fetchCacheFunc()
if err != nil {
// Still use stale cached data
data = c.cacheData
}
return
}