github.com/divan/go-ethereum@v1.8.14-0.20180820134928-1de9ada4016d/swarm/storage/memstore.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // memory storage layer for the package blockhash 18 19 package storage 20 21 import ( 22 "context" 23 "sync" 24 25 lru "github.com/hashicorp/golang-lru" 26 ) 27 28 type MemStore struct { 29 cache *lru.Cache 30 requests *lru.Cache 31 mu sync.RWMutex 32 disabled bool 33 } 34 35 //NewMemStore is instantiating a MemStore cache. We are keeping a record of all outgoing requests for chunks, that 36 //should later be delivered by peer nodes, in the `requests` LRU cache. We are also keeping all frequently requested 37 //chunks in the `cache` LRU cache. 38 // 39 //`requests` LRU cache capacity should ideally never be reached, this is why for the time being it should be initialised 40 //with the same value as the LDBStore capacity. 41 func NewMemStore(params *StoreParams, _ *LDBStore) (m *MemStore) { 42 if params.CacheCapacity == 0 { 43 return &MemStore{ 44 disabled: true, 45 } 46 } 47 48 onEvicted := func(key interface{}, value interface{}) { 49 v := value.(*Chunk) 50 <-v.dbStoredC 51 } 52 c, err := lru.NewWithEvict(int(params.CacheCapacity), onEvicted) 53 if err != nil { 54 panic(err) 55 } 56 57 requestEvicted := func(key interface{}, value interface{}) { 58 // temporary remove of the error log, until we figure out the problem, as it is too spamy 59 //log.Error("evict called on outgoing request") 60 } 61 r, err := lru.NewWithEvict(int(params.ChunkRequestsCacheCapacity), requestEvicted) 62 if err != nil { 63 panic(err) 64 } 65 66 return &MemStore{ 67 cache: c, 68 requests: r, 69 } 70 } 71 72 func (m *MemStore) Get(ctx context.Context, addr Address) (*Chunk, error) { 73 if m.disabled { 74 return nil, ErrChunkNotFound 75 } 76 77 m.mu.RLock() 78 defer m.mu.RUnlock() 79 80 r, ok := m.requests.Get(string(addr)) 81 // it is a request 82 if ok { 83 return r.(*Chunk), nil 84 } 85 86 // it is not a request 87 c, ok := m.cache.Get(string(addr)) 88 if !ok { 89 return nil, ErrChunkNotFound 90 } 91 return c.(*Chunk), nil 92 } 93 94 func (m *MemStore) Put(ctx context.Context, c *Chunk) { 95 if m.disabled { 96 return 97 } 98 99 m.mu.Lock() 100 defer m.mu.Unlock() 101 102 // it is a request 103 if c.ReqC != nil { 104 select { 105 case <-c.ReqC: 106 if c.GetErrored() != nil { 107 m.requests.Remove(string(c.Addr)) 108 return 109 } 110 m.cache.Add(string(c.Addr), c) 111 m.requests.Remove(string(c.Addr)) 112 default: 113 m.requests.Add(string(c.Addr), c) 114 } 115 return 116 } 117 118 // it is not a request 119 m.cache.Add(string(c.Addr), c) 120 m.requests.Remove(string(c.Addr)) 121 } 122 123 func (m *MemStore) setCapacity(n int) { 124 if n <= 0 { 125 m.disabled = true 126 } else { 127 onEvicted := func(key interface{}, value interface{}) { 128 v := value.(*Chunk) 129 <-v.dbStoredC 130 } 131 c, err := lru.NewWithEvict(n, onEvicted) 132 if err != nil { 133 panic(err) 134 } 135 136 r, err := lru.New(defaultChunkRequestsCacheCapacity) 137 if err != nil { 138 panic(err) 139 } 140 141 m = &MemStore{ 142 cache: c, 143 requests: r, 144 } 145 } 146 } 147 148 func (s *MemStore) Close() {}