go.mercari.io/datastore@v1.8.2/dsmiddleware/aememcache/aememcache.go (about) 1 package aememcache 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/gob" 7 "time" 8 9 "go.mercari.io/datastore" 10 "go.mercari.io/datastore/dsmiddleware/storagecache" 11 "google.golang.org/appengine" 12 "google.golang.org/appengine/memcache" 13 ) 14 15 var _ storagecache.Storage = &cacheHandler{} 16 var _ datastore.Middleware = &cacheHandler{} 17 18 // New AE Memcache middleware creates & returns. 19 func New(opts ...CacheOption) interface { 20 datastore.Middleware 21 storagecache.Storage 22 } { 23 ch := &cacheHandler{ 24 stOpts: &storagecache.Options{}, 25 } 26 27 for _, opt := range opts { 28 opt.Apply(ch) 29 } 30 31 s := storagecache.New(ch, ch.stOpts) 32 ch.Middleware = s 33 34 if ch.logf == nil { 35 ch.logf = func(ctx context.Context, format string, args ...interface{}) {} 36 } 37 if ch.cacheKey == nil { 38 ch.cacheKey = func(key datastore.Key) string { 39 return "mercari:aememcache:" + key.Encode() 40 } 41 } 42 43 return ch 44 } 45 46 type cacheHandler struct { 47 datastore.Middleware 48 stOpts *storagecache.Options 49 50 raiseMemcacheError bool 51 expireDuration time.Duration 52 logf func(ctx context.Context, format string, args ...interface{}) 53 cacheKey func(key datastore.Key) string 54 } 55 56 // A CacheOption is an cache option for a AE Memcache middleware. 57 type CacheOption interface { 58 Apply(*cacheHandler) 59 } 60 61 func (ch *cacheHandler) SetMulti(ctx context.Context, cis []*storagecache.CacheItem) error { 62 63 ch.logf(ctx, "dsmiddleware/aememcache.SetMulti: incoming len=%d", len(cis)) 64 65 itemList := make([]*memcache.Item, 0, len(cis)) 66 for _, ci := range cis { 67 if ci.Key.Incomplete() { 68 panic("incomplete key incoming") 69 } 70 var buf bytes.Buffer 71 enc := gob.NewEncoder(&buf) 72 err := enc.Encode(ci.PropertyList) 73 if err != nil { 74 ch.logf(ctx, "dsmiddleware/aememcache.SetMulti: gob.Encode error key=%s err=%s", ci.Key.String(), err.Error()) 75 continue 76 } 77 itemList = append(itemList, &memcache.Item{ 78 Key: ch.cacheKey(ci.Key), 79 Value: buf.Bytes(), 80 Expiration: ch.expireDuration, 81 }) 82 } 83 84 ch.logf(ctx, "dsmiddleware/aememcache.SetMulti: len=%d", len(itemList)) 85 86 err := memcache.SetMulti(ctx, itemList) 87 if err != nil { 88 ch.logf(ctx, "dsmiddleware/aememcache: error on memcache.SetMulti %s", err.Error()) 89 if ch.raiseMemcacheError { 90 if merr, ok := err.(appengine.MultiError); ok { 91 for _, err := range merr { 92 if err == nil || err == memcache.ErrCacheMiss { 93 continue 94 } 95 return merr 96 } 97 } else { 98 return err 99 } 100 } 101 102 keys := make([]string, 0, len(cis)) 103 for _, ci := range cis { 104 keys = append(keys, ci.Key.Encode()) 105 } 106 err = memcache.DeleteMulti(ctx, keys) 107 if err != nil { 108 ch.logf(ctx, "dsmiddleware/aememcache: error on memcache.DeleteMulti %s", err.Error()) 109 if ch.raiseMemcacheError { 110 if merr, ok := err.(appengine.MultiError); ok { 111 for _, err := range merr { 112 if err == nil || err == memcache.ErrCacheMiss { 113 continue 114 } 115 return merr 116 } 117 } else { 118 return err 119 } 120 } 121 } 122 } 123 124 return nil 125 } 126 127 func (ch *cacheHandler) GetMulti(ctx context.Context, keys []datastore.Key) ([]*storagecache.CacheItem, error) { 128 129 ch.logf(ctx, "dsmiddleware/aememcache.GetMulti: incoming len=%d", len(keys)) 130 131 resultList := make([]*storagecache.CacheItem, len(keys)) 132 133 cacheKeys := make([]string, 0, len(keys)) 134 for _, key := range keys { 135 cacheKeys = append(cacheKeys, ch.cacheKey(key)) 136 } 137 138 itemMap, err := memcache.GetMulti(ctx, cacheKeys) 139 140 if err != nil { 141 ch.logf(ctx, "dsmiddleware/aememcache: error on memcache.GetMulti %s", err.Error()) 142 if ch.raiseMemcacheError { 143 if merr, ok := err.(appengine.MultiError); ok { 144 for _, err := range merr { 145 if err == nil || err == memcache.ErrCacheMiss { 146 continue 147 } 148 return nil, datastore.MultiError(merr) 149 } 150 } else { 151 return nil, err 152 } 153 } 154 return resultList, nil 155 } 156 157 hit, miss := 0, 0 158 for idx, key := range keys { 159 item, ok := itemMap[ch.cacheKey(key)] 160 if !ok { 161 resultList[idx] = nil 162 miss++ 163 continue 164 } 165 buf := bytes.NewBuffer(item.Value) 166 dec := gob.NewDecoder(buf) 167 var ps datastore.PropertyList 168 err = dec.Decode(&ps) 169 if err != nil { 170 resultList[idx] = nil 171 ch.logf(ctx, "dsmiddleware/aememcache.GetMulti: gob.Decode error key=%s err=%s", key.String(), err.Error()) 172 miss++ 173 continue 174 } 175 176 resultList[idx] = &storagecache.CacheItem{ 177 Key: key, 178 PropertyList: ps, 179 } 180 hit++ 181 } 182 183 ch.logf(ctx, "dsmiddleware/aememcache.GetMulti: hit=%d miss=%d", hit, miss) 184 185 return resultList, nil 186 } 187 188 func (ch *cacheHandler) DeleteMulti(ctx context.Context, keys []datastore.Key) error { 189 ch.logf(ctx, "dsmiddleware/aememcache.DeleteMulti: incoming len=%d", len(keys)) 190 191 cacheKeys := make([]string, 0, len(keys)) 192 for _, key := range keys { 193 cacheKeys = append(cacheKeys, ch.cacheKey(key)) 194 } 195 196 err := memcache.DeleteMulti(ctx, cacheKeys) 197 if err != nil { 198 ch.logf(ctx, "dsmiddleware/aememcache: error on memcache.DeleteMulti %s", err.Error()) 199 if ch.raiseMemcacheError { 200 if merr, ok := err.(appengine.MultiError); ok { 201 for _, err := range merr { 202 if err == nil || err == memcache.ErrCacheMiss { 203 continue 204 } 205 return merr 206 } 207 } else { 208 return err 209 } 210 } 211 } 212 213 return nil 214 }