Make bitswap better
License: MIT Signed-off-by: Jeromy <why@ipfs.io>
Jeromy committed
May 19, 2016 at 14:32 UTC
d16591ad219af0bc0e5af317063cc9283c3dd8a4
11 files changed
+161
-21
exchange/bitswap/decision/engine.go
+14
-7
@@ -3,6 +3,7 @@ package decision
3
4
import (
5
"sync"
6
+ "time"
7
8
blocks "github.com/ipfs/go-ipfs/blocks"
9
bstore "github.com/ipfs/go-ipfs/blocks/blockstore"
@@ -68,7 +69,7 @@ type Engine struct {
69
// peerRequestQueue is a priority queue of requests received from peers.
70
// Requests are popped from the queue, packaged up, and placed in the
71
// outbox.
71
- peerRequestQueue peerRequestQueue
72
+ peerRequestQueue *prq
73
74
// FIXME it's a bit odd for the client and the worker to both share memory
75
// (both modify the peerRequestQueue) and also to communicate over the
@@ -86,6 +87,8 @@ type Engine struct {
87
lock sync.Mutex // protects the fields immediatly below
88
// ledgerMap lists Ledgers by their Partner key.
89
ledgerMap map[peer.ID]*ledger
90
+
91
+ ticker *time.Ticker
92
}
93
94
func NewEngine(ctx context.Context, bs bstore.Blockstore) *Engine {
@@ -95,6 +98,7 @@ func NewEngine(ctx context.Context, bs bstore.Blockstore) *Engine {
98
peerRequestQueue: newPRQ(),
99
outbox: make(chan (<-chan *Envelope), outboxChanBuffer),
100
workSignal: make(chan struct{}, 1),
101
+ ticker: time.NewTicker(time.Millisecond * 100),
102
}
103
go e.taskWorker(ctx)
104
return e
@@ -142,6 +146,9 @@ func (e *Engine) nextEnvelope(ctx context.Context) (*Envelope, error) {
146
return nil, ctx.Err()
147
case <-e.workSignal:
148
nextTask = e.peerRequestQueue.Pop()
149
+ case <-e.ticker.C:
150
+ e.peerRequestQueue.thawRound()
151
+ nextTask = e.peerRequestQueue.Pop()
152
}
153
}
154
@@ -191,9 +198,6 @@ func (e *Engine) Peers() []peer.ID {
198
// MessageReceived performs book-keeping. Returns error if passed invalid
199
// arguments.
200
func (e *Engine) MessageReceived(p peer.ID, m bsmsg.BitSwapMessage) error {
194
- e.lock.Lock()
195
- defer e.lock.Unlock()
196
-
201
if len(m.Wantlist()) == 0 && len(m.Blocks()) == 0 {
202
log.Debugf("received empty message from %s", p)
203
}
@@ -206,6 +210,8 @@ func (e *Engine) MessageReceived(p peer.ID, m bsmsg.BitSwapMessage) error {
210
}()
211
212
l := e.findOrCreate(p)
213
+ l.lk.Lock()
214
+ defer l.lk.Unlock()
215
if m.Full() {
216
l.wantList = wl.New()
217
}
@@ -236,10 +242,12 @@ func (e *Engine) addBlock(block blocks.Block) {
242
work := false
243
244
for _, l := range e.ledgerMap {
245
+ l.lk.Lock()
246
if entry, ok := l.WantListContains(block.Key()); ok {
247
e.peerRequestQueue.Push(entry, l.Partner)
248
work = true
249
}
250
+ l.lk.Unlock()
251
}
252
253
if work {
@@ -261,9 +269,6 @@ func (e *Engine) AddBlock(block blocks.Block) {
269
// send happen atomically
270
271
func (e *Engine) MessageSent(p peer.ID, m bsmsg.BitSwapMessage) error {
264
- e.lock.Lock()
265
- defer e.lock.Unlock()
266
-
272
l := e.findOrCreate(p)
273
for _, block := range m.Blocks() {
274
l.SentBytes(len(block.Data()))
@@ -290,11 +295,13 @@ func (e *Engine) numBytesReceivedFrom(p peer.ID) uint64 {
295
296
// ledger lazily instantiates a ledger
297
func (e *Engine) findOrCreate(p peer.ID) *ledger {
298
+ e.lock.Lock()
299
l, ok := e.ledgerMap[p]
300
if !ok {
301
l = newLedger(p)
302
e.ledgerMap[p] = l
303
}
304
+ e.lock.Unlock()
305
return l
306
}
307
exchange/bitswap/decision/ledger.go
+3
@@ -1,6 +1,7 @@
1
package decision
2
3
import (
4
+ "sync"
5
"time"
6
7
key "github.com/ipfs/go-ipfs/blocks/key"
@@ -44,6 +45,8 @@ type ledger struct {
45
// sentToPeer is a set of keys to ensure we dont send duplicate blocks
46
// to a given peer
47
sentToPeer map[key.Key]time.Time
48
+
49
+ lk sync.Mutex
50
}
51
52
type debtRatio struct {
exchange/bitswap/decision/peer_request_queue.go
+54
-3
@@ -15,14 +15,16 @@ type peerRequestQueue interface {
15
Pop() *peerRequestTask
16
Push(entry wantlist.Entry, to peer.ID)
17
Remove(k key.Key, p peer.ID)
18
+
19
// NB: cannot expose simply expose taskQueue.Len because trashed elements
20
// may exist. These trashed elements should not contribute to the count.
21
}
22
22
-func newPRQ() peerRequestQueue {
23
+func newPRQ() *prq {
24
return &prq{
25
taskMap: make(map[string]*peerRequestTask),
26
partners: make(map[peer.ID]*activePartner),
27
+ frozen: make(map[peer.ID]*activePartner),
28
pQueue: pq.New(partnerCompare),
29
}
30
}
@@ -38,6 +40,8 @@ type prq struct {
40
pQueue pq.PQ
41
taskMap map[string]*peerRequestTask
42
partners map[peer.ID]*activePartner
43
+
44
+ frozen map[peer.ID]*activePartner
45
}
46
47
// Push currently adds a new peerRequestTask to the end of the list
@@ -92,7 +96,7 @@ func (tl *prq) Pop() *peerRequestTask {
96
partner := tl.pQueue.Pop().(*activePartner)
97
98
var out *peerRequestTask
95
- for partner.taskQueue.Len() > 0 {
99
+ for partner.taskQueue.Len() > 0 && partner.freezeVal == 0 {
100
out = partner.taskQueue.Pop().(*peerRequestTask)
101
delete(tl.taskMap, out.Key())
102
if out.trash {
@@ -120,11 +124,47 @@ func (tl *prq) Remove(k key.Key, p peer.ID) {
124
t.trash = true
125
126
// having canceled a block, we now account for that in the given partner
123
- tl.partners[p].requests--
127
+ partner := tl.partners[p]
128
+ partner.requests--
129
+
130
+ // we now also 'freeze' that partner. If they sent us a cancel for a
131
+ // block we were about to send them, we should wait a short period of time
132
+ // to make sure we receive any other in-flight cancels before sending
133
+ // them a block they already potentially have
134
+ if partner.freezeVal == 0 {
135
+ tl.frozen[p] = partner
136
+ }
137
+
138
+ partner.freezeVal++
139
+ tl.pQueue.Update(partner.index)
140
}
141
tl.lock.Unlock()
142
}
143
144
+func (tl *prq) fullThaw() {
145
+ tl.lock.Lock()
146
+ defer tl.lock.Unlock()
147
+
148
+ for id, partner := range tl.frozen {
149
+ partner.freezeVal = 0
150
+ delete(tl.frozen, id)
151
+ tl.pQueue.Update(partner.index)
152
+ }
153
+}
154
+
155
+func (tl *prq) thawRound() {
156
+ tl.lock.Lock()
157
+ defer tl.lock.Unlock()
158
+
159
+ for id, partner := range tl.frozen {
160
+ partner.freezeVal -= (partner.freezeVal + 1) / 2
161
+ if partner.freezeVal <= 0 {
162
+ delete(tl.frozen, id)
163
+ }
164
+ tl.pQueue.Update(partner.index)
165
+ }
166
+}
167
+
168
type peerRequestTask struct {
169
Entry wantlist.Entry
170
Target peer.ID
@@ -196,6 +236,8 @@ type activePartner struct {
236
// for the PQ interface
237
index int
238
239
+ freezeVal int
240
+
241
// priority queue of tasks belonging to this peer
242
taskQueue pq.PQ
243
}
@@ -208,6 +250,7 @@ func newActivePartner() *activePartner {
250
}
251
252
// partnerCompare implements pq.ElemComparator
253
+// returns true if peer 'a' has higher priority than peer 'b'
254
func partnerCompare(a, b pq.Elem) bool {
255
pa := a.(*activePartner)
256
pb := b.(*activePartner)
@@ -220,6 +263,14 @@ func partnerCompare(a, b pq.Elem) bool {
263
if pb.requests == 0 {
264
return true
265
}
266
+
267
+ if pa.freezeVal > pb.freezeVal {
268
+ return false
269
+ }
270
+ if pa.freezeVal < pb.freezeVal {
271
+ return true
272
+ }
273
+
274
if pa.active == pb.active {
275
// sorting by taskQueue.Len() aids in cleaning out trash entries faster
276
// if we sorted instead by requests, one peer could potentially build up
exchange/bitswap/decision/peer_request_queue_test.go
+2
@@ -47,6 +47,8 @@ func TestPushPop(t *testing.T) {
47
prq.Remove(key.Key(consonant), partner)
48
}
49
50
+ prq.fullThaw()
51
+
52
var out []string
53
for {
54
received := prq.Pop()
exchange/bitswap/network/interface.go
+7
@@ -25,9 +25,16 @@ type BitSwapNetwork interface {
25
26
ConnectTo(context.Context, peer.ID) error
27
28
+ NewMessageSender(context.Context, peer.ID) (MessageSender, error)
29
+
30
Routing
31
}
32
33
+type MessageSender interface {
34
+ SendMsg(bsmsg.BitSwapMessage) error
35
+ Close() error
36
+}
37
+
38
// Implement Receiver to receive messages from the BitSwapNetwork
39
type Receiver interface {
40
ReceiveMessage(
exchange/bitswap/network/ipfs_impl.go
+21
@@ -42,6 +42,27 @@ type impl struct {
42
receiver Receiver
43
}
44
45
+type streamMessageSender struct {
46
+ s inet.Stream
47
+}
48
+
49
+func (s *streamMessageSender) Close() error {
50
+ return s.s.Close()
51
+}
52
+
53
+func (s *streamMessageSender) SendMsg(msg bsmsg.BitSwapMessage) error {
54
+ return msg.ToNet(s.s)
55
+}
56
+
57
+func (bsnet *impl) NewMessageSender(ctx context.Context, p peer.ID) (MessageSender, error) {
58
+ s, err := bsnet.newStreamToPeer(ctx, p)
59
+ if err != nil {
60
+ return nil, err
61
+ }
62
+
63
+ return &streamMessageSender{s: s}, nil
64
+}
65
+
66
func (bsnet *impl) newStreamToPeer(ctx context.Context, p peer.ID) (inet.Stream, error) {
67
68
// first, make sure we're connected.
exchange/bitswap/testnet/virtual.go
+24
@@ -112,6 +112,30 @@ func (nc *networkClient) FindProvidersAsync(ctx context.Context, k key.Key, max
112
return out
113
}
114
115
+type messagePasser struct {
116
+ net *network
117
+ target peer.ID
118
+ local peer.ID
119
+ ctx context.Context
120
+}
121
+
122
+func (mp *messagePasser) SendMsg(m bsmsg.BitSwapMessage) error {
123
+ return mp.net.SendMessage(mp.ctx, mp.local, mp.target, m)
124
+}
125
+
126
+func (mp *messagePasser) Close() error {
127
+ return nil
128
+}
129
+
130
+func (n *networkClient) NewMessageSender(ctx context.Context, p peer.ID) (bsnet.MessageSender, error) {
131
+ return &messagePasser{
132
+ net: n.network,
133
+ target: p,
134
+ local: n.local,
135
+ ctx: ctx,
136
+ }, nil
137
+}
138
+
139
// Provide provides the key to the network
140
func (nc *networkClient) Provide(ctx context.Context, k key.Key) error {
141
return nc.routing.Provide(ctx, k)
exchange/bitswap/wantmanager.go
+31
-11
@@ -26,9 +26,11 @@ type WantManager struct {
26
27
network bsnet.BitSwapNetwork
28
ctx context.Context
29
+ cancel func()
30
}
31
32
func NewWantManager(ctx context.Context, network bsnet.BitSwapNetwork) *WantManager {
33
+ ctx, cancel := context.WithCancel(ctx)
34
return &WantManager{
35
incoming: make(chan []*bsmsg.Entry, 10),
36
connect: make(chan peer.ID, 10),
@@ -38,6 +40,7 @@ func NewWantManager(ctx context.Context, network bsnet.BitSwapNetwork) *WantMana
40
wl: wantlist.NewThreadSafe(),
41
network: network,
42
ctx: ctx,
43
+ cancel: cancel,
44
}
45
}
46
@@ -58,6 +61,8 @@ type msgQueue struct {
61
out bsmsg.BitSwapMessage
62
network bsnet.BitSwapNetwork
63
64
+ sender bsnet.MessageSender
65
+
66
refcnt int
67
68
work chan struct{}
@@ -150,6 +155,11 @@ func (pm *WantManager) stopPeerHandler(p peer.ID) {
155
}
156
157
func (mq *msgQueue) runQueue(ctx context.Context) {
158
+ defer func() {
159
+ if mq.sender != nil {
160
+ mq.sender.Close()
161
+ }
162
+ }()
163
for {
164
select {
165
case <-mq.work: // there is work to be done
@@ -166,14 +176,25 @@ func (mq *msgQueue) doWork(ctx context.Context) {
176
// allow ten minutes for connections
177
// this includes looking them up in the dht
178
// dialing them, and handshaking
169
- conctx, cancel := context.WithTimeout(ctx, time.Minute*10)
170
- defer cancel()
179
+ if mq.sender == nil {
180
+ conctx, cancel := context.WithTimeout(ctx, time.Minute*10)
181
+ defer cancel()
182
+
183
+ err := mq.network.ConnectTo(conctx, mq.p)
184
+ if err != nil {
185
+ log.Infof("cant connect to peer %s: %s", mq.p, err)
186
+ // TODO: cant connect, what now?
187
+ return
188
+ }
189
172
- err := mq.network.ConnectTo(conctx, mq.p)
173
- if err != nil {
174
- log.Infof("cant connect to peer %s: %s", mq.p, err)
175
- // TODO: cant connect, what now?
176
- return
190
+ nsender, err := mq.network.NewMessageSender(ctx, mq.p)
191
+ if err != nil {
192
+ log.Infof("cant open new stream to peer %s: %s", mq.p, err)
193
+ // TODO: cant open stream, what now?
194
+ return
195
+ }
196
+
197
+ mq.sender = nsender
198
}
199
200
// grab outgoing message
@@ -186,13 +207,12 @@ func (mq *msgQueue) doWork(ctx context.Context) {
207
mq.out = nil
208
mq.outlk.Unlock()
209
189
- sendctx, cancel := context.WithTimeout(ctx, time.Minute*5)
190
- defer cancel()
191
-
210
// send wantlist updates
193
- err = mq.network.SendMessage(sendctx, mq.p, wlm)
211
+ err := mq.sender.SendMsg(wlm)
212
if err != nil {
213
log.Infof("bitswap send error: %s", err)
214
+ mq.sender.Close()
215
+ mq.sender = nil
216
// TODO: what do we do if this fails?
217
return
218
}
test/integration/addcat_test.go
+2
@@ -149,6 +149,8 @@ func DirectAddCat(data []byte, conf testutil.LatencyConfig) error {
149
if 0 != bytes.Compare(bufout.Bytes(), data) {
150
return errors.New("catted data does not match added data")
151
}
152
+
153
+ cancel()
154
return nil
155
}
156
test/integration/grandcentral_test.go
+2
@@ -73,6 +73,7 @@ func RunSupernodeBootstrappedAddCat(data []byte, conf testutil.LatencyConfig) er
73
if 0 != bytes.Compare(bufout.Bytes(), data) {
74
return errors.New("catted data does not match added data")
75
}
76
+ cancel()
77
return nil
78
}
79
@@ -177,5 +178,6 @@ func RunSupernodePutRecordGetRecord(conf testutil.LatencyConfig) error {
178
if 0 != bytes.Compare(note, received) {
179
return errors.New("record doesn't match")
180
}
181
+ cancel()
182
return nil
183
}
test/integration/three_legged_cat_test.go
+1
@@ -128,5 +128,6 @@ func RunThreeLeggedCat(data []byte, conf testutil.LatencyConfig) error {
128
if 0 != bytes.Compare(bufout.Bytes(), data) {
129
return errors.New("catted data does not match added data")
130
}
131
+ cancel()
132
return nil
133
}