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
+}