github.com/annwntech/go-micro/v2@v2.9.5/store/cache/cache.go (about)

     1  // Package cache implements a faulting style read cache on top of multiple micro stores
     2  package cache
     3  
     4  import (
     5  	"fmt"
     6  
     7  	"github.com/annwntech/go-micro/v2/store"
     8  	"github.com/annwntech/go-micro/v2/store/memory"
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  type cache struct {
    13  	stores []store.Store
    14  }
    15  
    16  // Cache is a cpu register style cache for the store.
    17  // It syncs between N stores in a faulting manner.
    18  type Cache interface {
    19  	// Implements the store interface
    20  	store.Store
    21  }
    22  
    23  // NewCache returns a new store using the underlying stores, which must be already Init()ialised
    24  func NewCache(stores ...store.Store) Cache {
    25  	if len(stores) == 0 {
    26  		stores = []store.Store{
    27  			memory.NewStore(),
    28  		}
    29  	}
    30  
    31  	// TODO: build in an in memory cache
    32  	c := &cache{
    33  		stores: stores,
    34  	}
    35  
    36  	return c
    37  }
    38  
    39  func (c *cache) Close() error {
    40  	return nil
    41  }
    42  
    43  func (c *cache) Init(opts ...store.Option) error {
    44  	// pass to the stores
    45  	for _, store := range c.stores {
    46  		if err := store.Init(opts...); err != nil {
    47  			return err
    48  		}
    49  	}
    50  	return nil
    51  }
    52  
    53  func (c *cache) Options() store.Options {
    54  	// return from first store
    55  	return c.stores[0].Options()
    56  }
    57  
    58  func (c *cache) String() string {
    59  	stores := make([]string, len(c.stores))
    60  	for i, s := range c.stores {
    61  		stores[i] = s.String()
    62  	}
    63  	return fmt.Sprintf("cache %v", stores)
    64  }
    65  
    66  func (c *cache) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) {
    67  	readOpts := store.ReadOptions{}
    68  	for _, o := range opts {
    69  		o(&readOpts)
    70  	}
    71  
    72  	if readOpts.Prefix || readOpts.Suffix {
    73  		// List, then try cached gets for each key
    74  		var lOpts []store.ListOption
    75  		if readOpts.Prefix {
    76  			lOpts = append(lOpts, store.ListPrefix(key))
    77  		}
    78  		if readOpts.Suffix {
    79  			lOpts = append(lOpts, store.ListSuffix(key))
    80  		}
    81  		if readOpts.Limit > 0 {
    82  			lOpts = append(lOpts, store.ListLimit(readOpts.Limit))
    83  		}
    84  		if readOpts.Offset > 0 {
    85  			lOpts = append(lOpts, store.ListOffset(readOpts.Offset))
    86  		}
    87  		keys, err := c.List(lOpts...)
    88  		if err != nil {
    89  			return []*store.Record{}, errors.Wrap(err, "cache.List failed")
    90  		}
    91  		recs := make([]*store.Record, len(keys))
    92  		for i, k := range keys {
    93  			r, err := c.readOne(k, opts...)
    94  			if err != nil {
    95  				return recs, errors.Wrap(err, "cache.readOne failed")
    96  			}
    97  			recs[i] = r
    98  		}
    99  		return recs, nil
   100  	}
   101  
   102  	// Otherwise just try cached get
   103  	r, err := c.readOne(key, opts...)
   104  	if err != nil {
   105  		return []*store.Record{}, err // preserve store.ErrNotFound
   106  	}
   107  	return []*store.Record{r}, nil
   108  }
   109  
   110  func (c *cache) readOne(key string, opts ...store.ReadOption) (*store.Record, error) {
   111  	for i, s := range c.stores {
   112  		// ReadOne ignores all options
   113  		r, err := s.Read(key)
   114  		if err == nil {
   115  			if len(r) > 1 {
   116  				return nil, errors.Wrapf(err, "read from L%d cache (%s) returned multiple records", i, c.stores[i].String())
   117  			}
   118  			for j := i - 1; j >= 0; j-- {
   119  				err := c.stores[j].Write(r[0])
   120  				if err != nil {
   121  					return nil, errors.Wrapf(err, "could not write to L%d cache (%s)", j, c.stores[j].String())
   122  				}
   123  			}
   124  			return r[0], nil
   125  		}
   126  	}
   127  	return nil, store.ErrNotFound
   128  }
   129  
   130  func (c *cache) Write(r *store.Record, opts ...store.WriteOption) error {
   131  	// Write to all layers in reverse
   132  	for i := len(c.stores) - 1; i >= 0; i-- {
   133  		if err := c.stores[i].Write(r, opts...); err != nil {
   134  			return errors.Wrapf(err, "could not write to L%d cache (%s)", i, c.stores[i].String())
   135  		}
   136  	}
   137  	return nil
   138  }
   139  
   140  func (c *cache) Delete(key string, opts ...store.DeleteOption) error {
   141  	for i, s := range c.stores {
   142  		if err := s.Delete(key, opts...); err != nil {
   143  			return errors.Wrapf(err, "could not delete from L%d cache (%s)", i, c.stores[i].String())
   144  		}
   145  	}
   146  	return nil
   147  }
   148  
   149  func (c *cache) List(opts ...store.ListOption) ([]string, error) {
   150  	// List only makes sense from the top level
   151  	return c.stores[len(c.stores)-1].List(opts...)
   152  }