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 }