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