gitee.com/sasukebo/go-micro/v4@v4.7.1/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 key string 36 value []byte 37 metadata map[string]interface{} 38 expiresAt time.Time 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 allKeys := make([]string, len(allItems)) 125 i := 0 126 127 for k := range allItems { 128 if !strings.HasPrefix(k, prefix+"/") { 129 continue 130 } 131 allKeys[i] = strings.TrimPrefix(k, prefix+"/") 132 i++ 133 } 134 135 if limit != 0 || offset != 0 { 136 sort.Slice(allKeys, func(i, j int) bool { return allKeys[i] < allKeys[j] }) 137 min := func(i, j uint) uint { 138 if i < j { 139 return i 140 } 141 return j 142 } 143 return allKeys[offset:min(limit, uint(len(allKeys)))] 144 } 145 146 return allKeys 147 } 148 149 func (m *memoryStore) Close() error { 150 m.store.Flush() 151 return nil 152 } 153 154 func (m *memoryStore) Init(opts ...Option) error { 155 for _, o := range opts { 156 o(&m.options) 157 } 158 return nil 159 } 160 161 func (m *memoryStore) String() string { 162 return "memory" 163 } 164 165 func (m *memoryStore) Read(key string, opts ...ReadOption) ([]*Record, error) { 166 readOpts := ReadOptions{} 167 for _, o := range opts { 168 o(&readOpts) 169 } 170 171 prefix := m.prefix(readOpts.Database, readOpts.Table) 172 173 var keys []string 174 175 // Handle Prefix / suffix 176 if readOpts.Prefix || readOpts.Suffix { 177 k := m.list(prefix, 0, 0) 178 limit := int(readOpts.Limit) 179 offset := int(readOpts.Offset) 180 181 if limit > len(k) { 182 limit = len(k) 183 } 184 185 if offset > len(k) { 186 offset = len(k) 187 } 188 189 for i := offset; i < limit; i++ { 190 kk := k[i] 191 192 if readOpts.Prefix && !strings.HasPrefix(kk, key) { 193 continue 194 } 195 196 if readOpts.Suffix && !strings.HasSuffix(kk, key) { 197 continue 198 } 199 200 keys = append(keys, kk) 201 } 202 } else { 203 keys = []string{key} 204 } 205 206 var results []*Record 207 208 for _, k := range keys { 209 r, err := m.get(prefix, k) 210 if err != nil { 211 return results, err 212 } 213 results = append(results, r) 214 } 215 216 return results, nil 217 } 218 219 func (m *memoryStore) Write(r *Record, opts ...WriteOption) error { 220 writeOpts := WriteOptions{} 221 for _, o := range opts { 222 o(&writeOpts) 223 } 224 225 prefix := m.prefix(writeOpts.Database, writeOpts.Table) 226 227 if len(opts) > 0 { 228 // Copy the record before applying options, or the incoming record will be mutated 229 newRecord := Record{} 230 newRecord.Key = r.Key 231 newRecord.Value = make([]byte, len(r.Value)) 232 newRecord.Metadata = make(map[string]interface{}) 233 copy(newRecord.Value, r.Value) 234 newRecord.Expiry = r.Expiry 235 236 if !writeOpts.Expiry.IsZero() { 237 newRecord.Expiry = time.Until(writeOpts.Expiry) 238 } 239 if writeOpts.TTL != 0 { 240 newRecord.Expiry = writeOpts.TTL 241 } 242 243 for k, v := range r.Metadata { 244 newRecord.Metadata[k] = v 245 } 246 247 m.set(prefix, &newRecord) 248 return nil 249 } 250 251 // set 252 m.set(prefix, r) 253 254 return nil 255 } 256 257 func (m *memoryStore) Delete(key string, opts ...DeleteOption) error { 258 deleteOptions := DeleteOptions{} 259 for _, o := range opts { 260 o(&deleteOptions) 261 } 262 263 prefix := m.prefix(deleteOptions.Database, deleteOptions.Table) 264 m.delete(prefix, key) 265 return nil 266 } 267 268 func (m *memoryStore) Options() Options { 269 return m.options 270 } 271 272 func (m *memoryStore) List(opts ...ListOption) ([]string, error) { 273 listOptions := ListOptions{} 274 275 for _, o := range opts { 276 o(&listOptions) 277 } 278 279 prefix := m.prefix(listOptions.Database, listOptions.Table) 280 keys := m.list(prefix, listOptions.Limit, listOptions.Offset) 281 282 if len(listOptions.Prefix) > 0 { 283 var prefixKeys []string 284 for _, k := range keys { 285 if strings.HasPrefix(k, listOptions.Prefix) { 286 prefixKeys = append(prefixKeys, k) 287 } 288 } 289 keys = prefixKeys 290 } 291 292 if len(listOptions.Suffix) > 0 { 293 var suffixKeys []string 294 for _, k := range keys { 295 if strings.HasSuffix(k, listOptions.Suffix) { 296 suffixKeys = append(suffixKeys, k) 297 } 298 } 299 keys = suffixKeys 300 } 301 302 return keys, nil 303 }