github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/swarm/storage/netstore.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 "context" 21 "encoding/hex" 22 "fmt" 23 "sync" 24 "sync/atomic" 25 "time" 26 27 "github.com/ethereum/go-ethereum/p2p/enode" 28 "github.com/ethereum/go-ethereum/swarm/log" 29 "github.com/ethereum/go-ethereum/swarm/spancontext" 30 "github.com/opentracing/opentracing-go" 31 olog "github.com/opentracing/opentracing-go/log" 32 "github.com/syndtr/goleveldb/leveldb" 33 34 lru "github.com/hashicorp/golang-lru" 35 ) 36 37 type ( 38 NewNetFetcherFunc func(ctx context.Context, addr Address, peers *sync.Map) NetFetcher 39 ) 40 41 type NetFetcher interface { 42 Request(hopCount uint8) 43 Offer(source *enode.ID) 44 } 45 46 // NetStore is an extension of local storage 47 // it implements the ChunkStore interface 48 // on request it initiates remote cloud retrieval using a fetcher 49 // fetchers are unique to a chunk and are stored in fetchers LRU memory cache 50 // fetchFuncFactory is a factory object to create a fetch function for a specific chunk address 51 type NetStore struct { 52 mu sync.Mutex 53 store SyncChunkStore 54 fetchers *lru.Cache 55 NewNetFetcherFunc NewNetFetcherFunc 56 closeC chan struct{} 57 } 58 59 var fetcherTimeout = 2 * time.Minute // timeout to cancel the fetcher even if requests are coming in 60 61 // NewNetStore creates a new NetStore object using the given local store. newFetchFunc is a 62 // constructor function that can create a fetch function for a specific chunk address. 63 func NewNetStore(store SyncChunkStore, nnf NewNetFetcherFunc) (*NetStore, error) { 64 fetchers, err := lru.New(defaultChunkRequestsCacheCapacity) 65 if err != nil { 66 return nil, err 67 } 68 return &NetStore{ 69 store: store, 70 fetchers: fetchers, 71 NewNetFetcherFunc: nnf, 72 closeC: make(chan struct{}), 73 }, nil 74 } 75 76 // Put stores a chunk in localstore, and delivers to all requestor peers using the fetcher stored in 77 // the fetchers cache 78 func (n *NetStore) Put(ctx context.Context, ch Chunk) error { 79 n.mu.Lock() 80 defer n.mu.Unlock() 81 82 // put to the chunk to the store, there should be no error 83 err := n.store.Put(ctx, ch) 84 if err != nil { 85 return err 86 } 87 88 // if chunk is now put in the store, check if there was an active fetcher and call deliver on it 89 // (this delivers the chunk to requestors via the fetcher) 90 log.Trace("n.getFetcher", "ref", ch.Address()) 91 if f := n.getFetcher(ch.Address()); f != nil { 92 log.Trace("n.getFetcher deliver", "ref", ch.Address()) 93 f.deliver(ctx, ch) 94 } 95 return nil 96 } 97 98 // Get retrieves the chunk from the NetStore DPA synchronously. 99 // It calls NetStore.get, and if the chunk is not in local Storage 100 // it calls fetch with the request, which blocks until the chunk 101 // arrived or context is done 102 func (n *NetStore) Get(rctx context.Context, ref Address) (Chunk, error) { 103 chunk, fetch, err := n.get(rctx, ref) 104 if err != nil { 105 return nil, err 106 } 107 if chunk != nil { 108 // this is not measuring how long it takes to get the chunk for the localstore, but 109 // rather just adding a span for clarity when inspecting traces in Jaeger, in order 110 // to make it easier to reason which is the node that actually delivered a chunk. 111 _, sp := spancontext.StartSpan( 112 rctx, 113 "localstore.get") 114 defer sp.Finish() 115 116 return chunk, nil 117 } 118 return fetch(rctx) 119 } 120 121 func (n *NetStore) BinIndex(po uint8) uint64 { 122 return n.store.BinIndex(po) 123 } 124 125 func (n *NetStore) Iterator(from uint64, to uint64, po uint8, f func(Address, uint64) bool) error { 126 return n.store.Iterator(from, to, po, f) 127 } 128 129 // FetchFunc returns nil if the store contains the given address. Otherwise it returns a wait function, 130 // which returns after the chunk is available or the context is done 131 func (n *NetStore) FetchFunc(ctx context.Context, ref Address) func(context.Context) error { 132 chunk, fetch, _ := n.get(ctx, ref) 133 if chunk != nil { 134 return nil 135 } 136 return func(ctx context.Context) error { 137 _, err := fetch(ctx) 138 return err 139 } 140 } 141 142 // Close chunk store 143 func (n *NetStore) Close() { 144 close(n.closeC) 145 n.store.Close() 146 147 wg := sync.WaitGroup{} 148 for _, key := range n.fetchers.Keys() { 149 if f, ok := n.fetchers.Get(key); ok { 150 if fetch, ok := f.(*fetcher); ok { 151 wg.Add(1) 152 go func(fetch *fetcher) { 153 defer wg.Done() 154 fetch.cancel() 155 156 select { 157 case <-fetch.deliveredC: 158 case <-fetch.cancelledC: 159 } 160 }(fetch) 161 } 162 } 163 } 164 wg.Wait() 165 } 166 167 // get attempts at retrieving the chunk from LocalStore 168 // If it is not found then using getOrCreateFetcher: 169 // 1. Either there is already a fetcher to retrieve it 170 // 2. A new fetcher is created and saved in the fetchers cache 171 // From here on, all Get will hit on this fetcher until the chunk is delivered 172 // or all fetcher contexts are done. 173 // It returns a chunk, a fetcher function and an error 174 // If chunk is nil, the returned fetch function needs to be called with a context to return the chunk. 175 func (n *NetStore) get(ctx context.Context, ref Address) (Chunk, func(context.Context) (Chunk, error), error) { 176 n.mu.Lock() 177 defer n.mu.Unlock() 178 179 chunk, err := n.store.Get(ctx, ref) 180 if err != nil { 181 // TODO: Fix comparison - we should be comparing against leveldb.ErrNotFound, this error should be wrapped. 182 if err != ErrChunkNotFound && err != leveldb.ErrNotFound { 183 log.Debug("Received error from LocalStore other than ErrNotFound", "err", err) 184 } 185 // The chunk is not available in the LocalStore, let's get the fetcher for it, or create a new one 186 // if it doesn't exist yet 187 f := n.getOrCreateFetcher(ctx, ref) 188 // If the caller needs the chunk, it has to use the returned fetch function to get it 189 return nil, f.Fetch, nil 190 } 191 192 return chunk, nil, nil 193 } 194 195 // Has is the storage layer entry point to query the underlying 196 // database to return if it has a chunk or not. 197 // Called from the DebugAPI 198 func (n *NetStore) Has(ctx context.Context, ref Address) bool { 199 return n.store.Has(ctx, ref) 200 } 201 202 // getOrCreateFetcher attempts at retrieving an existing fetchers 203 // if none exists, creates one and saves it in the fetchers cache 204 // caller must hold the lock 205 func (n *NetStore) getOrCreateFetcher(ctx context.Context, ref Address) *fetcher { 206 if f := n.getFetcher(ref); f != nil { 207 return f 208 } 209 210 // no fetcher for the given address, we have to create a new one 211 key := hex.EncodeToString(ref) 212 // create the context during which fetching is kept alive 213 cctx, cancel := context.WithTimeout(ctx, fetcherTimeout) 214 // destroy is called when all requests finish 215 destroy := func() { 216 // remove fetcher from fetchers 217 n.fetchers.Remove(key) 218 // stop fetcher by cancelling context called when 219 // all requests cancelled/timedout or chunk is delivered 220 cancel() 221 } 222 // peers always stores all the peers which have an active request for the chunk. It is shared 223 // between fetcher and the NewFetchFunc function. It is needed by the NewFetchFunc because 224 // the peers which requested the chunk should not be requested to deliver it. 225 peers := &sync.Map{} 226 227 cctx, sp := spancontext.StartSpan( 228 cctx, 229 "netstore.fetcher", 230 ) 231 232 sp.LogFields(olog.String("ref", ref.String())) 233 fetcher := newFetcher(sp, ref, n.NewNetFetcherFunc(cctx, ref, peers), destroy, peers, n.closeC) 234 n.fetchers.Add(key, fetcher) 235 236 return fetcher 237 } 238 239 // getFetcher retrieves the fetcher for the given address from the fetchers cache if it exists, 240 // otherwise it returns nil 241 func (n *NetStore) getFetcher(ref Address) *fetcher { 242 key := hex.EncodeToString(ref) 243 f, ok := n.fetchers.Get(key) 244 if ok { 245 return f.(*fetcher) 246 } 247 return nil 248 } 249 250 // RequestsCacheLen returns the current number of outgoing requests stored in the cache 251 func (n *NetStore) RequestsCacheLen() int { 252 return n.fetchers.Len() 253 } 254 255 // One fetcher object is responsible to fetch one chunk for one address, and keep track of all the 256 // peers who have requested it and did not receive it yet. 257 type fetcher struct { 258 addr Address // address of chunk 259 chunk Chunk // fetcher can set the chunk on the fetcher 260 deliveredC chan struct{} // chan signalling chunk delivery to requests 261 cancelledC chan struct{} // chan signalling the fetcher has been cancelled (removed from fetchers in NetStore) 262 netFetcher NetFetcher // remote fetch function to be called with a request source taken from the context 263 cancel func() // cleanup function for the remote fetcher to call when all upstream contexts are called 264 peers *sync.Map // the peers which asked for the chunk 265 requestCnt int32 // number of requests on this chunk. If all the requests are done (delivered or context is done) the cancel function is called 266 deliverOnce *sync.Once // guarantees that we only close deliveredC once 267 span opentracing.Span // measure retrieve time per chunk 268 } 269 270 // newFetcher creates a new fetcher object for the fiven addr. fetch is the function which actually 271 // does the retrieval (in non-test cases this is coming from the network package). cancel function is 272 // called either 273 // 1. when the chunk has been fetched all peers have been either notified or their context has been done 274 // 2. the chunk has not been fetched but all context from all the requests has been done 275 // The peers map stores all the peers which have requested chunk. 276 func newFetcher(span opentracing.Span, addr Address, nf NetFetcher, cancel func(), peers *sync.Map, closeC chan struct{}) *fetcher { 277 cancelOnce := &sync.Once{} // cancel should only be called once 278 return &fetcher{ 279 addr: addr, 280 deliveredC: make(chan struct{}), 281 deliverOnce: &sync.Once{}, 282 cancelledC: closeC, 283 netFetcher: nf, 284 cancel: func() { 285 cancelOnce.Do(func() { 286 cancel() 287 }) 288 }, 289 peers: peers, 290 span: span, 291 } 292 } 293 294 // Fetch fetches the chunk synchronously, it is called by NetStore.Get is the chunk is not available 295 // locally. 296 func (f *fetcher) Fetch(rctx context.Context) (Chunk, error) { 297 atomic.AddInt32(&f.requestCnt, 1) 298 defer func() { 299 // if all the requests are done the fetcher can be cancelled 300 if atomic.AddInt32(&f.requestCnt, -1) == 0 { 301 f.cancel() 302 } 303 f.span.Finish() 304 }() 305 306 // The peer asking for the chunk. Store in the shared peers map, but delete after the request 307 // has been delivered 308 peer := rctx.Value("peer") 309 if peer != nil { 310 f.peers.Store(peer, time.Now()) 311 defer f.peers.Delete(peer) 312 } 313 314 // If there is a source in the context then it is an offer, otherwise a request 315 sourceIF := rctx.Value("source") 316 317 hopCount, _ := rctx.Value("hopcount").(uint8) 318 319 if sourceIF != nil { 320 var source enode.ID 321 if err := source.UnmarshalText([]byte(sourceIF.(string))); err != nil { 322 return nil, err 323 } 324 f.netFetcher.Offer(&source) 325 } else { 326 f.netFetcher.Request(hopCount) 327 } 328 329 // wait until either the chunk is delivered or the context is done 330 select { 331 case <-rctx.Done(): 332 return nil, rctx.Err() 333 case <-f.deliveredC: 334 return f.chunk, nil 335 case <-f.cancelledC: 336 return nil, fmt.Errorf("fetcher cancelled") 337 } 338 } 339 340 // deliver is called by NetStore.Put to notify all pending requests 341 func (f *fetcher) deliver(ctx context.Context, ch Chunk) { 342 f.deliverOnce.Do(func() { 343 f.chunk = ch 344 // closing the deliveredC channel will terminate ongoing requests 345 close(f.deliveredC) 346 log.Trace("n.getFetcher close deliveredC", "ref", ch.Address()) 347 }) 348 }