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  }