better query processing (runner)
Juan Batiz-Benet committed
Sep 18, 2014 at 19:30 UTC
a114e9cd12fea900bfbf10af1989573a17b4ad10
5 files changed
+291
-170
routing/dht/dht.go
+16
-8
@@ -177,6 +177,9 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p *peer.Peer, pmes *Message
177
if err != nil {
178
return nil, err
179
}
180
+ if rmes == nil {
181
+ return nil, errors.New("no response to request")
182
+ }
183
184
rtt := time.Since(start)
185
rmes.Peer().SetLatency(rtt)
@@ -218,19 +221,22 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer,
221
return nil, nil, err
222
}
223
224
+ u.POut("pmes.GetValue() %v\n", pmes.GetValue())
225
if value := pmes.GetValue(); value != nil {
226
// Success! We were given the value
227
+ u.POut("getValueOrPeers: got value\n")
228
return value, nil, nil
229
}
230
231
// TODO decide on providers. This probably shouldn't be happening.
227
- // if prv := pmes.GetProviderPeers(); prv != nil && len(prv) > 0 {
228
- // val, err := dht.getFromPeerList(key, timeout,, level)
229
- // if err != nil {
230
- // return nil, nil, err
231
- // }
232
- // return val, nil, nil
233
- // }
232
+ if prv := pmes.GetProviderPeers(); prv != nil && len(prv) > 0 {
233
+ val, err := dht.getFromPeerList(ctx, key, prv, level)
234
+ if err != nil {
235
+ return nil, nil, err
236
+ }
237
+ u.POut("getValueOrPeers: get from providers\n")
238
+ return val, nil, nil
239
+ }
240
241
// Perhaps we were given closer peers
242
var peers []*peer.Peer
@@ -256,10 +262,12 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer,
262
}
263
264
if len(peers) > 0 {
265
+ u.POut("getValueOrPeers: peers\n")
266
return nil, peers, nil
267
}
268
262
- return nil, nil, errors.New("NotFound. did not get value or closer peers.")
269
+ u.POut("getValueOrPeers: u.ErrNotFound\n")
270
+ return nil, nil, u.ErrNotFound
271
}
272
273
// getValueSingle simply performs the get value RPC with the given parameters
routing/dht/ext_test.go
+43
-39
@@ -18,14 +18,23 @@ import (
18
"time"
19
)
20
21
+// mesHandleFunc is a function that takes in outgoing messages
22
+// and can respond to them, simulating other peers on the network.
23
+// returning nil will chose not to respond and pass the message onto the
24
+// next registered handler
25
+type mesHandleFunc func(msg.NetMessage) msg.NetMessage
26
+
27
// fauxNet is a standin for a swarm.Network in order to more easily recreate
28
// different testing scenarios
29
type fauxSender struct {
30
handlers []mesHandleFunc
31
}
32
27
-func (f *fauxSender) SendRequest(ctx context.Context, m msg.NetMessage) (msg.NetMessage, error) {
33
+func (f *fauxSender) AddHandler(fn func(msg.NetMessage) msg.NetMessage) {
34
+ f.handlers = append(f.handlers, fn)
35
+}
36
37
+func (f *fauxSender) SendRequest(ctx context.Context, m msg.NetMessage) (msg.NetMessage, error) {
38
for _, h := range f.handlers {
39
reply := h(m)
40
if reply != nil {
@@ -33,7 +42,12 @@ func (f *fauxSender) SendRequest(ctx context.Context, m msg.NetMessage) (msg.Net
42
}
43
}
44
36
- return nil, nil
45
+ // no reply? ok force a timeout
46
+ select {
47
+ case <-ctx.Done():
48
+ }
49
+
50
+ return nil, ctx.Err()
51
}
52
53
func (f *fauxSender) SendMessage(ctx context.Context, m msg.NetMessage) error {
@@ -49,17 +63,6 @@ func (f *fauxSender) SendMessage(ctx context.Context, m msg.NetMessage) error {
63
// fauxNet is a standin for a swarm.Network in order to more easily recreate
64
// different testing scenarios
65
type fauxNet struct {
52
- handlers []mesHandleFunc
53
-}
54
-
55
-// mesHandleFunc is a function that takes in outgoing messages
56
-// and can respond to them, simulating other peers on the network.
57
-// returning nil will chose not to respond and pass the message onto the
58
-// next registered handler
59
-type mesHandleFunc func(msg.NetMessage) msg.NetMessage
60
-
61
-func (f *fauxNet) AddHandler(fn func(msg.NetMessage) msg.NetMessage) {
62
- f.handlers = append(f.handlers, fn)
66
}
67
68
// DialPeer attempts to establish a connection to a given peer
@@ -98,25 +101,23 @@ func TestGetFailures(t *testing.T) {
101
local.ID = peer.ID("test_peer")
102
103
d := NewDHT(local, peerstore, fn, fs, ds.NewMapDatastore())
101
-
104
other := &peer.Peer{ID: peer.ID("other_peer")}
103
-
104
- d.Start()
105
-
105
d.Update(other)
106
107
// This one should time out
108
+ // u.POut("Timout Test\n")
109
_, err := d.GetValue(u.Key("test"), time.Millisecond*10)
110
if err != nil {
111
- if err != u.ErrTimeout {
112
- t.Fatal("Got different error than we expected.")
111
+ if err != context.DeadlineExceeded {
112
+ t.Fatal("Got different error than we expected", err)
113
}
114
} else {
115
t.Fatal("Did not get expected error!")
116
}
117
118
+ // u.POut("NotFound Test\n")
119
// Reply with failures to every message
119
- fn.AddHandler(func(mes msg.NetMessage) msg.NetMessage {
120
+ fs.AddHandler(func(mes msg.NetMessage) msg.NetMessage {
121
pmes := new(Message)
122
err := proto.Unmarshal(mes.Data(), pmes)
123
if err != nil {
@@ -140,18 +141,7 @@ func TestGetFailures(t *testing.T) {
141
t.Fatal("expected error, got none.")
142
}
143
143
- success := make(chan struct{})
144
- fn.handlers = nil
145
- fn.AddHandler(func(mes msg.NetMessage) msg.NetMessage {
146
- resp := new(Message)
147
- err := proto.Unmarshal(mes.Data(), resp)
148
- if err != nil {
149
- t.Fatal(err)
150
- }
151
- success <- struct{}{}
152
- return nil
153
- })
154
-
144
+ fs.handlers = nil
145
// Now we test this DHT's handleGetValue failure
146
typ := Message_GET_VALUE
147
str := "hello"
@@ -161,17 +151,32 @@ func TestGetFailures(t *testing.T) {
151
Value: []byte{0},
152
}
153
154
+ // u.POut("handleGetValue Test\n")
155
mes, err := msg.FromObject(other, &req)
156
if err != nil {
157
t.Error(err)
158
}
159
169
- mes, err = fs.SendRequest(ctx, mes)
160
+ mes, err = d.HandleMessage(ctx, mes)
161
if err != nil {
162
t.Error(err)
163
}
164
174
- <-success
165
+ pmes := new(Message)
166
+ err = proto.Unmarshal(mes.Data(), pmes)
167
+ if err != nil {
168
+ t.Fatal(err)
169
+ }
170
+ if pmes.GetValue() != nil {
171
+ t.Fatal("shouldnt have value")
172
+ }
173
+ if pmes.GetCloserPeers() != nil {
174
+ t.Fatal("shouldnt have closer peers")
175
+ }
176
+ if pmes.GetProviderPeers() != nil {
177
+ t.Fatal("shouldnt have provider peers")
178
+ }
179
+
180
}
181
182
// TODO: Maybe put these in some sort of "ipfs_testutil" package
@@ -192,7 +197,6 @@ func TestNotFound(t *testing.T) {
197
peerstore := peer.NewPeerstore()
198
199
d := NewDHT(local, peerstore, fn, fs, ds.NewMapDatastore())
195
- d.Start()
200
201
var ps []*peer.Peer
202
for i := 0; i < 5; i++ {
@@ -201,7 +205,7 @@ func TestNotFound(t *testing.T) {
205
}
206
207
// Reply with random peers to every message
204
- fn.AddHandler(func(mes msg.NetMessage) msg.NetMessage {
208
+ fs.AddHandler(func(mes msg.NetMessage) msg.NetMessage {
209
pmes := new(Message)
210
err := proto.Unmarshal(mes.Data(), pmes)
211
if err != nil {
@@ -228,7 +232,8 @@ func TestNotFound(t *testing.T) {
232
233
})
234
231
- _, err := d.GetValue(u.Key("hello"), time.Second*30)
235
+ v, err := d.GetValue(u.Key("hello"), time.Second*5)
236
+ u.POut("get value got %v\n", v)
237
if err != nil {
238
switch err {
239
case u.ErrNotFound:
@@ -254,7 +259,6 @@ func TestLessThanKResponses(t *testing.T) {
259
local.ID = peer.ID("test_peer")
260
261
d := NewDHT(local, peerstore, fn, fs, ds.NewMapDatastore())
257
- d.Start()
262
263
var ps []*peer.Peer
264
for i := 0; i < 5; i++ {
@@ -264,7 +268,7 @@ func TestLessThanKResponses(t *testing.T) {
268
other := _randPeer()
269
270
// Reply with random peers to every message
267
- fn.AddHandler(func(mes msg.NetMessage) msg.NetMessage {
271
+ fs.AddHandler(func(mes msg.NetMessage) msg.NetMessage {
272
pmes := new(Message)
273
err := proto.Unmarshal(mes.Data(), pmes)
274
if err != nil {
routing/dht/handlers.go
+7
-9
@@ -58,7 +58,10 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error
58
return nil, err
59
}
60
61
- // if we have the value, respond with it!
61
+ // Note: changed the behavior here to return _as much_ info as possible
62
+ // (potentially all of {value, closer peers, provider})
63
+
64
+ // if we have the value, send it back
65
if err == nil {
66
u.DOut("handleGetValue success!\n")
67
@@ -68,7 +71,6 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error
71
}
72
73
resp.Value = byts
71
- return resp, nil
74
}
75
76
// if we know any providers for the requested value, return those.
@@ -76,20 +78,16 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error
78
if len(provs) > 0 {
79
u.DOut("handleGetValue returning %d provider[s]\n", len(provs))
80
resp.ProviderPeers = peersToPBPeers(provs)
79
- return resp, nil
81
}
82
83
// Find closest peer on given cluster to desired key and reply with that info
84
closer := dht.betterPeerToQuery(pmes)
84
- if closer == nil {
85
- u.DOut("handleGetValue could not find a closer node than myself.\n")
86
- resp.CloserPeers = nil
85
+ if closer != nil {
86
+ u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty())
87
+ resp.CloserPeers = peersToPBPeers([]*peer.Peer{closer})
88
return resp, nil
89
}
90
90
- // we got a closer peer, it seems. return it.
91
- u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty())
92
- resp.CloserPeers = peersToPBPeers([]*peer.Peer{closer})
91
return resp, nil
92
}
93
routing/dht/query.go
+181
-50
@@ -1,19 +1,45 @@
1
package dht
2
3
import (
4
+ "sync"
5
+
6
peer "github.com/jbenet/go-ipfs/peer"
7
queue "github.com/jbenet/go-ipfs/peer/queue"
8
+ kb "github.com/jbenet/go-ipfs/routing/kbucket"
9
u "github.com/jbenet/go-ipfs/util"
10
+ todoctr "github.com/jbenet/go-ipfs/util/todocounter"
11
12
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
13
)
14
15
+const maxQueryConcurrency = 5
16
+
17
type dhtQuery struct {
12
- // a PeerQueue
13
- peers queue.PeerQueue
18
+ // the key we're querying for
19
+ key u.Key
20
21
// the function to execute per peer
22
qfunc queryFunc
23
+
24
+ // the concurrency parameter
25
+ concurrency int
26
+}
27
+
28
+type dhtQueryResult struct {
29
+ value []byte // GetValue
30
+ peer *peer.Peer // FindPeer
31
+ providerPeers []*peer.Peer // GetProviders
32
+ closerPeers []*peer.Peer // *
33
+ success bool
34
+}
35
+
36
+// constructs query
37
+func newQuery(k u.Key, f queryFunc) *dhtQuery {
38
+ return &dhtQuery{
39
+ key: k,
40
+ qfunc: f,
41
+ concurrency: maxQueryConcurrency,
42
+ }
43
}
44
45
// QueryFunc is a function that runs a particular query with a given peer.
@@ -21,65 +47,170 @@ type dhtQuery struct {
47
// - the value
48
// - a list of peers potentially better able to serve the query
49
// - an error
24
-type queryFunc func(context.Context, *peer.Peer) (interface{}, []*peer.Peer, error)
50
+type queryFunc func(context.Context, *peer.Peer) (*dhtQueryResult, error)
51
+
52
+// Run runs the query at hand. pass in a list of peers to use first.
53
+func (q *dhtQuery) Run(ctx context.Context, peers []*peer.Peer) (*dhtQueryResult, error) {
54
+ runner := newQueryRunner(ctx, q)
55
+ return runner.Run(peers)
56
+}
57
+
58
+type dhtQueryRunner struct {
59
+
60
+ // the query to run
61
+ query *dhtQuery
62
+
63
+ // peersToQuery is a list of peers remaining to query
64
+ peersToQuery *queue.ChanQueue
65
+
66
+ // peersSeen are all the peers queried. used to prevent querying same peer 2x
67
+ peersSeen peer.Map
68
26
-func (q *dhtQuery) Run(ctx context.Context, concurrency int) (interface{}, error) {
27
- // get own cancel function to signal when we've found the value
69
+ // rateLimit is a channel used to rate limit our processing (semaphore)
70
+ rateLimit chan struct{}
71
+
72
+ // peersRemaining is a counter of peers remaining (toQuery + processing)
73
+ peersRemaining todoctr.Counter
74
+
75
+ // context
76
+ ctx context.Context
77
+ cancel context.CancelFunc
78
+
79
+ // result
80
+ result *dhtQueryResult
81
+
82
+ // result errors
83
+ errs []error
84
+
85
+ // lock for concurrent access to fields
86
+ sync.RWMutex
87
+}
88
+
89
+func newQueryRunner(ctx context.Context, q *dhtQuery) *dhtQueryRunner {
90
ctx, cancel := context.WithCancel(ctx)
91
30
- // the variable waiting to be populated upon success
31
- var result interface{}
32
-
33
- // chanQueue is how workers receive their work
34
- chanQueue := queue.NewChanQueue(ctx, q.peers)
35
-
36
- // worker
37
- worker := func() {
38
- for {
39
- select {
40
- case p := <-chanQueue.DeqChan:
41
-
42
- val, closer, err := q.qfunc(ctx, p)
43
- if err != nil {
44
- u.PErr("error running query: %v\n", err)
45
- continue
46
- }
47
-
48
- if val != nil {
49
- result = val
50
- cancel() // signal we're done.
51
- return
52
- }
53
-
54
- if closer != nil {
55
- for _, p := range closer {
56
- select {
57
- case chanQueue.EnqChan <- p:
58
- case <-ctx.Done():
59
- return
60
- }
61
- }
62
- }
63
-
64
- case <-ctx.Done():
65
- return
66
- }
67
- }
92
+ return &dhtQueryRunner{
93
+ ctx: ctx,
94
+ cancel: cancel,
95
+ query: q,
96
+ peersToQuery: queue.NewChanQueue(ctx, queue.NewXORDistancePQ(q.key)),
97
+ peersRemaining: todoctr.NewSyncCounter(),
98
+ peersSeen: peer.Map{},
99
+ rateLimit: make(chan struct{}, q.concurrency),
100
+ }
101
+}
102
+
103
+func (r *dhtQueryRunner) Run(peers []*peer.Peer) (*dhtQueryResult, error) {
104
+ // setup concurrency rate limiting
105
+ for i := 0; i < r.query.concurrency; i++ {
106
+ r.rateLimit <- struct{}{}
107
}
108
70
- // launch all workers
71
- for i := 0; i < concurrency; i++ {
72
- go worker()
109
+ // add all the peers we got first.
110
+ for _, p := range peers {
111
+ r.addPeerToQuery(p, nil) // don't have access to self here...
112
}
113
114
// wait until we're done. yep.
115
select {
77
- case <-ctx.Done():
116
+ case <-r.peersRemaining.Done():
117
+ r.cancel() // ran all and nothing. cancel all outstanding workers.
118
+
119
+ r.RLock()
120
+ defer r.RUnlock()
121
+
122
+ if len(r.errs) > 0 {
123
+ return nil, r.errs[0]
124
+ }
125
+ return nil, u.ErrNotFound
126
+
127
+ case <-r.ctx.Done():
128
+ r.RLock()
129
+ defer r.RUnlock()
130
+
131
+ if r.result != nil && r.result.success {
132
+ return r.result, nil
133
+ }
134
+ return nil, r.ctx.Err()
135
+ }
136
+
137
+}
138
+
139
+func (r *dhtQueryRunner) addPeerToQuery(next *peer.Peer, benchmark *peer.Peer) {
140
+ if next == nil {
141
+ // wtf why are peers nil?!?
142
+ u.PErr("Query getting nil peers!!!\n")
143
+ return
144
+ }
145
+
146
+ // if new peer further away than whom we got it from, bother (loops)
147
+ if benchmark != nil && kb.Closer(benchmark.ID, next.ID, r.query.key) {
148
+ return
149
+ }
150
+
151
+ // if already seen, no need.
152
+ r.Lock()
153
+ _, found := r.peersSeen[next.Key()]
154
+ if found {
155
+ r.Unlock()
156
+ return
157
+ }
158
+ r.peersSeen[next.Key()] = next
159
+ r.Unlock()
160
+
161
+ // do this after unlocking to prevent possible deadlocks.
162
+ r.peersRemaining.Increment(1)
163
+ select {
164
+ case r.peersToQuery.EnqChan <- next:
165
+ case <-r.ctx.Done():
166
}
167
+}
168
+
169
+func (r *dhtQueryRunner) spawnWorkers(p *peer.Peer) {
170
+ for {
171
+ select {
172
+ case <-r.peersRemaining.Done():
173
+ return
174
+
175
+ case <-r.ctx.Done():
176
+ return
177
+
178
+ case p := <-r.peersToQuery.DeqChan:
179
+ go r.queryPeer(p)
180
+ }
181
+ }
182
+}
183
80
- if result != nil {
81
- return result, nil
184
+func (r *dhtQueryRunner) queryPeer(p *peer.Peer) {
185
+ // make sure we rate limit concurrency.
186
+ select {
187
+ case <-r.rateLimit:
188
+ case <-r.ctx.Done():
189
+ r.peersRemaining.Decrement(1)
190
+ return
191
+ }
192
+
193
+ // finally, run the query against this peer
194
+ res, err := r.query.qfunc(r.ctx, p)
195
+
196
+ if err != nil {
197
+ r.Lock()
198
+ r.errs = append(r.errs, err)
199
+ r.Unlock()
200
+
201
+ } else if res.success {
202
+ r.Lock()
203
+ r.result = res
204
+ r.Unlock()
205
+ r.cancel() // signal to everyone that we're done.
206
+
207
+ } else if res.closerPeers != nil {
208
+ for _, next := range res.closerPeers {
209
+ r.addPeerToQuery(next, p)
210
+ }
211
}
212
84
- return nil, ctx.Err()
213
+ // signal we're done proccessing peer p
214
+ r.peersRemaining.Decrement(1)
215
+ r.rateLimit <- struct{}{}
216
}
routing/dht/routing.go
+44
-64
@@ -3,14 +3,11 @@ package dht
3
import (
4
"bytes"
5
"encoding/json"
6
- "errors"
7
- "fmt"
6
"time"
7
8
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
9
10
peer "github.com/jbenet/go-ipfs/peer"
13
- queue "github.com/jbenet/go-ipfs/peer/queue"
11
kb "github.com/jbenet/go-ipfs/routing/kbucket"
12
u "github.com/jbenet/go-ipfs/util"
13
)
@@ -24,28 +21,23 @@ import (
21
func (dht *IpfsDHT) PutValue(key u.Key, value []byte) error {
22
ctx := context.TODO()
23
27
- query := &dhtQuery{}
28
- query.peers = queue.NewXORDistancePQ(key)
24
+ peers := []*peer.Peer{}
25
26
// get the peers we need to announce to
27
for _, route := range dht.routingTables {
32
- peers := route.NearestPeers(kb.ConvertKey(key), KValue)
33
- for _, p := range peers {
34
- if p == nil {
35
- // this shouldn't be happening.
36
- panic("p should not be nil")
37
- }
38
-
39
- query.peers.Enqueue(p)
40
- }
28
+ npeers := route.NearestPeers(kb.ConvertKey(key), KValue)
29
+ peers = append(peers, npeers...)
30
}
31
43
- query.qfunc = func(ctx context.Context, p *peer.Peer) (interface{}, []*peer.Peer, error) {
44
- dht.putValueToNetwork(ctx, p, string(key), value)
45
- return nil, nil, nil
46
- }
32
+ query := newQuery(key, func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) {
33
+ err := dht.putValueToNetwork(ctx, p, string(key), value)
34
+ if err != nil {
35
+ return nil, err
36
+ }
37
+ return &dhtQueryResult{success: true}, nil
38
+ })
39
48
- _, err := query.Run(ctx, query.peers.Len())
40
+ _, err := query.Run(ctx, peers)
41
return err
42
}
43
@@ -63,7 +55,6 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) {
55
val, err := dht.getLocal(key)
56
if err == nil {
57
ll.Success = true
66
- u.DOut("Found local, returning.\n")
58
return val, nil
59
}
60
@@ -74,30 +65,33 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) {
65
return nil, kb.ErrLookupFailure
66
}
67
77
- query := &dhtQuery{}
78
- query.peers = queue.NewXORDistancePQ(key)
68
+ // setup the Query
69
+ query := newQuery(key, func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) {
70
80
- // get the peers we need to announce to
81
- for _, p := range closest {
82
- query.peers.Enqueue(p)
83
- }
71
+ val, peers, err := dht.getValueOrPeers(ctx, p, key, routeLevel)
72
+ if err != nil {
73
+ return nil, err
74
+ }
75
85
- // setup the Query Function
86
- query.qfunc = func(ctx context.Context, p *peer.Peer) (interface{}, []*peer.Peer, error) {
87
- return dht.getValueOrPeers(ctx, p, key, routeLevel)
88
- }
76
+ res := &dhtQueryResult{value: val, closerPeers: peers}
77
+ if val != nil {
78
+ res.success = true
79
+ }
80
+
81
+ return res, nil
82
+ })
83
84
// run it!
91
- result, err := query.Run(ctx, query.peers.Len())
85
+ result, err := query.Run(ctx, closest)
86
if err != nil {
87
return nil, err
88
}
89
96
- byt, ok := result.([]byte)
97
- if !ok {
98
- return nil, fmt.Errorf("received non-byte slice value")
90
+ if result.value == nil {
91
+ return nil, u.ErrNotFound
92
}
100
- return byt, nil
93
+
94
+ return result.value, nil
95
}
96
97
// Value provider layer of indirection.
@@ -278,25 +272,19 @@ func (dht *IpfsDHT) findPeerMultiple(id peer.ID, timeout time.Duration) (*peer.P
272
return p, nil
273
}
274
281
- query := &dhtQuery{}
282
- query.peers = queue.NewXORDistancePQ(u.Key(id))
283
-
275
// get the peers we need to announce to
276
routeLevel := 0
277
peers := dht.routingTables[routeLevel].NearestPeers(kb.ConvertPeerID(id), AlphaValue)
278
if len(peers) == 0 {
279
return nil, kb.ErrLookupFailure
280
}
290
- for _, p := range peers {
291
- query.peers.Enqueue(p)
292
- }
281
282
// setup query function
295
- query.qfunc = func(ctx context.Context, p *peer.Peer) (interface{}, []*peer.Peer, error) {
283
+ query := newQuery(u.Key(id), func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) {
284
pmes, err := dht.findPeerSingle(ctx, p, id, routeLevel)
285
if err != nil {
286
u.DErr("getPeer error: %v\n", err)
299
- return nil, nil, err
287
+ return nil, err
288
}
289
290
plist := pmes.GetCloserPeers()
@@ -313,25 +301,24 @@ func (dht *IpfsDHT) findPeerMultiple(id peer.ID, timeout time.Duration) (*peer.P
301
}
302
303
if nxtp.ID.Equal(id) {
316
- return nxtp, nil, nil
304
+ return &dhtQueryResult{peer: nxtp, success: true}, nil
305
}
306
307
nxtprs[i] = nxtp
308
}
309
322
- return nil, nxtprs, nil
323
- }
310
+ return &dhtQueryResult{closerPeers: nxtprs}, nil
311
+ })
312
325
- p5, err := query.Run(ctx, query.peers.Len())
313
+ result, err := query.Run(ctx, peers)
314
if err != nil {
315
return nil, err
316
}
317
330
- p6, ok := p5.(*peer.Peer)
331
- if !ok {
332
- return nil, errors.New("received non peer object")
318
+ if result.peer == nil {
319
+ return nil, u.ErrNotFound
320
}
334
- return p6, nil
321
+ return result.peer, nil
322
}
323
324
// Ping a peer, log the time it took
@@ -350,21 +337,14 @@ func (dht *IpfsDHT) getDiagnostic(timeout time.Duration) ([]*diagInfo, error) {
337
ctx, _ := context.WithTimeout(context.TODO(), timeout)
338
339
u.DOut("Begin Diagnostic")
353
- query := &dhtQuery{}
354
- query.peers = queue.NewXORDistancePQ(u.Key(dht.self.ID))
355
-
356
- targets := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10)
357
- for _, p := range targets {
358
- query.peers.Enqueue(p)
359
- }
360
-
340
+ peers := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10)
341
var out []*diagInfo
342
363
- query.qfunc = func(ctx context.Context, p *peer.Peer) (interface{}, []*peer.Peer, error) {
343
+ query := newQuery(dht.self.Key(), func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) {
344
pmes := newMessage(Message_DIAGNOSTIC, "", 0)
345
rpmes, err := dht.sendRequest(ctx, p, pmes)
346
if err != nil {
367
- return nil, nil, err
347
+ return nil, err
348
}
349
350
dec := json.NewDecoder(bytes.NewBuffer(rpmes.GetValue()))
@@ -377,9 +357,9 @@ func (dht *IpfsDHT) getDiagnostic(timeout time.Duration) ([]*diagInfo, error) {
357
358
out = append(out, di)
359
}
380
- return nil, nil, nil
381
- }
360
+ return &dhtQueryResult{success: true}, nil
361
+ })
362
383
- _, err := query.Run(ctx, query.peers.Len())
363
+ _, err := query.Run(ctx, peers)
364
return out, err
365
}