go-micro.dev/v5@v5.12.0/store/memory.go (about) 1 package store 2 3 import ( 4 "path/filepath" 5 "sort" 6 "strings" 7 "time" 8 9 "github.com/patrickmn/go-cache" 10 "github.com/pkg/errors" 11 ) 12 13 // NewMemoryStore returns a memory store. 14 func NewMemoryStore(opts ...Option) Store { 15 s := &memoryStore{ 16 options: Options{ 17 Database: "micro", 18 Table: "micro", 19 }, 20 store: cache.New(cache.NoExpiration, 5*time.Minute), 21 } 22 for _, o := range opts { 23 o(&s.options) 24 } 25 return s 26 } 27 28 type memoryStore struct { 29 options Options 30 31 store *cache.Cache 32 } 33 34 type storeRecord struct { 35 expiresAt time.Time 36 metadata map[string]interface{} 37 key string 38 value []byte 39 } 40 41 func (m *memoryStore) key(prefix, key string) string { 42 return filepath.Join(prefix, key) 43 } 44 45 func (m *memoryStore) prefix(database, table string) string { 46 if len(database) == 0 { 47 database = m.options.Database 48 } 49 if len(table) == 0 { 50 table = m.options.Table 51 } 52 return filepath.Join(database, table) 53 } 54 55 func (m *memoryStore) get(prefix, key string) (*Record, error) { 56 key = m.key(prefix, key) 57 58 var storedRecord *storeRecord 59 r, found := m.store.Get(key) 60 if !found { 61 return nil, ErrNotFound 62 } 63 64 storedRecord, ok := r.(*storeRecord) 65 if !ok { 66 return nil, errors.New("Retrieved a non *storeRecord from the cache") 67 } 68 69 // Copy the record on the way out 70 newRecord := &Record{} 71 newRecord.Key = strings.TrimPrefix(storedRecord.key, prefix+"/") 72 newRecord.Value = make([]byte, len(storedRecord.value)) 73 newRecord.Metadata = make(map[string]interface{}) 74 75 // copy the value into the new record 76 copy(newRecord.Value, storedRecord.value) 77 78 // check if we need to set the expiry 79 if !storedRecord.expiresAt.IsZero() { 80 newRecord.Expiry = time.Until(storedRecord.expiresAt) 81 } 82 83 // copy in the metadata 84 for k, v := range storedRecord.metadata { 85 newRecord.Metadata[k] = v 86 } 87 88 return newRecord, nil 89 } 90 91 func (m *memoryStore) set(prefix string, r *Record) { 92 key := m.key(prefix, r.Key) 93 94 // copy the incoming record and then 95 // convert the expiry in to a hard timestamp 96 i := &storeRecord{} 97 i.key = r.Key 98 i.value = make([]byte, len(r.Value)) 99 i.metadata = make(map[string]interface{}) 100 101 // copy the the value 102 copy(i.value, r.Value) 103 104 // set the expiry 105 if r.Expiry != 0 { 106 i.expiresAt = time.Now().Add(r.Expiry) 107 } 108 109 // set the metadata 110 for k, v := range r.Metadata { 111 i.metadata[k] = v 112 } 113 114 m.store.Set(key, i, r.Expiry) 115 } 116 117 func (m *memoryStore) delete(prefix, key string) { 118 key = m.key(prefix, key) 119 m.store.Delete(key) 120 } 121 122 func (m *memoryStore) list(prefix string, limit, offset uint) []string { 123 allItems := m.store.Items() 124 foundKeys := make([]string, 0, len(allItems)) 125 126 for k := range allItems { 127 if !strings.HasPrefix(k, prefix+"/") { 128 continue 129 } 130 foundKeys = append(foundKeys, strings.TrimPrefix(k, prefix+"/")) 131 } 132 133 if limit != 0 || offset != 0 { 134 sort.Slice(foundKeys, func(i, j int) bool { return foundKeys[i] < foundKeys[j] }) 135 min := func(i, j uint) uint { 136 if i < j { 137 return i 138 } 139 return j 140 } 141 return foundKeys[offset:min(limit, uint(len(foundKeys)))] 142 } 143 144 return foundKeys 145 } 146 147 func (m *memoryStore) Close() error { 148 m.store.Flush() 149 return nil 150 } 151 152 func (m *memoryStore) Init(opts ...Option) error { 153 for _, o := range opts { 154 o(&m.options) 155 } 156 return nil 157 } 158 159 func (m *memoryStore) String() string { 160 return "memory" 161 } 162 163 func (m *memoryStore) Read(key string, opts ...ReadOption) ([]*Record, error) { 164 readOpts := ReadOptions{} 165 for _, o := range opts { 166 o(&readOpts) 167 } 168 169 prefix := m.prefix(readOpts.Database, readOpts.Table) 170 171 var keys []string 172 173 // Handle Prefix / suffix 174 if readOpts.Prefix || readOpts.Suffix { 175 k := m.list(prefix, 0, 0) 176 limit := int(readOpts.Limit) 177 offset := int(readOpts.Offset) 178 179 if limit > len(k) { 180 limit = len(k) 181 } 182 183 if offset > len(k) { 184 offset = len(k) 185 } 186 187 for i := offset; i < limit; i++ { 188 kk := k[i] 189 190 if readOpts.Prefix && !strings.HasPrefix(kk, key) { 191 continue 192 } 193 194 if readOpts.Suffix && !strings.HasSuffix(kk, key) { 195 continue 196 } 197 198 keys = append(keys, kk) 199 } 200 } else { 201 keys = []string{key} 202 } 203 204 var results []*Record 205 206 for _, k := range keys { 207 r, err := m.get(prefix, k) 208 if err != nil { 209 return results, err 210 } 211 results = append(results, r) 212 } 213 214 return results, nil 215 } 216 217 func (m *memoryStore) Write(r *Record, opts ...WriteOption) error { 218 writeOpts := WriteOptions{} 219 for _, o := range opts { 220 o(&writeOpts) 221 } 222 223 prefix := m.prefix(writeOpts.Database, writeOpts.Table) 224 225 if len(opts) > 0 { 226 // Copy the record before applying options, or the incoming record will be mutated 227 newRecord := Record{} 228 newRecord.Key = r.Key 229 newRecord.Value = make([]byte, len(r.Value)) 230 newRecord.Metadata = make(map[string]interface{}) 231 copy(newRecord.Value, r.Value) 232 newRecord.Expiry = r.Expiry 233 234 if !writeOpts.Expiry.IsZero() { 235 newRecord.Expiry = time.Until(writeOpts.Expiry) 236 } 237 if writeOpts.TTL != 0 { 238 newRecord.Expiry = writeOpts.TTL 239 } 240 241 for k, v := range r.Metadata { 242 newRecord.Metadata[k] = v 243 } 244 245 m.set(prefix, &newRecord) 246 return nil 247 } 248 249 // set 250 m.set(prefix, r) 251 252 return nil 253 } 254 255 func (m *memoryStore) Delete(key string, opts ...DeleteOption) error { 256 deleteOptions := DeleteOptions{} 257 for _, o := range opts { 258 o(&deleteOptions) 259 } 260 261 prefix := m.prefix(deleteOptions.Database, deleteOptions.Table) 262 m.delete(prefix, key) 263 return nil 264 } 265 266 func (m *memoryStore) Options() Options { 267 return m.options 268 } 269 270 func (m *memoryStore) List(opts ...ListOption) ([]string, error) { 271 listOptions := ListOptions{} 272 273 for _, o := range opts { 274 o(&listOptions) 275 } 276 277 prefix := m.prefix(listOptions.Database, listOptions.Table) 278 keys := m.list(prefix, listOptions.Limit, listOptions.Offset) 279 280 if len(listOptions.Prefix) > 0 { 281 var prefixKeys []string 282 for _, k := range keys { 283 if strings.HasPrefix(k, listOptions.Prefix) { 284 prefixKeys = append(prefixKeys, k) 285 } 286 } 287 keys = prefixKeys 288 } 289 290 if len(listOptions.Suffix) > 0 { 291 var suffixKeys []string 292 for _, k := range keys { 293 if strings.HasSuffix(k, listOptions.Suffix) { 294 suffixKeys = append(suffixKeys, k) 295 } 296 } 297 keys = suffixKeys 298 } 299 300 return keys, nil 301 }