-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcache.go
More file actions
146 lines (123 loc) · 2.99 KB
/
cache.go
File metadata and controls
146 lines (123 loc) · 2.99 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
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
package cache
import (
"context"
"errors"
"time"
"github.com/thewizardplusplus/go-cache/gc"
"github.com/thewizardplusplus/go-cache/models"
hashmap "github.com/thewizardplusplus/go-hashmap"
)
// ...
var (
ErrKeyMissed = errors.New("key missed")
ErrKeyExpired = errors.New("key expired")
)
// Cache ...
type Cache struct {
storage hashmap.Storage
clock models.Clock
}
// NewCache ...
func NewCache(options ...Option) Cache {
// default options
cache := Cache{
storage: hashmap.NewConcurrentHashMap(),
clock: time.Now,
}
for _, option := range options {
option(&cache)
}
return cache
}
// NewCacheWithGC ...
//
// It additionally runs garbage collection in background.
//
func NewCacheWithGC(ctx context.Context, options ...OptionWithGC) Cache {
config := newConfigWithGC(options)
gcInstance := config.gcFactory(config.storage, config.clock)
go gc.Run(ctx, gcInstance, config.gcPeriod)
return NewCache(WithStorage(config.storage), WithClock(config.clock))
}
// Get ...
//
// The error can be ErrKeyMissed or ErrKeyExpired only.
//
func (cache Cache) Get(key hashmap.Key) (data interface{}, err error) {
data, ok := cache.storage.Get(key)
if !ok {
return nil, ErrKeyMissed
}
value := data.(models.Value)
if value.IsExpired(cache.clock) {
return nil, ErrKeyExpired
}
return value.Data, nil
}
// GetWithGC ...
//
// It additionally deletes the value if its time to live expired.
//
func (cache Cache) GetWithGC(key hashmap.Key) (data interface{}, err error) {
data, err = cache.Get(key)
if err != nil {
if err == ErrKeyExpired {
cache.storage.Delete(key)
}
return nil, err
}
return data, nil
}
// Iterate ...
//
// If the handler returns false, iteration is broken.
//
func (cache Cache) Iterate(ctx context.Context, handler hashmap.Handler) bool {
return cache.iterateWithExpiredHandler(ctx, handler, func(key hashmap.Key) {})
}
// IterateWithGC ...
//
// It additionally deletes iterated values if their time to live expired.
//
// If the handler returns false, iteration is broken.
//
func (cache Cache) IterateWithGC(
ctx context.Context,
handler hashmap.Handler,
) bool {
return cache.iterateWithExpiredHandler(ctx, handler, cache.storage.Delete)
}
// Set ...
//
// Zero time to live means infinite one.
//
func (cache Cache) Set(key hashmap.Key, data interface{}, ttl time.Duration) {
var expirationTime time.Time
if ttl != 0 {
expirationTime = cache.clock().Add(ttl)
}
cache.storage.Set(key, models.Value{
Data: data,
ExpirationTime: expirationTime,
})
}
// Delete ...
func (cache Cache) Delete(key hashmap.Key) {
cache.storage.Delete(key)
}
func (cache Cache) iterateWithExpiredHandler(
ctx context.Context,
handler hashmap.Handler,
expiredHandler func(key hashmap.Key),
) bool {
return cache.storage.Iterate(
hashmap.WithInterruption(ctx, func(key hashmap.Key, data interface{}) bool {
value := data.(models.Value)
if value.IsExpired(cache.clock) {
expiredHandler(key)
return true
}
return handler(key, value.Data)
}),
)
}