-
Notifications
You must be signed in to change notification settings - Fork 8
/
map_store.go
144 lines (118 loc) · 2.76 KB
/
map_store.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 throttle
import (
"bytes"
"encoding/json"
"reflect"
"sync"
"time"
)
const (
defaultCleaningPeriod = 15 * time.Minute
)
// A very simple implementation of a key value store (a concurrent safe map)
type MapStore struct {
*sync.RWMutex
data map[string][]byte
binding FreshnessInformer
}
type FreshnessInformer interface {
IsFresh() bool
}
type MapStoreOptions struct {
// The period to clean the store in
CleaningPeriod time.Duration
}
// Error Type for the key value store
type MapStoreError string
// The Error for Key Value Store
func (err MapStoreError) Error() string {
return "Throttle Map Store Error: " + string(err)
}
// Set a key
func (s *MapStore) Set(key string, value []byte) error {
s.Lock()
s.data[key] = value
s.Unlock()
return nil
}
// Delete a key
func (s *MapStore) Delete(key string) {
s.Lock()
delete(s.data, key)
s.Unlock()
}
// Get a key, will return an error if the key does not exist
func (s *MapStore) Get(key string) (value []byte, err error) {
s.RLock()
value, ok := s.data[key]
s.RUnlock()
if !ok {
err = MapStoreError("Key " + key + " does not exist")
return value, err
} else {
return value, nil
}
}
// Read the data into the given binding
func (s *MapStore) Read(key string) (FreshnessInformer, error) {
byteArray, err := s.Get(key)
if err != nil {
return nil, err
}
byteBufferString := bytes.NewBuffer(byteArray)
var arbitraryStructure interface{}
if err := json.NewDecoder(byteBufferString).Decode(&arbitraryStructure); err != nil {
return nil, err
}
for k, v := range arbitraryStructure.(map[string]interface{}) {
if field := reflect.ValueOf(s.binding).FieldByName(k); field.IsValid() && field.CanSet() {
field.Set(reflect.ValueOf(v))
}
}
return s.binding, err
}
// Clean the store from expired values
func (s *MapStore) Clean() {
for key := range s.data {
value, err := s.Read(key)
if err == nil && !value.IsFresh() {
s.Delete(key)
} else if err != nil {
panic(err)
}
}
}
// Simple cleanup mechanism, cleaning the store every 15 minutes
func (s *MapStore) CleanEvery(cleaningPeriod time.Duration) {
c := time.Tick(cleaningPeriod)
for {
select {
case <-c:
s.Clean()
}
}
}
// Returns a simple key value store
func NewMapStore(binding FreshnessInformer, options ...*MapStoreOptions) *MapStore {
s := &MapStore{
&sync.RWMutex{},
make(map[string][]byte),
binding,
}
o := newMapStoreOptions(options)
go s.CleanEvery(o.CleaningPeriod)
return s
}
// Returns new map store options from defaults and given options
func newMapStoreOptions(options []*MapStoreOptions) *MapStoreOptions {
o := &MapStoreOptions{
defaultCleaningPeriod,
}
if len(options) == 0 {
return o
}
if options[0].CleaningPeriod != 0 {
o.CleaningPeriod = options[0].CleaningPeriod
}
return o
}