github.com/ethersphere/bee/v2@v2.2.0/pkg/storage/cache/cache.go (about) 1 // Copyright 2023 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package cache 6 7 import ( 8 "github.com/ethersphere/bee/v2/pkg/storage" 9 "github.com/ethersphere/bee/v2/pkg/storage/storageutil" 10 lru "github.com/hashicorp/golang-lru/v2" 11 ) 12 13 // key returns a string representation of the given key. 14 func key(key storage.Key) string { 15 return storageutil.JoinFields(key.Namespace(), key.ID()) 16 } 17 18 var _ storage.IndexStore = (*Cache)(nil) 19 20 // Cache is a wrapper around a storage.Store that adds a layer 21 // of in-memory caching for the Get and Has operations. 22 type Cache struct { 23 storage.IndexStore 24 25 lru *lru.Cache[string, []byte] 26 metrics metrics 27 } 28 29 // Wrap adds a layer of in-memory caching to storage.Reader Get and Has operations. 30 // It returns an error if the capacity is less than or equal to zero or if the 31 // given store implements storage.Tx 32 func Wrap(store storage.IndexStore, capacity int) (*Cache, error) { 33 lru, err := lru.New[string, []byte](capacity) 34 if err != nil { 35 return nil, err 36 } 37 38 return &Cache{store, lru, newMetrics()}, nil 39 } 40 41 // add caches given item. 42 func (c *Cache) add(i storage.Item) { 43 b, err := i.Marshal() 44 if err != nil { 45 return 46 } 47 c.lru.Add(key(i), b) 48 } 49 50 // Get implements storage.Store interface. 51 // On a call it tries to first retrieve the item from cache. 52 // If the item does not exist in cache, it tries to retrieve 53 // it from the underlying store. 54 func (c *Cache) Get(i storage.Item) error { 55 if val, ok := c.lru.Get(key(i)); ok { 56 c.metrics.CacheHit.Inc() 57 return i.Unmarshal(val) 58 } 59 60 if err := c.IndexStore.Get(i); err != nil { 61 return err 62 } 63 64 c.metrics.CacheMiss.Inc() 65 c.add(i) 66 67 return nil 68 } 69 70 // Has implements storage.Store interface. 71 // On a call it tries to first retrieve the item from cache. 72 // If the item does not exist in cache, it tries to retrieve 73 // it from the underlying store. 74 func (c *Cache) Has(k storage.Key) (bool, error) { 75 if _, ok := c.lru.Get(key(k)); ok { 76 c.metrics.CacheHit.Inc() 77 return true, nil 78 } 79 80 c.metrics.CacheMiss.Inc() 81 return c.IndexStore.Has(k) 82 } 83 84 // Put implements storage.Store interface. 85 // On a call it also inserts the item into the cache so that the next 86 // call to Put and Has will be able to retrieve the item from cache. 87 func (c *Cache) Put(i storage.Item) error { 88 c.add(i) 89 return c.IndexStore.Put(i) 90 } 91 92 // Delete implements storage.Store interface. 93 // On a call it also removes the item from the cache. 94 func (c *Cache) Delete(i storage.Item) error { 95 _ = c.lru.Remove(key(i)) 96 return c.IndexStore.Delete(i) 97 } 98 99 func (c *Cache) Close() error { 100 c.lru.Purge() 101 return nil 102 }