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 }