github.com/gobitfly/go-ethereum@v1.8.12/swarm/storage/localstore.go (about) 1 // Copyright 2016 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 package storage 18 19 import ( 20 "encoding/binary" 21 "fmt" 22 "path/filepath" 23 "sync" 24 25 "github.com/ethereum/go-ethereum/metrics" 26 "github.com/ethereum/go-ethereum/swarm/log" 27 "github.com/ethereum/go-ethereum/swarm/storage/mock" 28 ) 29 30 type LocalStoreParams struct { 31 *StoreParams 32 ChunkDbPath string 33 Validators []ChunkValidator `toml:"-"` 34 } 35 36 func NewDefaultLocalStoreParams() *LocalStoreParams { 37 return &LocalStoreParams{ 38 StoreParams: NewDefaultStoreParams(), 39 } 40 } 41 42 //this can only finally be set after all config options (file, cmd line, env vars) 43 //have been evaluated 44 func (p *LocalStoreParams) Init(path string) { 45 if p.ChunkDbPath == "" { 46 p.ChunkDbPath = filepath.Join(path, "chunks") 47 } 48 } 49 50 // LocalStore is a combination of inmemory db over a disk persisted db 51 // implements a Get/Put with fallback (caching) logic using any 2 ChunkStores 52 type LocalStore struct { 53 Validators []ChunkValidator 54 memStore *MemStore 55 DbStore *LDBStore 56 mu sync.Mutex 57 } 58 59 // This constructor uses MemStore and DbStore as components 60 func NewLocalStore(params *LocalStoreParams, mockStore *mock.NodeStore) (*LocalStore, error) { 61 ldbparams := NewLDBStoreParams(params.StoreParams, params.ChunkDbPath) 62 dbStore, err := NewMockDbStore(ldbparams, mockStore) 63 if err != nil { 64 return nil, err 65 } 66 return &LocalStore{ 67 memStore: NewMemStore(params.StoreParams, dbStore), 68 DbStore: dbStore, 69 Validators: params.Validators, 70 }, nil 71 } 72 73 func NewTestLocalStoreForAddr(params *LocalStoreParams) (*LocalStore, error) { 74 ldbparams := NewLDBStoreParams(params.StoreParams, params.ChunkDbPath) 75 dbStore, err := NewLDBStore(ldbparams) 76 if err != nil { 77 return nil, err 78 } 79 localStore := &LocalStore{ 80 memStore: NewMemStore(params.StoreParams, dbStore), 81 DbStore: dbStore, 82 Validators: params.Validators, 83 } 84 return localStore, nil 85 } 86 87 // Put is responsible for doing validation and storage of the chunk 88 // by using configured ChunkValidators, MemStore and LDBStore. 89 // If the chunk is not valid, its GetErrored function will 90 // return ErrChunkInvalid. 91 // This method will check if the chunk is already in the MemStore 92 // and it will return it if it is. If there is an error from 93 // the MemStore.Get, it will be returned by calling GetErrored 94 // on the chunk. 95 // This method is responsible for closing Chunk.ReqC channel 96 // when the chunk is stored in memstore. 97 // After the LDBStore.Put, it is ensured that the MemStore 98 // contains the chunk with the same data, but nil ReqC channel. 99 func (ls *LocalStore) Put(chunk *Chunk) { 100 if l := len(chunk.SData); l < 9 { 101 log.Debug("incomplete chunk data", "addr", chunk.Addr, "length", l) 102 chunk.SetErrored(ErrChunkInvalid) 103 chunk.markAsStored() 104 return 105 } 106 valid := true 107 for _, v := range ls.Validators { 108 if valid = v.Validate(chunk.Addr, chunk.SData); valid { 109 break 110 } 111 } 112 if !valid { 113 log.Trace("invalid content address", "addr", chunk.Addr) 114 chunk.SetErrored(ErrChunkInvalid) 115 chunk.markAsStored() 116 return 117 } 118 119 log.Trace("localstore.put", "addr", chunk.Addr) 120 121 ls.mu.Lock() 122 defer ls.mu.Unlock() 123 124 chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) 125 126 memChunk, err := ls.memStore.Get(chunk.Addr) 127 switch err { 128 case nil: 129 if memChunk.ReqC == nil { 130 chunk.markAsStored() 131 return 132 } 133 case ErrChunkNotFound: 134 default: 135 chunk.SetErrored(err) 136 return 137 } 138 139 ls.DbStore.Put(chunk) 140 141 // chunk is no longer a request, but a chunk with data, so replace it in memStore 142 newc := NewChunk(chunk.Addr, nil) 143 newc.SData = chunk.SData 144 newc.Size = chunk.Size 145 newc.dbStoredC = chunk.dbStoredC 146 147 ls.memStore.Put(newc) 148 149 if memChunk != nil && memChunk.ReqC != nil { 150 close(memChunk.ReqC) 151 } 152 } 153 154 // Get(chunk *Chunk) looks up a chunk in the local stores 155 // This method is blocking until the chunk is retrieved 156 // so additional timeout may be needed to wrap this call if 157 // ChunkStores are remote and can have long latency 158 func (ls *LocalStore) Get(addr Address) (chunk *Chunk, err error) { 159 ls.mu.Lock() 160 defer ls.mu.Unlock() 161 162 return ls.get(addr) 163 } 164 165 func (ls *LocalStore) get(addr Address) (chunk *Chunk, err error) { 166 chunk, err = ls.memStore.Get(addr) 167 if err == nil { 168 if chunk.ReqC != nil { 169 select { 170 case <-chunk.ReqC: 171 default: 172 metrics.GetOrRegisterCounter("localstore.get.errfetching", nil).Inc(1) 173 return chunk, ErrFetching 174 } 175 } 176 metrics.GetOrRegisterCounter("localstore.get.cachehit", nil).Inc(1) 177 return 178 } 179 metrics.GetOrRegisterCounter("localstore.get.cachemiss", nil).Inc(1) 180 chunk, err = ls.DbStore.Get(addr) 181 if err != nil { 182 metrics.GetOrRegisterCounter("localstore.get.error", nil).Inc(1) 183 return 184 } 185 chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) 186 ls.memStore.Put(chunk) 187 return 188 } 189 190 // retrieve logic common for local and network chunk retrieval requests 191 func (ls *LocalStore) GetOrCreateRequest(addr Address) (chunk *Chunk, created bool) { 192 metrics.GetOrRegisterCounter("localstore.getorcreaterequest", nil).Inc(1) 193 194 ls.mu.Lock() 195 defer ls.mu.Unlock() 196 197 var err error 198 chunk, err = ls.get(addr) 199 if err == nil && chunk.GetErrored() == nil { 200 metrics.GetOrRegisterCounter("localstore.getorcreaterequest.hit", nil).Inc(1) 201 log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v found locally", addr)) 202 return chunk, false 203 } 204 if err == ErrFetching && chunk.GetErrored() == nil { 205 metrics.GetOrRegisterCounter("localstore.getorcreaterequest.errfetching", nil).Inc(1) 206 log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v hit on an existing request %v", addr, chunk.ReqC)) 207 return chunk, false 208 } 209 // no data and no request status 210 metrics.GetOrRegisterCounter("localstore.getorcreaterequest.miss", nil).Inc(1) 211 log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v not found locally. open new request", addr)) 212 chunk = NewChunk(addr, make(chan bool)) 213 ls.memStore.Put(chunk) 214 return chunk, true 215 } 216 217 // RequestsCacheLen returns the current number of outgoing requests stored in the cache 218 func (ls *LocalStore) RequestsCacheLen() int { 219 return ls.memStore.requests.Len() 220 } 221 222 // Close the local store 223 func (ls *LocalStore) Close() { 224 ls.DbStore.Close() 225 }