workers.go 4.47 KB
Newer Older
1 2 3
package bitswap

import (
4 5
	"os"
	"strconv"
6 7
	"time"

8 9 10 11
	inflect "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/chuckpreslar/inflect"
	process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
	u "github.com/ipfs/go-ipfs/util"
12 13
)

14 15 16 17 18 19 20 21 22 23 24 25
var TaskWorkerCount = 16

func init() {
	twc := os.Getenv("IPFS_TASK_WORKERS")
	if twc != "" {
		n, err := strconv.Atoi(twc)
		if err != nil {
			return
		}
		TaskWorkerCount = n
	}
}
Jeromy's avatar
Jeromy committed
26

27
func (bs *Bitswap) startWorkers(px process.Process, ctx context.Context) {
28 29 30 31 32
	// Start up a worker to handle block requests this node is making
	px.Go(func(px process.Process) {
		bs.clientWorker(ctx)
	})

Jeromy's avatar
Jeromy committed
33 34 35 36 37 38
	// Start up workers to handle requests from other nodes for the data on this node
	for i := 0; i < TaskWorkerCount; i++ {
		px.Go(func(px process.Process) {
			bs.taskWorker(ctx)
		})
	}
39 40 41 42 43 44

	// Start up a worker to manage periodically resending our wantlist out to peers
	px.Go(func(px process.Process) {
		bs.rebroadcastWorker(ctx)
	})

45 46 47 48
	px.Go(func(px process.Process) {
		bs.provideCollector(ctx)
	})

49 50 51 52 53
	// Spawn up multiple workers to handle incoming blocks
	// consider increasing number if providing blocks bottlenecks
	// file transfers
	for i := 0; i < provideWorkers; i++ {
		px.Go(func(px process.Process) {
Jeromy's avatar
Jeromy committed
54
			bs.provideWorker(ctx)
55 56 57 58
		})
	}
}

59
func (bs *Bitswap) taskWorker(ctx context.Context) {
60 61 62 63 64 65 66 67 68 69 70
	defer log.Info("bitswap task worker shutting down...")
	for {
		select {
		case nextEnvelope := <-bs.engine.Outbox():
			select {
			case envelope, ok := <-nextEnvelope:
				if !ok {
					continue
				}
				log.Event(ctx, "deliverBlocks", envelope.Message, envelope.Peer)
				bs.send(ctx, envelope.Peer, envelope.Message)
Jeromy's avatar
Jeromy committed
71
				envelope.Sent()
72 73 74 75 76 77 78 79 80
			case <-ctx.Done():
				return
			}
		case <-ctx.Done():
			return
		}
	}
}

81
func (bs *Bitswap) provideWorker(ctx context.Context) {
82 83
	for {
		select {
84
		case k, ok := <-bs.provideKeys:
85
			if !ok {
86
				log.Debug("provideKeys channel closed")
87 88
				return
			}
Henry's avatar
Henry committed
89
			ctx, cancel := context.WithTimeout(ctx, provideTimeout)
90
			err := bs.network.Provide(ctx, k)
91 92 93
			if err != nil {
				log.Error(err)
			}
Henry's avatar
Henry committed
94
			cancel()
95 96 97 98 99 100
		case <-ctx.Done():
			return
		}
	}
}

101 102
func (bs *Bitswap) provideCollector(ctx context.Context) {
	defer close(bs.provideKeys)
Jeromy's avatar
Jeromy committed
103
	var toProvide []u.Key
104
	var nextKey u.Key
Jeromy's avatar
Jeromy committed
105
	var keysOut chan u.Key
106 107 108 109 110 111 112 113

	for {
		select {
		case blk, ok := <-bs.newBlocks:
			if !ok {
				log.Debug("newBlocks channel closed")
				return
			}
Jeromy's avatar
Jeromy committed
114 115 116 117
			if keysOut == nil {
				nextKey = blk.Key()
				keysOut = bs.provideKeys
			} else {
Jeromy's avatar
Jeromy committed
118
				toProvide = append(toProvide, blk.Key())
Jeromy's avatar
Jeromy committed
119 120
			}
		case keysOut <- nextKey:
Jeromy's avatar
Jeromy committed
121 122 123
			if len(toProvide) > 0 {
				nextKey = toProvide[0]
				toProvide = toProvide[1:]
124
			} else {
Jeromy's avatar
Jeromy committed
125
				keysOut = nil
126 127 128 129 130 131 132
			}
		case <-ctx.Done():
			return
		}
	}
}

133
// TODO ensure only one active request per key
134
func (bs *Bitswap) clientWorker(parent context.Context) {
135 136 137 138 139 140 141 142 143 144 145 146 147 148
	defer log.Info("bitswap client worker shutting down...")

	for {
		select {
		case req := <-bs.batchRequests:
			keys := req.keys
			if len(keys) == 0 {
				log.Warning("Received batch request for zero blocks")
				continue
			}
			for i, k := range keys {
				bs.wantlist.Add(k, kMaxPriority-i)
			}

149 150 151 152 153
			done := make(chan struct{})
			go func() {
				bs.wantNewBlocks(req.ctx, keys)
				close(done)
			}()
154 155 156 157

			// NB: Optimization. Assumes that providers of key[0] are likely to
			// be able to provide for all keys. This currently holds true in most
			// every situation. Later, this assumption may not hold as true.
Henry's avatar
Henry committed
158
			child, cancel := context.WithTimeout(req.ctx, providerRequestTimeout)
159 160 161 162 163
			providers := bs.network.FindProvidersAsync(child, keys[0], maxProvidersPerRequest)
			err := bs.sendWantlistToPeers(req.ctx, providers)
			if err != nil {
				log.Debugf("error sending wantlist: %s", err)
			}
Henry's avatar
Henry committed
164
			cancel()
165 166 167 168

			// Wait for wantNewBlocks to finish
			<-done

169 170 171 172 173 174
		case <-parent.Done():
			return
		}
	}
}

175
func (bs *Bitswap) rebroadcastWorker(parent context.Context) {
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
	ctx, cancel := context.WithCancel(parent)
	defer cancel()

	broadcastSignal := time.After(rebroadcastDelay.Get())

	for {
		select {
		case <-time.Tick(10 * time.Second):
			n := bs.wantlist.Len()
			if n > 0 {
				log.Debug(n, inflect.FromNumber("keys", n), "in bitswap wantlist")
			}
		case <-broadcastSignal: // resend unfulfilled wantlist keys
			entries := bs.wantlist.Entries()
			if len(entries) > 0 {
				bs.sendWantlistToProviders(ctx, entries)
			}
			broadcastSignal = time.After(rebroadcastDelay.Get())
		case <-parent.Done():
			return
		}
	}
}