volatile/volatile.go (view raw)
1package volatile
2
3import (
4 "fmt"
5 "time"
6
7 "github.com/sirupsen/logrus"
8)
9
10var logger = logrus.New()
11
12type Element[V any] struct {
13 value *V
14 Timestamp time.Time
15}
16
17type Volatile[K comparable, V any] struct {
18 data map[K]Element[V]
19 timeToLive time.Duration
20}
21
22func (v *Volatile[K, V]) clean() int {
23 now := time.Now()
24 keysToDelete := []K{}
25
26 for key, value := range v.data {
27 if now.Sub(value.Timestamp) > v.timeToLive {
28 keysToDelete = append(keysToDelete, key)
29 }
30 }
31 for _, key := range keysToDelete {
32 delete(v.data, key)
33 }
34 return len(keysToDelete)
35}
36
37func (v *Volatile[K, V]) cleanupRoutine(interval time.Duration) {
38 ticker := time.NewTicker(interval)
39 defer ticker.Stop()
40
41 for {
42 select {
43 case <-ticker.C:
44 amt := v.clean()
45 logger.Debug(amt, " elements were automatically removed from cache.")
46 }
47 }
48}
49
50func NewVolatile[K comparable, V any](timeToLive time.Duration, cleanupInterval time.Duration) *Volatile[K, V] {
51 v := &Volatile[K, V]{
52 data: make(map[K]Element[V]),
53 timeToLive: timeToLive,
54 }
55 go v.cleanupRoutine(cleanupInterval)
56 return v
57}
58
59func (v *Volatile[K, V]) Has(key K) bool {
60 v.clean()
61 _, ok := v.data[key]
62 return ok
63}
64
65func (v *Volatile[K, V]) Get(key K) (*V, error) {
66 v.clean()
67 element, ok := v.data[key]
68 if !ok {
69 return nil, fmt.Errorf("not found")
70 }
71 return element.value, nil
72}
73
74func (v *Volatile[K, V]) Remove(key K) (*V, error) {
75 v.clean()
76 value, ok := v.data[key]
77
78 if ok {
79 delete(v.data, key)
80 return value.value, nil
81 }
82
83 return nil, fmt.Errorf("not found")
84}
85
86func (v *Volatile[K, V]) Set(key K, value *V) error {
87 v.data[key] = Element[V]{value: value, Timestamp: time.Now()}
88 v.clean()
89 return nil
90}