Skip to content
This repository was archived by the owner on Feb 1, 2023. It is now read-only.

Commit b3a47bc

Browse files
dirkmcStebalien
authored andcommitted
feat: bitswap protocol extensions
This commit extends the bitswap protocol with two additional wantlist properties: * WANT_HAVE/HAVE: Instead of asking for a block, a node can specify that they want to know if any peers "have" the block. * WANT_HAVE_NOT/HAVE_NOT: Instead of waiting for a timeout, a node can explicitly request to be told immediately if their peers don't currently have the given block. Additionally, nodes now tell their peers how much data they have queued to send them when sending messages. This allows peers to better distribute requests, keeping all peers busy but not overloaded. Changes in this PR are described in: #186
1 parent dcfe40e commit b3a47bc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+8216
-1857
lines changed

benchmarks_test.go

Lines changed: 350 additions & 84 deletions
Large diffs are not rendered by default.

bitswap.go

Lines changed: 66 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ package bitswap
55
import (
66
"context"
77
"errors"
8+
89
"sync"
910
"time"
1011

11-
bssrs "github.com/ipfs/go-bitswap/sessionrequestsplitter"
1212
delay "github.com/ipfs/go-ipfs-delay"
1313

14+
bsbpm "github.com/ipfs/go-bitswap/blockpresencemanager"
1415
decision "github.com/ipfs/go-bitswap/decision"
1516
bsgetter "github.com/ipfs/go-bitswap/getter"
1617
bsmsg "github.com/ipfs/go-bitswap/message"
@@ -20,6 +21,7 @@ import (
2021
bspm "github.com/ipfs/go-bitswap/peermanager"
2122
bspqm "github.com/ipfs/go-bitswap/providerquerymanager"
2223
bssession "github.com/ipfs/go-bitswap/session"
24+
bssim "github.com/ipfs/go-bitswap/sessioninterestmanager"
2325
bssm "github.com/ipfs/go-bitswap/sessionmanager"
2426
bsspm "github.com/ipfs/go-bitswap/sessionpeermanager"
2527
bswm "github.com/ipfs/go-bitswap/wantmanager"
@@ -113,24 +115,30 @@ func New(parent context.Context, network bsnet.BitSwapNetwork,
113115
return bsmq.New(ctx, p, network)
114116
}
115117

116-
wm := bswm.New(ctx, bspm.New(ctx, peerQueueFactory))
118+
sim := bssim.New()
119+
bpm := bsbpm.New()
120+
pm := bspm.New(ctx, peerQueueFactory, network.Self())
121+
wm := bswm.New(ctx, pm, sim, bpm)
117122
pqm := bspqm.New(ctx, network)
118123

119-
sessionFactory := func(ctx context.Context, id uint64, pm bssession.PeerManager, srs bssession.RequestSplitter,
124+
sessionFactory := func(ctx context.Context, id uint64, spm bssession.SessionPeerManager,
125+
sim *bssim.SessionInterestManager,
126+
pm bssession.PeerManager,
127+
bpm *bsbpm.BlockPresenceManager,
120128
notif notifications.PubSub,
121129
provSearchDelay time.Duration,
122-
rebroadcastDelay delay.D) bssm.Session {
123-
return bssession.New(ctx, id, wm, pm, srs, notif, provSearchDelay, rebroadcastDelay)
130+
rebroadcastDelay delay.D,
131+
self peer.ID) bssm.Session {
132+
return bssession.New(ctx, id, wm, spm, sim, pm, bpm, notif, provSearchDelay, rebroadcastDelay, self)
124133
}
125-
sessionPeerManagerFactory := func(ctx context.Context, id uint64) bssession.PeerManager {
134+
sessionPeerManagerFactory := func(ctx context.Context, id uint64) bssession.SessionPeerManager {
126135
return bsspm.New(ctx, id, network.ConnectionManager(), pqm)
127136
}
128-
sessionRequestSplitterFactory := func(ctx context.Context) bssession.RequestSplitter {
129-
return bssrs.New(ctx)
130-
}
131137
notif := notifications.New()
138+
sm := bssm.New(ctx, sessionFactory, sim, sessionPeerManagerFactory, bpm, pm, notif, network.Self())
139+
wm.SetSessionManager(sm)
140+
engine := decision.NewEngine(ctx, bstore, network.ConnectionManager(), network.Self())
132141

133-
engine := decision.NewEngine(ctx, bstore, network.ConnectionManager()) // TODO close the engine with Close() method
134142
bs := &Bitswap{
135143
blockstore: bstore,
136144
engine: engine,
@@ -139,8 +147,10 @@ func New(parent context.Context, network bsnet.BitSwapNetwork,
139147
newBlocks: make(chan cid.Cid, HasBlockBufferSize),
140148
provideKeys: make(chan cid.Cid, provideKeysBufferSize),
141149
wm: wm,
150+
pm: pm,
142151
pqm: pqm,
143-
sm: bssm.New(ctx, sessionFactory, sessionPeerManagerFactory, sessionRequestSplitterFactory, notif),
152+
sm: sm,
153+
sim: sim,
144154
notif: notif,
145155
counters: new(counters),
146156
dupMetric: dupHist,
@@ -156,7 +166,6 @@ func New(parent context.Context, network bsnet.BitSwapNetwork,
156166
option(bs)
157167
}
158168

159-
bs.wm.Startup()
160169
bs.pqm.Startup()
161170
network.SetDelegate(bs)
162171

@@ -181,6 +190,8 @@ type Bitswap struct {
181190
// the wantlist tracks global wants for bitswap
182191
wm *bswm.WantManager
183192

193+
pm *bspm.PeerManager
194+
184195
// the provider query manager manages requests to find providers
185196
pqm *bspqm.ProviderQueryManager
186197

@@ -215,9 +226,13 @@ type Bitswap struct {
215226
allMetric metrics.Histogram
216227
sentHistogram metrics.Histogram
217228

218-
// the sessionmanager manages tracking sessions
229+
// the SessionManager routes requests to interested sessions
219230
sm *bssm.SessionManager
220231

232+
// the SessionInterestManager keeps track of which sessions are interested
233+
// in which CIDs
234+
sim *bssim.SessionInterestManager
235+
221236
// whether or not to make provide announcements
222237
provideEnabled bool
223238

@@ -275,14 +290,14 @@ func (bs *Bitswap) GetBlocks(ctx context.Context, keys []cid.Cid) (<-chan blocks
275290
// HasBlock announces the existence of a block to this bitswap service. The
276291
// service will potentially notify its peers.
277292
func (bs *Bitswap) HasBlock(blk blocks.Block) error {
278-
return bs.receiveBlocksFrom(context.Background(), "", []blocks.Block{blk})
293+
return bs.receiveBlocksFrom(context.Background(), "", []blocks.Block{blk}, nil, nil)
279294
}
280295

281296
// TODO: Some of this stuff really only needs to be done when adding a block
282297
// from the user, not when receiving it from the network.
283298
// In case you run `git blame` on this comment, I'll save you some time: ask
284299
// @whyrusleeping, I don't know the answers you seek.
285-
func (bs *Bitswap) receiveBlocksFrom(ctx context.Context, from peer.ID, blks []blocks.Block) error {
300+
func (bs *Bitswap) receiveBlocksFrom(ctx context.Context, from peer.ID, blks []blocks.Block, haves []cid.Cid, dontHaves []cid.Cid) error {
286301
select {
287302
case <-bs.process.Closing():
288303
return errors.New("bitswap is closed")
@@ -293,22 +308,20 @@ func (bs *Bitswap) receiveBlocksFrom(ctx context.Context, from peer.ID, blks []b
293308

294309
// If blocks came from the network
295310
if from != "" {
296-
// Split blocks into wanted blocks vs duplicates
297-
wanted = make([]blocks.Block, 0, len(blks))
298-
for _, b := range blks {
299-
if bs.sm.IsWanted(b.Cid()) {
300-
wanted = append(wanted, b)
301-
} else {
302-
log.Debugf("[recv] block not in wantlist; cid=%s, peer=%s", b.Cid(), from)
303-
}
311+
var notWanted []blocks.Block
312+
wanted, notWanted = bs.sim.SplitWantedUnwanted(blks)
313+
for _, b := range notWanted {
314+
log.Debugf("[recv] block not in wantlist; cid=%s, peer=%s", b.Cid(), from)
304315
}
305316
}
306317

307318
// Put wanted blocks into blockstore
308-
err := bs.blockstore.PutMany(wanted)
309-
if err != nil {
310-
log.Errorf("Error writing %d blocks to datastore: %s", len(wanted), err)
311-
return err
319+
if len(wanted) > 0 {
320+
err := bs.blockstore.PutMany(wanted)
321+
if err != nil {
322+
log.Errorf("Error writing %d blocks to datastore: %s", len(wanted), err)
323+
return err
324+
}
312325
}
313326

314327
// NOTE: There exists the possiblity for a race condition here. If a user
@@ -322,33 +335,25 @@ func (bs *Bitswap) receiveBlocksFrom(ctx context.Context, from peer.ID, blks []b
322335
allKs = append(allKs, b.Cid())
323336
}
324337

325-
wantedKs := allKs
326-
if len(blks) != len(wanted) {
327-
wantedKs = make([]cid.Cid, 0, len(wanted))
328-
for _, b := range wanted {
329-
wantedKs = append(wantedKs, b.Cid())
330-
}
331-
}
332-
333338
// Send all block keys (including duplicates) to any sessions that want them.
334339
// (The duplicates are needed by sessions for accounting purposes)
335-
bs.sm.ReceiveFrom(from, allKs)
340+
bs.wm.ReceiveFrom(ctx, from, allKs, haves, dontHaves)
336341

337-
// Send wanted block keys to decision engine
338-
bs.engine.AddBlocks(wantedKs)
342+
// Send wanted blocks to decision engine
343+
bs.engine.ReceiveFrom(from, wanted, haves)
339344

340345
// Publish the block to any Bitswap clients that had requested blocks.
341-
// (the sessions use this pubsub mechanism to inform clients of received
346+
// (the sessions use this pubsub mechanism to inform clients of incoming
342347
// blocks)
343348
for _, b := range wanted {
344349
bs.notif.Publish(b)
345350
}
346351

347352
// If the reprovider is enabled, send wanted blocks to reprovider
348353
if bs.provideEnabled {
349-
for _, k := range wantedKs {
354+
for _, blk := range wanted {
350355
select {
351-
case bs.newBlocks <- k:
356+
case bs.newBlocks <- blk.Cid():
352357
// send block off to be reprovided
353358
case <-bs.process.Closing():
354359
return bs.process.Close()
@@ -380,20 +385,22 @@ func (bs *Bitswap) ReceiveMessage(ctx context.Context, p peer.ID, incoming bsmsg
380385

381386
iblocks := incoming.Blocks()
382387

383-
if len(iblocks) == 0 {
384-
return
385-
}
386-
387-
bs.updateReceiveCounters(iblocks)
388-
for _, b := range iblocks {
389-
log.Debugf("[recv] block; cid=%s, peer=%s", b.Cid(), p)
388+
if len(iblocks) > 0 {
389+
bs.updateReceiveCounters(iblocks)
390+
for _, b := range iblocks {
391+
log.Debugf("[recv] block; cid=%s, peer=%s", b.Cid(), p)
392+
}
390393
}
391394

392-
// Process blocks
393-
err := bs.receiveBlocksFrom(ctx, p, iblocks)
394-
if err != nil {
395-
log.Warningf("ReceiveMessage recvBlockFrom error: %s", err)
396-
return
395+
haves := incoming.Haves()
396+
dontHaves := incoming.DontHaves()
397+
if len(iblocks) > 0 || len(haves) > 0 || len(dontHaves) > 0 {
398+
// Process blocks
399+
err := bs.receiveBlocksFrom(ctx, p, iblocks, haves, dontHaves)
400+
if err != nil {
401+
log.Warningf("ReceiveMessage recvBlockFrom error: %s", err)
402+
return
403+
}
397404
}
398405
}
399406

@@ -479,12 +486,12 @@ func (bs *Bitswap) Close() error {
479486

480487
// GetWantlist returns the current local wantlist.
481488
func (bs *Bitswap) GetWantlist() []cid.Cid {
482-
entries := bs.wm.CurrentWants()
483-
out := make([]cid.Cid, 0, len(entries))
484-
for _, e := range entries {
485-
out = append(out, e.Cid)
486-
}
487-
return out
489+
return bs.pm.CurrentWants()
490+
}
491+
492+
// GetWanthaves returns the current list of want-haves.
493+
func (bs *Bitswap) GetWantHaves() []cid.Cid {
494+
return bs.pm.CurrentWantHaves()
488495
}
489496

490497
// IsOnline is needed to match go-ipfs-exchange-interface

bitswap_test.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -571,15 +571,17 @@ func TestWantlistCleanup(t *testing.T) {
571571
defer ig.Close()
572572
bg := blocksutil.NewBlockGenerator()
573573

574-
instances := ig.Instances(1)[0]
575-
bswap := instances.Exchange
574+
instances := ig.Instances(2)
575+
instance := instances[0]
576+
bswap := instance.Exchange
576577
blocks := bg.Blocks(20)
577578

578579
var keys []cid.Cid
579580
for _, b := range blocks {
580581
keys = append(keys, b.Cid())
581582
}
582583

584+
// Once context times out, key should be removed from wantlist
583585
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
584586
defer cancel()
585587
_, err := bswap.GetBlock(ctx, keys[0])
@@ -589,10 +591,11 @@ func TestWantlistCleanup(t *testing.T) {
589591

590592
time.Sleep(time.Millisecond * 50)
591593

592-
if len(bswap.GetWantlist()) > 0 {
594+
if len(bswap.GetWantHaves()) > 0 {
593595
t.Fatal("should not have anyting in wantlist")
594596
}
595597

598+
// Once context times out, keys should be removed from wantlist
596599
ctx, cancel = context.WithTimeout(context.Background(), time.Millisecond*50)
597600
defer cancel()
598601
_, err = bswap.GetBlocks(ctx, keys[:10])
@@ -603,29 +606,37 @@ func TestWantlistCleanup(t *testing.T) {
603606
<-ctx.Done()
604607
time.Sleep(time.Millisecond * 50)
605608

606-
if len(bswap.GetWantlist()) > 0 {
609+
if len(bswap.GetWantHaves()) > 0 {
607610
t.Fatal("should not have anyting in wantlist")
608611
}
609612

613+
// Send want for single block, with no timeout
610614
_, err = bswap.GetBlocks(context.Background(), keys[:1])
611615
if err != nil {
612616
t.Fatal(err)
613617
}
614618

619+
// Send want for 10 blocks
615620
ctx, cancel = context.WithCancel(context.Background())
616621
_, err = bswap.GetBlocks(ctx, keys[10:])
617622
if err != nil {
618623
t.Fatal(err)
619624
}
620625

626+
// Even after 50 milli-seconds we haven't explicitly cancelled anything
627+
// and no timeouts have expired, so we should have 11 want-haves
621628
time.Sleep(time.Millisecond * 50)
622-
if len(bswap.GetWantlist()) != 5 {
623-
t.Fatal("should have 5 keys in wantlist")
629+
if len(bswap.GetWantHaves()) != 11 {
630+
t.Fatal("should have 11 keys in wantlist")
624631
}
625632

633+
// Cancel the timeout for the request for 10 blocks. This should remove
634+
// the want-haves
626635
cancel()
636+
637+
// Once the cancel is processed, we are left with the request for 1 block
627638
time.Sleep(time.Millisecond * 50)
628-
if !(len(bswap.GetWantlist()) == 1 && bswap.GetWantlist()[0] == keys[0]) {
639+
if !(len(bswap.GetWantHaves()) == 1 && bswap.GetWantHaves()[0] == keys[0]) {
629640
t.Fatal("should only have keys[0] in wantlist")
630641
}
631642
}

0 commit comments

Comments
 (0)