go.mercari.io/datastore@v1.8.2/dsmiddleware/localcache/localcache.go (about) 1 package localcache 2 3 import ( 4 "context" 5 "sync" 6 "time" 7 8 "go.mercari.io/datastore" 9 "go.mercari.io/datastore/dsmiddleware/storagecache" 10 ) 11 12 var _ storagecache.Storage = &cacheHandler{} 13 var _ datastore.Middleware = &cacheHandler{} 14 15 const defaultExpiration = 3 * time.Minute 16 17 // New in-memory localcache middleware creates and returns. 18 func New(opts ...CacheOption) CacheHandler { 19 ch := &cacheHandler{ 20 cache: make(map[string]cacheItem), 21 stOpts: &storagecache.Options{}, 22 } 23 24 for _, opt := range opts { 25 opt.Apply(ch) 26 } 27 28 s := storagecache.New(ch, ch.stOpts) 29 ch.Middleware = s 30 31 if ch.expireDuration == 0 { 32 ch.expireDuration = defaultExpiration 33 } 34 if ch.logf == nil { 35 ch.logf = func(ctx context.Context, format string, args ...interface{}) {} 36 } 37 38 return ch 39 } 40 41 // CacheHandler abstracts cache operations to Storage. 42 type CacheHandler interface { 43 datastore.Middleware 44 storagecache.Storage 45 46 HasCache(key datastore.Key) bool 47 DeleteCache(ctx context.Context, key datastore.Key) 48 CacheKeys() []string 49 CacheLen() int 50 FlushLocalCache() 51 } 52 53 type cacheHandler struct { 54 datastore.Middleware 55 stOpts *storagecache.Options 56 57 cache map[string]cacheItem 58 m sync.Mutex 59 expireDuration time.Duration 60 logf func(ctx context.Context, format string, args ...interface{}) 61 } 62 63 // A CacheOption is an option for cache. 64 type CacheOption interface { 65 Apply(*cacheHandler) 66 } 67 68 type cacheItem struct { 69 Key datastore.Key 70 PropertyList datastore.PropertyList 71 setAt time.Time 72 expiration time.Duration 73 } 74 75 func (ch *cacheHandler) HasCache(key datastore.Key) bool { 76 _, ok := ch.cache[key.Encode()] 77 return ok 78 } 79 80 func (ch *cacheHandler) DeleteCache(ctx context.Context, key datastore.Key) { 81 ch.m.Lock() 82 defer ch.m.Unlock() 83 ch.logf(ctx, "dsmiddleware/localcache.DeleteCache: key=%s", key.String()) 84 delete(ch.cache, key.Encode()) 85 } 86 87 func (ch *cacheHandler) CacheKeys() []string { 88 list := make([]string, 0, len(ch.cache)) 89 for keyStr := range ch.cache { 90 list = append(list, keyStr) 91 } 92 93 return list 94 } 95 96 func (ch *cacheHandler) CacheLen() int { 97 return len(ch.cache) 98 } 99 100 func (ch *cacheHandler) FlushLocalCache() { 101 ch.m.Lock() 102 defer ch.m.Unlock() 103 ch.cache = make(map[string]cacheItem) 104 } 105 106 func (ch *cacheHandler) SetMulti(ctx context.Context, cis []*storagecache.CacheItem) error { 107 ch.m.Lock() 108 defer ch.m.Unlock() 109 110 ch.logf(ctx, "dsmiddleware/localcache.SetMulti: len=%d", len(cis)) 111 for idx, ci := range cis { 112 ch.logf(ctx, "dsmiddleware/localcache.SetMulti: idx=%d key=%s len(ps)=%d", idx, ci.Key.String(), len(ci.PropertyList)) 113 } 114 115 now := time.Now() 116 for _, ci := range cis { 117 if ci.Key.Incomplete() { 118 continue 119 } 120 ch.cache[ci.Key.Encode()] = cacheItem{ 121 Key: ci.Key, 122 PropertyList: ci.PropertyList, 123 setAt: now, 124 expiration: ch.expireDuration, 125 } 126 } 127 128 return nil 129 } 130 131 func (ch *cacheHandler) GetMulti(ctx context.Context, keys []datastore.Key) ([]*storagecache.CacheItem, error) { 132 ch.m.Lock() 133 defer ch.m.Unlock() 134 135 now := time.Now() 136 137 ch.logf(ctx, "dsmiddleware/localcache.GetMulti: len=%d", len(keys)) 138 for idx, key := range keys { 139 ch.logf(ctx, "dsmiddleware/localcache.GetMulti: idx=%d key=%s", idx, key.String()) 140 } 141 142 resultList := make([]*storagecache.CacheItem, len(keys)) 143 for idx, key := range keys { 144 if key.Incomplete() { 145 ch.logf(ctx, "dsmiddleware/localcache.GetMulti: idx=%d, incomplete key=%s", idx, key.String()) 146 continue 147 } 148 cItem, ok := ch.cache[key.Encode()] 149 if !ok { 150 ch.logf(ctx, "dsmiddleware/localcache.GetMulti: idx=%d, missed key=%s", idx, key.String()) 151 continue 152 } 153 154 if cItem.setAt.Add(cItem.expiration).After(now) { 155 ch.logf(ctx, "dsmiddleware/localcache.GetMulti: idx=%d, hit key=%s len(ps)=%d", idx, key.String(), len(cItem.PropertyList)) 156 resultList[idx] = &storagecache.CacheItem{ 157 Key: key, 158 PropertyList: cItem.PropertyList, 159 } 160 } else { 161 ch.logf(ctx, "dsmiddleware/localcache.GetMulti: idx=%d, expired key=%s", idx, key.String()) 162 delete(ch.cache, key.Encode()) 163 } 164 } 165 166 return resultList, nil 167 } 168 169 func (ch *cacheHandler) DeleteMulti(ctx context.Context, keys []datastore.Key) error { 170 ch.m.Lock() 171 defer ch.m.Unlock() 172 173 ch.logf(ctx, "dsmiddleware/localcache.DeleteMulti: len=%d", len(keys)) 174 for idx, key := range keys { 175 ch.logf(ctx, "dsmiddleware/localcache.DeleteMulti: idx=%d key=%s", idx, key.String()) 176 } 177 178 for _, key := range keys { 179 delete(ch.cache, key.Encode()) 180 } 181 182 return nil 183 }