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  }