github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/exchange/bitswap/bitswap.go (about)

     1  // package bitswap implements the IPFS Exchange interface with the BitSwap
     2  // bilateral exchange protocol.
     3  package bitswap
     4  
     5  import (
     6  	"errors"
     7  	"math"
     8  	"sync"
     9  	"time"
    10  
    11  	process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
    12  	procctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
    13  	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
    14  	blocks "github.com/ipfs/go-ipfs/blocks"
    15  	blockstore "github.com/ipfs/go-ipfs/blocks/blockstore"
    16  	key "github.com/ipfs/go-ipfs/blocks/key"
    17  	exchange "github.com/ipfs/go-ipfs/exchange"
    18  	decision "github.com/ipfs/go-ipfs/exchange/bitswap/decision"
    19  	bsmsg "github.com/ipfs/go-ipfs/exchange/bitswap/message"
    20  	bsnet "github.com/ipfs/go-ipfs/exchange/bitswap/network"
    21  	notifications "github.com/ipfs/go-ipfs/exchange/bitswap/notifications"
    22  	wantlist "github.com/ipfs/go-ipfs/exchange/bitswap/wantlist"
    23  	peer "github.com/ipfs/go-ipfs/p2p/peer"
    24  	"github.com/ipfs/go-ipfs/thirdparty/delay"
    25  	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
    26  )
    27  
    28  var log = eventlog.Logger("bitswap")
    29  
    30  const (
    31  	// maxProvidersPerRequest specifies the maximum number of providers desired
    32  	// from the network. This value is specified because the network streams
    33  	// results.
    34  	// TODO: if a 'non-nice' strategy is implemented, consider increasing this value
    35  	maxProvidersPerRequest = 3
    36  	providerRequestTimeout = time.Second * 10
    37  	hasBlockTimeout        = time.Second * 15
    38  	provideTimeout         = time.Second * 15
    39  	sizeBatchRequestChan   = 32
    40  	// kMaxPriority is the max priority as defined by the bitswap protocol
    41  	kMaxPriority = math.MaxInt32
    42  
    43  	HasBlockBufferSize    = 256
    44  	provideKeysBufferSize = 2048
    45  	provideWorkerMax      = 512
    46  )
    47  
    48  var rebroadcastDelay = delay.Fixed(time.Second * 10)
    49  
    50  // New initializes a BitSwap instance that communicates over the provided
    51  // BitSwapNetwork. This function registers the returned instance as the network
    52  // delegate.
    53  // Runs until context is cancelled.
    54  func New(parent context.Context, p peer.ID, network bsnet.BitSwapNetwork,
    55  	bstore blockstore.Blockstore, nice bool) exchange.Interface {
    56  
    57  	// important to use provided parent context (since it may include important
    58  	// loggable data). It's probably not a good idea to allow bitswap to be
    59  	// coupled to the concerns of the IPFS daemon in this way.
    60  	//
    61  	// FIXME(btc) Now that bitswap manages itself using a process, it probably
    62  	// shouldn't accept a context anymore. Clients should probably use Close()
    63  	// exclusively. We should probably find another way to share logging data
    64  	ctx, cancelFunc := context.WithCancel(parent)
    65  
    66  	notif := notifications.New()
    67  	px := process.WithTeardown(func() error {
    68  		notif.Shutdown()
    69  		return nil
    70  	})
    71  
    72  	bs := &Bitswap{
    73  		self:          p,
    74  		blockstore:    bstore,
    75  		notifications: notif,
    76  		engine:        decision.NewEngine(ctx, bstore), // TODO close the engine with Close() method
    77  		network:       network,
    78  		findKeys:      make(chan *blockRequest, sizeBatchRequestChan),
    79  		process:       px,
    80  		newBlocks:     make(chan *blocks.Block, HasBlockBufferSize),
    81  		provideKeys:   make(chan key.Key, provideKeysBufferSize),
    82  		wm:            NewWantManager(ctx, network),
    83  	}
    84  	go bs.wm.Run()
    85  	network.SetDelegate(bs)
    86  
    87  	// Start up bitswaps async worker routines
    88  	bs.startWorkers(px, ctx)
    89  
    90  	// bind the context and process.
    91  	// do it over here to avoid closing before all setup is done.
    92  	go func() {
    93  		<-px.Closing() // process closes first
    94  		cancelFunc()
    95  	}()
    96  	procctx.CloseAfterContext(px, ctx) // parent cancelled first
    97  
    98  	return bs
    99  }
   100  
   101  // Bitswap instances implement the bitswap protocol.
   102  type Bitswap struct {
   103  
   104  	// the ID of the peer to act on behalf of
   105  	self peer.ID
   106  
   107  	// network delivers messages on behalf of the session
   108  	network bsnet.BitSwapNetwork
   109  
   110  	// the peermanager manages sending messages to peers in a way that
   111  	// wont block bitswap operation
   112  	wm *WantManager
   113  
   114  	// blockstore is the local database
   115  	// NB: ensure threadsafety
   116  	blockstore blockstore.Blockstore
   117  
   118  	notifications notifications.PubSub
   119  
   120  	// send keys to a worker to find and connect to providers for them
   121  	findKeys chan *blockRequest
   122  
   123  	engine *decision.Engine
   124  
   125  	process process.Process
   126  
   127  	newBlocks chan *blocks.Block
   128  
   129  	provideKeys chan key.Key
   130  
   131  	counterLk      sync.Mutex
   132  	blocksRecvd    int
   133  	dupBlocksRecvd int
   134  }
   135  
   136  type blockRequest struct {
   137  	keys []key.Key
   138  	ctx  context.Context
   139  }
   140  
   141  // GetBlock attempts to retrieve a particular block from peers within the
   142  // deadline enforced by the context.
   143  func (bs *Bitswap) GetBlock(parent context.Context, k key.Key) (*blocks.Block, error) {
   144  
   145  	// Any async work initiated by this function must end when this function
   146  	// returns. To ensure this, derive a new context. Note that it is okay to
   147  	// listen on parent in this scope, but NOT okay to pass |parent| to
   148  	// functions called by this one. Otherwise those functions won't return
   149  	// when this context's cancel func is executed. This is difficult to
   150  	// enforce. May this comment keep you safe.
   151  
   152  	ctx, cancelFunc := context.WithCancel(parent)
   153  
   154  	ctx = eventlog.ContextWithLoggable(ctx, eventlog.Uuid("GetBlockRequest"))
   155  	log.Event(ctx, "Bitswap.GetBlockRequest.Start", &k)
   156  	defer log.Event(ctx, "Bitswap.GetBlockRequest.End", &k)
   157  
   158  	defer func() {
   159  		cancelFunc()
   160  	}()
   161  
   162  	promise, err := bs.GetBlocks(ctx, []key.Key{k})
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  
   167  	select {
   168  	case block, ok := <-promise:
   169  		if !ok {
   170  			select {
   171  			case <-ctx.Done():
   172  				return nil, ctx.Err()
   173  			default:
   174  				return nil, errors.New("promise channel was closed")
   175  			}
   176  		}
   177  		return block, nil
   178  	case <-parent.Done():
   179  		return nil, parent.Err()
   180  	}
   181  }
   182  
   183  func (bs *Bitswap) WantlistForPeer(p peer.ID) []key.Key {
   184  	var out []key.Key
   185  	for _, e := range bs.engine.WantlistForPeer(p) {
   186  		out = append(out, e.Key)
   187  	}
   188  	return out
   189  }
   190  
   191  // GetBlocks returns a channel where the caller may receive blocks that
   192  // correspond to the provided |keys|. Returns an error if BitSwap is unable to
   193  // begin this request within the deadline enforced by the context.
   194  //
   195  // NB: Your request remains open until the context expires. To conserve
   196  // resources, provide a context with a reasonably short deadline (ie. not one
   197  // that lasts throughout the lifetime of the server)
   198  func (bs *Bitswap) GetBlocks(ctx context.Context, keys []key.Key) (<-chan *blocks.Block, error) {
   199  	select {
   200  	case <-bs.process.Closing():
   201  		return nil, errors.New("bitswap is closed")
   202  	default:
   203  	}
   204  	promise := bs.notifications.Subscribe(ctx, keys...)
   205  
   206  	for _, k := range keys {
   207  		log.Event(ctx, "Bitswap.GetBlockRequest.Start", &k)
   208  	}
   209  
   210  	bs.wm.WantBlocks(keys)
   211  
   212  	req := &blockRequest{
   213  		keys: keys,
   214  		ctx:  ctx,
   215  	}
   216  	select {
   217  	case bs.findKeys <- req:
   218  		return promise, nil
   219  	case <-ctx.Done():
   220  		return nil, ctx.Err()
   221  	}
   222  }
   223  
   224  // HasBlock announces the existance of a block to this bitswap service. The
   225  // service will potentially notify its peers.
   226  func (bs *Bitswap) HasBlock(ctx context.Context, blk *blocks.Block) error {
   227  	select {
   228  	case <-bs.process.Closing():
   229  		return errors.New("bitswap is closed")
   230  	default:
   231  	}
   232  
   233  	err := bs.tryPutBlock(blk, 4) // attempt to store block up to four times
   234  	if err != nil {
   235  		log.Errorf("Error writing block to datastore: %s", err)
   236  		return err
   237  	}
   238  
   239  	bs.notifications.Publish(blk)
   240  
   241  	select {
   242  	case bs.newBlocks <- blk:
   243  		// send block off to be reprovided
   244  	case <-ctx.Done():
   245  		return ctx.Err()
   246  	}
   247  	return nil
   248  }
   249  
   250  func (bs *Bitswap) tryPutBlock(blk *blocks.Block, attempts int) error {
   251  	var err error
   252  	for i := 0; i < attempts; i++ {
   253  		if err = bs.blockstore.Put(blk); err == nil {
   254  			break
   255  		}
   256  
   257  		time.Sleep(time.Millisecond * time.Duration(400*(i+1)))
   258  	}
   259  	return err
   260  }
   261  
   262  func (bs *Bitswap) connectToProviders(ctx context.Context, entries []wantlist.Entry) {
   263  
   264  	ctx, cancel := context.WithCancel(ctx)
   265  	defer cancel()
   266  
   267  	// Get providers for all entries in wantlist (could take a while)
   268  	wg := sync.WaitGroup{}
   269  	for _, e := range entries {
   270  		wg.Add(1)
   271  		go func(k key.Key) {
   272  			defer wg.Done()
   273  
   274  			child, cancel := context.WithTimeout(ctx, providerRequestTimeout)
   275  			defer cancel()
   276  			providers := bs.network.FindProvidersAsync(child, k, maxProvidersPerRequest)
   277  			for prov := range providers {
   278  				go func(p peer.ID) {
   279  					bs.network.ConnectTo(ctx, p)
   280  				}(prov)
   281  			}
   282  		}(e.Key)
   283  	}
   284  
   285  	wg.Wait() // make sure all our children do finish.
   286  }
   287  
   288  func (bs *Bitswap) ReceiveMessage(ctx context.Context, p peer.ID, incoming bsmsg.BitSwapMessage) {
   289  	// This call records changes to wantlists, blocks received,
   290  	// and number of bytes transfered.
   291  	bs.engine.MessageReceived(p, incoming)
   292  	// TODO: this is bad, and could be easily abused.
   293  	// Should only track *useful* messages in ledger
   294  
   295  	iblocks := incoming.Blocks()
   296  
   297  	if len(iblocks) == 0 {
   298  		return
   299  	}
   300  
   301  	// quickly send out cancels, reduces chances of duplicate block receives
   302  	var keys []key.Key
   303  	for _, block := range iblocks {
   304  		if _, found := bs.wm.wl.Contains(block.Key()); !found {
   305  			log.Info("received un-asked-for block: %s", block)
   306  			continue
   307  		}
   308  		keys = append(keys, block.Key())
   309  	}
   310  	bs.wm.CancelWants(keys)
   311  
   312  	wg := sync.WaitGroup{}
   313  	for _, block := range iblocks {
   314  		wg.Add(1)
   315  		go func(b *blocks.Block) {
   316  			defer wg.Done()
   317  
   318  			if err := bs.updateReceiveCounters(b.Key()); err != nil {
   319  				return // ignore error, is either logged previously, or ErrAlreadyHaveBlock
   320  			}
   321  
   322  			k := b.Key()
   323  			log.Event(ctx, "Bitswap.GetBlockRequest.End", &k)
   324  
   325  			log.Debugf("got block %s from %s", b, p)
   326  			hasBlockCtx, cancel := context.WithTimeout(ctx, hasBlockTimeout)
   327  			defer cancel()
   328  			if err := bs.HasBlock(hasBlockCtx, b); err != nil {
   329  				log.Warningf("ReceiveMessage HasBlock error: %s", err)
   330  			}
   331  		}(block)
   332  	}
   333  	wg.Wait()
   334  }
   335  
   336  var ErrAlreadyHaveBlock = errors.New("already have block")
   337  
   338  func (bs *Bitswap) updateReceiveCounters(k key.Key) error {
   339  	bs.counterLk.Lock()
   340  	defer bs.counterLk.Unlock()
   341  	bs.blocksRecvd++
   342  	has, err := bs.blockstore.Has(k)
   343  	if err != nil {
   344  		log.Infof("blockstore.Has error: %s", err)
   345  		return err
   346  	}
   347  	if err == nil && has {
   348  		bs.dupBlocksRecvd++
   349  	}
   350  
   351  	if has {
   352  		return ErrAlreadyHaveBlock
   353  	}
   354  	return nil
   355  }
   356  
   357  // Connected/Disconnected warns bitswap about peer connections
   358  func (bs *Bitswap) PeerConnected(p peer.ID) {
   359  	bs.wm.Connected(p)
   360  }
   361  
   362  // Connected/Disconnected warns bitswap about peer connections
   363  func (bs *Bitswap) PeerDisconnected(p peer.ID) {
   364  	bs.wm.Disconnected(p)
   365  	bs.engine.PeerDisconnected(p)
   366  }
   367  
   368  func (bs *Bitswap) ReceiveError(err error) {
   369  	log.Infof("Bitswap ReceiveError: %s", err)
   370  	// TODO log the network error
   371  	// TODO bubble the network error up to the parent context/error logger
   372  }
   373  
   374  func (bs *Bitswap) Close() error {
   375  	return bs.process.Close()
   376  }
   377  
   378  func (bs *Bitswap) GetWantlist() []key.Key {
   379  	var out []key.Key
   380  	for _, e := range bs.wm.wl.Entries() {
   381  		out = append(out, e.Key)
   382  	}
   383  	return out
   384  }