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