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

     1  package bitswap
     2  
     3  import (
     4  	"time"
     5  
     6  	process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
     7  	procctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
     8  	ratelimit "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit"
     9  	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
    10  
    11  	key "github.com/ipfs/go-ipfs/blocks/key"
    12  	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
    13  )
    14  
    15  var TaskWorkerCount = 8
    16  
    17  func (bs *Bitswap) startWorkers(px process.Process, ctx context.Context) {
    18  	// Start up a worker to handle block requests this node is making
    19  	px.Go(func(px process.Process) {
    20  		bs.providerConnector(ctx)
    21  	})
    22  
    23  	// Start up workers to handle requests from other nodes for the data on this node
    24  	for i := 0; i < TaskWorkerCount; i++ {
    25  		i := i
    26  		px.Go(func(px process.Process) {
    27  			bs.taskWorker(ctx, i)
    28  		})
    29  	}
    30  
    31  	// Start up a worker to manage periodically resending our wantlist out to peers
    32  	px.Go(func(px process.Process) {
    33  		bs.rebroadcastWorker(ctx)
    34  	})
    35  
    36  	// Start up a worker to manage sending out provides messages
    37  	px.Go(func(px process.Process) {
    38  		bs.provideCollector(ctx)
    39  	})
    40  
    41  	// Spawn up multiple workers to handle incoming blocks
    42  	// consider increasing number if providing blocks bottlenecks
    43  	// file transfers
    44  	px.Go(bs.provideWorker)
    45  }
    46  
    47  func (bs *Bitswap) taskWorker(ctx context.Context, id int) {
    48  	idmap := eventlog.LoggableMap{"ID": id}
    49  	defer log.Info("bitswap task worker shutting down...")
    50  	for {
    51  		log.Event(ctx, "Bitswap.TaskWorker.Loop", idmap)
    52  		select {
    53  		case nextEnvelope := <-bs.engine.Outbox():
    54  			select {
    55  			case envelope, ok := <-nextEnvelope:
    56  				if !ok {
    57  					continue
    58  				}
    59  				log.Event(ctx, "Bitswap.TaskWorker.Work", eventlog.LoggableMap{
    60  					"ID":     id,
    61  					"Target": envelope.Peer.Pretty(),
    62  					"Block":  envelope.Block.Multihash.B58String(),
    63  				})
    64  
    65  				bs.wm.SendBlock(ctx, envelope)
    66  			case <-ctx.Done():
    67  				return
    68  			}
    69  		case <-ctx.Done():
    70  			return
    71  		}
    72  	}
    73  }
    74  
    75  func (bs *Bitswap) provideWorker(px process.Process) {
    76  
    77  	limiter := ratelimit.NewRateLimiter(px, provideWorkerMax)
    78  
    79  	limitedGoProvide := func(k key.Key, wid int) {
    80  		ev := eventlog.LoggableMap{"ID": wid}
    81  		limiter.LimitedGo(func(px process.Process) {
    82  
    83  			ctx := procctx.OnClosingContext(px) // derive ctx from px
    84  			defer log.EventBegin(ctx, "Bitswap.ProvideWorker.Work", ev, &k).Done()
    85  
    86  			ctx, cancel := context.WithTimeout(ctx, provideTimeout) // timeout ctx
    87  			defer cancel()
    88  
    89  			if err := bs.network.Provide(ctx, k); err != nil {
    90  				log.Error(err)
    91  			}
    92  		})
    93  	}
    94  
    95  	// worker spawner, reads from bs.provideKeys until it closes, spawning a
    96  	// _ratelimited_ number of workers to handle each key.
    97  	limiter.Go(func(px process.Process) {
    98  		for wid := 2; ; wid++ {
    99  			ev := eventlog.LoggableMap{"ID": 1}
   100  			log.Event(procctx.OnClosingContext(px), "Bitswap.ProvideWorker.Loop", ev)
   101  
   102  			select {
   103  			case <-px.Closing():
   104  				return
   105  			case k, ok := <-bs.provideKeys:
   106  				if !ok {
   107  					log.Debug("provideKeys channel closed")
   108  					return
   109  				}
   110  				limitedGoProvide(k, wid)
   111  			}
   112  		}
   113  	})
   114  }
   115  
   116  func (bs *Bitswap) provideCollector(ctx context.Context) {
   117  	defer close(bs.provideKeys)
   118  	var toProvide []key.Key
   119  	var nextKey key.Key
   120  	var keysOut chan key.Key
   121  
   122  	for {
   123  		select {
   124  		case blk, ok := <-bs.newBlocks:
   125  			if !ok {
   126  				log.Debug("newBlocks channel closed")
   127  				return
   128  			}
   129  			if keysOut == nil {
   130  				nextKey = blk.Key()
   131  				keysOut = bs.provideKeys
   132  			} else {
   133  				toProvide = append(toProvide, blk.Key())
   134  			}
   135  		case keysOut <- nextKey:
   136  			if len(toProvide) > 0 {
   137  				nextKey = toProvide[0]
   138  				toProvide = toProvide[1:]
   139  			} else {
   140  				keysOut = nil
   141  			}
   142  		case <-ctx.Done():
   143  			return
   144  		}
   145  	}
   146  }
   147  
   148  // connects to providers for the given keys
   149  func (bs *Bitswap) providerConnector(parent context.Context) {
   150  	defer log.Info("bitswap client worker shutting down...")
   151  
   152  	for {
   153  		log.Event(parent, "Bitswap.ProviderConnector.Loop")
   154  		select {
   155  		case req := <-bs.findKeys:
   156  			keys := req.keys
   157  			if len(keys) == 0 {
   158  				log.Warning("Received batch request for zero blocks")
   159  				continue
   160  			}
   161  			log.Event(parent, "Bitswap.ProviderConnector.Work", eventlog.LoggableMap{"Keys": keys})
   162  
   163  			// NB: Optimization. Assumes that providers of key[0] are likely to
   164  			// be able to provide for all keys. This currently holds true in most
   165  			// every situation. Later, this assumption may not hold as true.
   166  			child, cancel := context.WithTimeout(req.ctx, providerRequestTimeout)
   167  			providers := bs.network.FindProvidersAsync(child, keys[0], maxProvidersPerRequest)
   168  			for p := range providers {
   169  				go bs.network.ConnectTo(req.ctx, p)
   170  			}
   171  			cancel()
   172  
   173  		case <-parent.Done():
   174  			return
   175  		}
   176  	}
   177  }
   178  
   179  func (bs *Bitswap) rebroadcastWorker(parent context.Context) {
   180  	ctx, cancel := context.WithCancel(parent)
   181  	defer cancel()
   182  
   183  	broadcastSignal := time.NewTicker(rebroadcastDelay.Get())
   184  	defer broadcastSignal.Stop()
   185  
   186  	tick := time.NewTicker(10 * time.Second)
   187  	defer tick.Stop()
   188  
   189  	for {
   190  		log.Event(ctx, "Bitswap.Rebroadcast.idle")
   191  		select {
   192  		case <-tick.C:
   193  			n := bs.wm.wl.Len()
   194  			if n > 0 {
   195  				log.Debug(n, "keys in bitswap wantlist")
   196  			}
   197  		case <-broadcastSignal.C: // resend unfulfilled wantlist keys
   198  			log.Event(ctx, "Bitswap.Rebroadcast.active")
   199  			entries := bs.wm.wl.Entries()
   200  			if len(entries) > 0 {
   201  				bs.connectToProviders(ctx, entries)
   202  			}
   203  		case <-parent.Done():
   204  			return
   205  		}
   206  	}
   207  }