generated from devnw/oss-template
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcache_exported.go
144 lines (123 loc) · 3.11 KB
/
cache_exported.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
// Package ttl implements a TTL cache which can be used to store
// values a specified timeout period. The cache implementation
// supports extending the timeout of regularly read values as well
// as storing custom TTL timeouts for specific key/value pairs
// with the `SetTTL` method.
package ttl
import (
"context"
"fmt"
"sync"
"time"
)
// Cache implements a TTL Cache which can be used to store
// values values a specified timeout period. If the value
// is read before the timeout period has passed, and the
// extend flag is set to true, the timeout period will be
// extended by the timeout period. This ensures that the
// value is not removed if it is regularly accessed within
// the timeout period.
type Cache[K comparable, V any] struct {
ctx context.Context
timeout time.Duration
extend bool
values map[K]*rw[V]
valuesMu sync.RWMutex
}
// NewCache creates a new TTL Cache using the a timeout
// for the default timeout of stored values and the extend
// value to determine if the cache lifetime of the set values
// should be extended upon read
func NewCache[K comparable, V any](
ctx context.Context,
timeout time.Duration,
extend bool,
) *Cache[K, V] {
if ctx == nil {
ctx = context.Background()
}
c := &Cache[K, V]{
ctx: ctx,
timeout: timeout,
extend: extend,
values: make(map[K]*rw[V]),
}
go func() {
<-ctx.Done()
c.cleanup()
}()
return c
}
// Delete removes the values associated with the
// passed key from the cache
func (c *Cache[K, V]) Delete(ctx context.Context, key K) {
c.valuesMu.Lock()
defer c.valuesMu.Unlock()
rw, ok := c.values[key]
if !ok {
return
}
// Cancel the context and delete the map entry
// for this key
rw.cancel()
delete(c.values, key)
}
// Get accesses the value for the Key provide
func (c *Cache[K, V]) Get(ctx context.Context, key K) (V, bool) {
if c.values == nil {
return *new(V), false
}
c.valuesMu.RLock()
rw, ok := c.values[key]
c.valuesMu.RUnlock()
// No stored value for this key
if !ok {
return *new(V), ok
}
select {
case <-c.ctx.Done():
return *new(V), false
case v, ok := <-rw.read:
if !ok {
return *new(V), ok
}
return v, ok
}
}
// Set sets the value for the key provided
func (c *Cache[K, V]) Set(ctx context.Context, key K, value V) error {
return c.SetTTL(ctx, key, value, c.timeout)
}
// SetTTL allows for direct control over the TTL of a specific
// Key in the cache which is passed as timeout in parameter three.
// This timeout can be `nil` which will keep the value permanently
// in the cache without expiration until it's deleted
func (c *Cache[K, V]) SetTTL(
ctx context.Context,
key K, value V,
timeout time.Duration,
) error {
if c.values == nil {
return fmt.Errorf("canceled cache instance")
}
// Pull the parent context if the passed context is nil
if ctx == nil {
ctx = c.ctx
}
c.valuesMu.RLock()
rw, ok := c.values[key]
c.valuesMu.RUnlock()
// No stored value for this key
if !ok {
return c.write(key, c.set(key, value, timeout, c.extend))
}
select {
case <-ctx.Done():
return ctx.Err()
case rw.write <- newvalue[V]{
v: value,
timeout: timeout,
}:
}
return nil
}