github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/lru/lru.go (about) 1 package lru 2 3 import ( 4 json "encoding/json" 5 "reflect" 6 "sync" 7 8 lru "github.com/hashicorp/golang-lru" 9 libkb "github.com/keybase/client/go/libkb" 10 keybase1 "github.com/keybase/client/go/protocol/keybase1" 11 jsonw "github.com/keybase/go-jsonw" 12 context "golang.org/x/net/context" 13 ) 14 15 type Cache struct { 16 sync.Mutex 17 mem *lru.Cache 18 version int 19 typ reflect.Type 20 stats stats 21 } 22 23 type stats struct { 24 memHit int 25 diskHit int 26 diskStale int 27 miss int 28 } 29 30 func NewLRU(ctx libkb.LRUContext, sz int, version int, exampleObj interface{}) *Cache { 31 cache, err := lru.New(sz) 32 if err != nil { 33 ctx.GetLog().Fatalf("Bad LRU constructor: %s", err.Error()) 34 } 35 return &Cache{ 36 mem: cache, 37 version: version, 38 typ: reflect.TypeOf(exampleObj), 39 } 40 } 41 42 type diskWrapper struct { 43 Version int `codec:"v"` 44 Data string `codex:"d"` 45 CachedAt keybase1.Time `codec:"t"` 46 } 47 48 func (c *Cache) ClearMemory() { 49 c.mem.Purge() 50 } 51 52 func (c *Cache) Get(ctx context.Context, lctx libkb.LRUContext, k libkb.LRUKeyer) (interface{}, error) { 53 c.Lock() 54 defer c.Unlock() 55 val, ok := c.mem.Get(k.MemKey()) 56 if ok { 57 c.stats.memHit++ 58 lctx.GetVDebugLog().CLogf(ctx, libkb.VLog3, "lru(%v): mem hit", k.MemKey()) 59 return val, nil 60 } 61 var w diskWrapper 62 ok, err := lctx.GetKVStore().GetInto(&w, k.DbKey()) 63 if err != nil { 64 return nil, err 65 } 66 if !ok { 67 c.stats.miss++ 68 lctx.GetVDebugLog().CLogf(ctx, libkb.VLog3, "lru(%v): miss", k.DbKey()) 69 return nil, nil 70 } 71 if w.Version != c.version { 72 c.stats.diskStale++ 73 lctx.GetVDebugLog().CLogf(ctx, libkb.VLog0, "lru(%v), old version: %d < %d", k.DbKey(), w.Version, c.version) 74 return nil, nil 75 } 76 var ret interface{} 77 if len(w.Data) > 0 { 78 tmp := reflect.New(c.typ) 79 ret = tmp.Interface() 80 81 if err = jsonw.EnsureMaxDepthBytesDefault([]byte(w.Data)); err != nil { 82 return nil, err 83 } 84 85 if err = json.Unmarshal([]byte(w.Data), ret); err != nil { 86 return nil, err 87 } 88 } 89 c.stats.diskHit++ 90 lctx.GetVDebugLog().CLogf(ctx, libkb.VLog3, "lru(%v): disk hit", k.DbKey()) 91 c.mem.Add(k.MemKey(), ret) 92 return ret, nil 93 } 94 95 func (c *Cache) Put(ctx context.Context, lctx libkb.LRUContext, k libkb.LRUKeyer, v interface{}) error { 96 c.Lock() 97 defer c.Unlock() 98 var data string 99 if v != nil { 100 b, err := json.Marshal(v) 101 if err != nil { 102 return err 103 } 104 data = string(b) 105 } 106 w := diskWrapper{ 107 Version: c.version, 108 Data: data, 109 CachedAt: keybase1.ToTime(lctx.GetClock().Now()), 110 } 111 err := lctx.GetKVStore().PutObj(k.DbKey(), nil, w) 112 if err != nil { 113 return err 114 } 115 c.mem.Add(k.MemKey(), v) 116 return nil 117 } 118 119 func (c *Cache) OnLogout(mctx libkb.MetaContext) error { 120 c.ClearMemory() 121 return nil 122 } 123 124 func (c *Cache) OnDbNuke(mctx libkb.MetaContext) error { 125 c.ClearMemory() 126 return nil 127 } 128 129 var _ libkb.LRUer = (*Cache)(nil)