-
Notifications
You must be signed in to change notification settings - Fork 0
/
memcache.go
162 lines (136 loc) · 3.53 KB
/
memcache.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package memcache
import (
"errors"
"sync"
"time"
)
var (
errKeyDoesntExist = errors.New("key doesnt exists")
errNotImplemented = errors.New("not implemented")
errKeyAlreadyExist = errors.New("key already exists")
errMaxCapacityReached = errors.New("reached max capacity")
)
// MemCache .
type MemCache struct {
mu sync.RWMutex // global m utex
imu sync.RWMutex // increment mutex
capacity uint64 // capacity
numElements uint64 // number of items
store *MemStore // memory store
defaultExpTime time.Duration // default live duration
}
// New return a new MemCache
// capacity: max items
// allocCapacity: startup allocation size cant be > capacity
// defaultExpireTime: item liveness duration
func New(capacity uint64, allocCapacity uint64, defaultExpireTime time.Duration) *MemCache {
return &MemCache{
capacity: capacity,
numElements: 0,
store: NewMemStore(allocCapacity),
defaultExpTime: defaultExpireTime,
}
}
// Audit memstore
// Will delete all expire items
func (mc *MemCache) Audit() {
mc.mu.Lock()
defer mc.mu.Unlock()
mc.store.Audit()
}
// Renew will clear cache and reallocate new space for allocCapacity
func (mc *MemCache) Renew(allocCapacity uint64) {
mc.mu.Lock()
mc.mu.Unlock()
mc.store = NewMemStore(allocCapacity)
}
// size return numElements
func (mc *MemCache) size() uint64 {
mc.imu.RLock()
defer mc.imu.RUnlock()
return mc.numElements
}
// increment memcache num items
func (mc *MemCache) incr() {
mc.imu.Lock()
defer mc.imu.Unlock()
mc.numElements++
}
// decrement memcache num items
func (mc *MemCache) decr() {
mc.imu.Lock()
defer mc.imu.Unlock()
mc.numElements--
}
// Put will add a new item to the cache store
// It will store item if doesn't exist and max
// capacity isn't reached elsewill return err
// errKeyAlreadyExist
func (mc *MemCache) Put(key string, value interface{}) error {
mc.mu.RLock()
defer mc.mu.RUnlock()
// if key already exist return err
if _, exist := mc.store.Get(key); exist {
return errKeyAlreadyExist
}
// check capacity
if mc.size() >= mc.capacity {
return errMaxCapacityReached
}
mc.incr()
// compute expiration time and put new item
tExp := time.Now().Add(mc.defaultExpTime)
mc.store.Put(key, newItem(value, tExp))
return nil
}
// Get will return element of the given key and a nil
// error if key already exists
func (mc *MemCache) Get(key string) (interface{}, error) {
item, exist := mc.store.Get(key)
if !exist {
return nil, errKeyDoesntExist
}
return item.GetValue(), nil
}
// Delete will try to delete key if it exists else will
// return a err
func (mc *MemCache) Delete(key string) error {
mc.mu.RLock()
defer mc.mu.RUnlock()
if _, exist := mc.store.Get(key); !exist {
return errKeyDoesntExist
}
mc.store.Delete(key)
mc.decr()
return nil
}
// Update will try to update item if it exists
func (mc *MemCache) Update(key string, value interface{}) error {
mc.mu.RLock()
defer mc.mu.RUnlock()
// exist
item, exist := mc.store.Get(key)
if !exist {
return errKeyDoesntExist
}
item.SetValue(value)
return nil
}
// Patch will update item if it exist else will create item
// with given value
func (mc *MemCache) Patch(key string, value interface{}) error {
mc.mu.RLock()
defer mc.mu.RUnlock()
item, exist := mc.store.Get(key)
if !exist {
if mc.size() >= mc.capacity {
return errMaxCapacityReached
}
mc.incr()
tExp := time.Now().Add(mc.defaultExpTime)
mc.store.Put(key, newItem(value, tExp))
return nil
}
item.SetValue(value)
return nil
}