-
Notifications
You must be signed in to change notification settings - Fork 42
/
cache.go
138 lines (106 loc) · 2.62 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
136
137
138
package rcap
import (
"sync"
"time"
"github.com/pkg/errors"
)
// ErrCacheKeyNotFound is returned when a non-existent cache key is requested
var ErrCacheKeyNotFound = errors.New("key not found")
// CacheConfig is configuration for the cache capability
type CacheConfig struct {
Enabled bool `json:"enabled" yaml:"enabled"`
Rules CacheRules `json:"rules" yaml:"rules"`
RedisConfig *RedisConfig `json:"redis,omitempty" yaml:"redis,omitempty"`
}
type CacheRules struct {
AllowSet bool `json:"allowSet" yaml:"allowSet"`
AllowGet bool `json:"allowGet" yaml:"allowGet"`
AllowDelete bool `json:"allowDelete" yaml:"allowDelete"`
}
// CacheCapability gives Runnables access to a key/value cache
type CacheCapability interface {
Set(key string, val []byte, ttl int) error
Get(key string) ([]byte, error)
Delete(key string) error
}
// memoryCache is a "default" cache implementation for Reactr
type memoryCache struct {
config CacheConfig
values map[string]*uniqueVal
lock sync.RWMutex
}
// this is used to 1) allow pointers and 2) ensure checks for unique values are cheaper (pointer equality)
type uniqueVal struct {
val []byte
}
func SetupCache(config CacheConfig) CacheCapability {
var cache CacheCapability
if config.RedisConfig == nil {
m := &memoryCache{
config: config,
values: make(map[string]*uniqueVal),
lock: sync.RWMutex{},
}
cache = m
} else {
r := newRedisCache(config)
cache = r
}
return cache
}
func (m *memoryCache) Set(key string, val []byte, ttl int) error {
if !m.config.Enabled || !m.config.Rules.AllowSet {
return ErrCapabilityNotEnabled
}
m.lock.Lock()
defer m.lock.Unlock()
uVal := &uniqueVal{
val: val,
}
m.values[key] = uVal
if ttl > 0 {
go func() {
<-time.After(time.Second * time.Duration(ttl))
m.lock.Lock()
defer m.lock.Unlock()
currentVal := m.values[key]
if currentVal == uVal {
delete(m.values, key)
}
}()
}
return nil
}
func (m *memoryCache) Get(key string) ([]byte, error) {
if !m.config.Enabled || !m.config.Rules.AllowGet {
return nil, ErrCapabilityNotEnabled
}
m.lock.RLock()
defer m.lock.RUnlock()
uVal, exists := m.values[key]
if !exists {
return nil, ErrCacheKeyNotFound
}
return uVal.val, nil
}
func (m *memoryCache) Delete(key string) error {
if !m.config.Enabled || !m.config.Rules.AllowDelete {
return ErrCapabilityNotEnabled
}
m.lock.Lock()
defer m.lock.Unlock()
_, exists := m.values[key]
if !exists {
return nil
}
delete(m.values, key)
return nil
}
func defaultCacheRules() CacheRules {
c := CacheRules{
AllowSet: true,
AllowGet: true,
AllowDelete: true,
}
return c
}