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  }