-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcache.go
135 lines (121 loc) · 2.66 KB
/
cache.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
package main
import (
"encoding/json"
"fmt"
"io"
"os"
"path"
"path/filepath"
"sync"
"time"
)
type FileCacheBody struct {
Updated time.Time
Body interface{}
}
type FileCache struct {
sync.RWMutex
WorkDir string
FileCacheBody *FileCacheBody
CacheFileName string
}
func tryRestoreCache(workdir string, cacheFilename string) (*FileCacheBody, error) {
file, err := os.Open(path.Join(workdir, fmt.Sprintf("%s.json", cacheFilename)))
if err != nil {
return nil, err
}
bytes, err := io.ReadAll(file)
if err != nil {
return nil, err
}
var fcBody FileCacheBody
err = json.Unmarshal(bytes, &fcBody)
if err != nil {
return nil, err
}
return &fcBody, nil
}
func NewFileCache(workdir string, cacheName string) *FileCache {
fcBody, err := tryRestoreCache(workdir, cacheName)
if err != nil {
logger.Sugar().Error(err.Error())
fcBody = &FileCacheBody{}
}
return &FileCache{
WorkDir: workdir,
CacheFileName: cacheName,
FileCacheBody: fcBody,
}
}
func (fc *FileCache) Put(data interface{}) (persistent bool, err error) {
fc.RWMutex.Lock()
defer fc.RWMutex.Unlock()
// refresh on-memory data
fc.FileCacheBody.Updated = time.Now()
fc.FileCacheBody.Body = data
bytes, err := json.Marshal(*fc.FileCacheBody)
if err != nil {
return false, err
}
file, err := os.Create(filepath.Join(fc.WorkDir, fmt.Sprintf("%s.json", fc.CacheFileName)))
if err != nil {
return false, err
}
nbytes, err := file.Write(bytes)
if len(bytes) != nbytes {
return false, fmt.Errorf("written bytes mismatch: %d, actual: %d", len(bytes), nbytes)
}
return true, nil
}
func (fc *FileCache) Get() interface{} {
fc.RWMutex.RLock()
defer fc.RWMutex.RUnlock()
return fc.FileCacheBody.Body
}
func (fc *FileCache) MaybeGet(ttl time.Duration) interface{} {
fc.RWMutex.RLock()
defer fc.RWMutex.RUnlock()
if fc.IsExpired(ttl) {
return nil
} else {
return fc.Get()
}
}
// XXX: to support generics
func MaybeGetFromFileCache[T any](fc *FileCache, ttl time.Duration) *T {
if result := fc.MaybeGet(ttl); result == nil {
return nil
} else {
switch result.(type) {
case []interface{}:
var obj T
txt, err := json.Marshal(result)
if err != nil {
return nil
}
err = json.Unmarshal(txt, &obj)
if err != nil {
return nil
}
return &obj
case map[string]interface{}:
var obj T
txt, err := json.Marshal(result)
if err != nil {
return nil
}
err = json.Unmarshal(txt, &obj)
if err != nil {
return nil
}
return &obj
default:
return result.(*T)
}
}
}
func (fc *FileCache) IsExpired(ttl time.Duration) bool {
fc.RWMutex.RLock()
defer fc.RWMutex.RUnlock()
return time.Now().After(fc.FileCacheBody.Updated.Add(ttl))
}