@cryptotaxi247 / kubo / commits / 3be5c913e

fix issue with sessions not receiving locally added blocks

License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>

Jeromy committed Jul 7, 2017 at 11:40 UTC 3be5c913eee0a9ee00eac7055fd48b506017161c
3 files changed +120 -25
exchange/bitswap/bitswap.go
+9 -1
@@ -317,6 +317,10 @@ func (bs *Bitswap) HasBlock(blk blocks.Block) error {
317 // it now as it requires more thought and isnt causing immediate problems.
318 bs.notifications.Publish(blk)
319
320 + for _, s := range bs.SessionsForBlock(blk.Cid()) {
321 + s.receiveBlockFrom("", blk)
322 + }
323 +
324 bs.engine.AddBlock(blk)
325
326 select {
@@ -370,7 +374,7 @@ func (bs *Bitswap) ReceiveMessage(ctx context.Context, p peer.ID, incoming bsmsg
374 wg := sync.WaitGroup{}
375 for _, block := range iblocks {
376 wg.Add(1)
373 - go func(b blocks.Block) {
377 + go func(b blocks.Block) { // TODO: this probably doesnt need to be a goroutine...
378 defer wg.Done()
379
380 bs.updateReceiveCounters(b)
@@ -382,7 +386,11 @@ func (bs *Bitswap) ReceiveMessage(ctx context.Context, p peer.ID, incoming bsmsg
386 ses.receiveBlockFrom(p, b)
387 bs.CancelWants([]*cid.Cid{k}, ses.id)
388 }
389 +
390 log.Debugf("got block %s from %s", b, p)
391 + // TODO: rework this to not call 'HasBlock'. 'HasBlock' is really
392 + // designed to be called when blocks are coming in from non-bitswap
393 + // places (like the user manually adding data)
394 if err := bs.HasBlock(b); err != nil {
395 log.Warningf("ReceiveMessage HasBlock error: %s", err)
396 }
exchange/bitswap/session.go
+71 -24
@@ -21,7 +21,7 @@ const activeWantsLimit = 16
21 // info to, and who to request blocks from
22 type Session struct {
23 ctx context.Context
24 - tofetch []*cid.Cid
24 + tofetch *cidQueue
25 activePeers map[peer.ID]struct{}
26 activePeersArr []peer.ID
27
@@ -55,6 +55,7 @@ func (bs *Bitswap) NewSession(ctx context.Context) *Session {
55 liveWants: make(map[string]time.Time),
56 newReqs: make(chan []*cid.Cid),
57 cancelKeys: make(chan []*cid.Cid),
58 + tofetch: newCidQueue(),
59 interestReqs: make(chan interestReq),
60 ctx: ctx,
61 bs: bs,
@@ -157,7 +158,9 @@ func (s *Session) run(ctx context.Context) {
158
159 s.wantBlocks(ctx, now)
160 }
160 - s.tofetch = append(s.tofetch, keys...)
161 + for _, k := range keys {
162 + s.tofetch.Push(k)
163 + }
164 case keys := <-s.cancelKeys:
165 s.cancel(keys)
166
@@ -188,8 +191,7 @@ func (s *Session) run(ctx context.Context) {
191 case p := <-newpeers:
192 s.addActivePeer(p)
193 case lwchk := <-s.interestReqs:
191 - _, ok := s.liveWants[lwchk.c.KeyString()]
192 - lwchk.resp <- ok
194 + lwchk.resp <- s.cidIsWanted(lwchk.c)
195 case <-ctx.Done():
196 s.tick.Stop()
197 return
@@ -197,19 +199,31 @@ func (s *Session) run(ctx context.Context) {
199 }
200 }
201
202 +func (s *Session) cidIsWanted(c *cid.Cid) bool {
203 + _, ok := s.liveWants[c.KeyString()]
204 + if !ok {
205 + ok = s.tofetch.Has(c)
206 + }
207 +
208 + return ok
209 +}
210 +
211 func (s *Session) receiveBlock(ctx context.Context, blk blocks.Block) {
201 - ks := blk.Cid().KeyString()
202 - if _, ok := s.liveWants[ks]; ok {
203 - tval := s.liveWants[ks]
204 - s.latTotal += time.Since(tval)
212 + c := blk.Cid()
213 + if s.cidIsWanted(c) {
214 + ks := c.KeyString()
215 + tval, ok := s.liveWants[ks]
216 + if ok {
217 + s.latTotal += time.Since(tval)
218 + delete(s.liveWants, ks)
219 + } else {
220 + s.tofetch.Remove(c)
221 + }
222 s.fetchcnt++
206 - delete(s.liveWants, ks)
223 s.notif.Publish(blk)
224
209 - if len(s.tofetch) > 0 {
210 - next := s.tofetch[0:1]
211 - s.tofetch = s.tofetch[1:]
212 - s.wantBlocks(ctx, next)
225 + if next := s.tofetch.Pop(); next != nil {
226 + s.wantBlocks(ctx, []*cid.Cid{next})
227 }
228 }
229 }
@@ -222,19 +236,9 @@ func (s *Session) wantBlocks(ctx context.Context, ks []*cid.Cid) {
236 }
237
238 func (s *Session) cancel(keys []*cid.Cid) {
225 - sset := cid.NewSet()
239 for _, c := range keys {
227 - sset.Add(c)
240 + s.tofetch.Remove(c)
241 }
229 - var i, j int
230 - for ; j < len(s.tofetch); j++ {
231 - if sset.Has(s.tofetch[j]) {
232 - continue
233 - }
234 - s.tofetch[i] = s.tofetch[j]
235 - i++
236 - }
237 - s.tofetch = s.tofetch[:i]
242 }
243
244 func (s *Session) cancelWants(keys []*cid.Cid) {
@@ -260,3 +264,46 @@ func (s *Session) GetBlocks(ctx context.Context, keys []*cid.Cid) (<-chan blocks
264 func (s *Session) GetBlock(parent context.Context, k *cid.Cid) (blocks.Block, error) {
265 return getBlock(parent, k, s.GetBlocks)
266 }
267 +
268 +type cidQueue struct {
269 + elems []*cid.Cid
270 + eset *cid.Set
271 +}
272 +
273 +func newCidQueue() *cidQueue {
274 + return &cidQueue{eset: cid.NewSet()}
275 +}
276 +
277 +func (cq *cidQueue) Pop() *cid.Cid {
278 + for {
279 + if len(cq.elems) == 0 {
280 + return nil
281 + }
282 +
283 + out := cq.elems[0]
284 + cq.elems = cq.elems[1:]
285 +
286 + if cq.eset.Has(out) {
287 + cq.eset.Remove(out)
288 + return out
289 + }
290 + }
291 +}
292 +
293 +func (cq *cidQueue) Push(c *cid.Cid) {
294 + if cq.eset.Visit(c) {
295 + cq.elems = append(cq.elems, c)
296 + }
297 +}
298 +
299 +func (cq *cidQueue) Remove(c *cid.Cid) {
300 + cq.eset.Remove(c)
301 +}
302 +
303 +func (cq *cidQueue) Has(c *cid.Cid) bool {
304 + return cq.eset.Has(c)
305 +}
306 +
307 +func (cq *cidQueue) Len() int {
308 + return cq.eset.Len()
309 +}
exchange/bitswap/session_test.go
+40
@@ -202,3 +202,43 @@ func TestInterestCacheOverflow(t *testing.T) {
202 t.Fatal("timed out waiting for block")
203 }
204 }
205 +
206 +func TestPutAfterSessionCacheEvict(t *testing.T) {
207 + ctx, cancel := context.WithCancel(context.Background())
208 + defer cancel()
209 +
210 + vnet := getVirtualNetwork()
211 + sesgen := NewTestSessionGenerator(vnet)
212 + defer sesgen.Close()
213 + bgen := blocksutil.NewBlockGenerator()
214 +
215 + blks := bgen.Blocks(2500)
216 + inst := sesgen.Instances(1)
217 +
218 + a := inst[0]
219 +
220 + ses := a.Exchange.NewSession(ctx)
221 +
222 + var allcids []*cid.Cid
223 + for _, blk := range blks[1:] {
224 + allcids = append(allcids, blk.Cid())
225 + }
226 +
227 + blkch, err := ses.GetBlocks(ctx, allcids)
228 + if err != nil {
229 + t.Fatal(err)
230 + }
231 +
232 + // wait to ensure that all the above cids were added to the sessions cache
233 + time.Sleep(time.Millisecond * 50)
234 +
235 + if err := a.Exchange.HasBlock(blks[17]); err != nil {
236 + t.Fatal(err)
237 + }
238 +
239 + select {
240 + case <-blkch:
241 + case <-time.After(time.Millisecond * 50):
242 + t.Fatal("timed out waiting for block")
243 + }
244 +}