don't fail promises that already succeeded
License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>
Jeromy committed
Feb 10, 2016 at 21:42 UTC
cf7f5da42659ea1dab7e3aec6b3950cf2b91773c
2 files changed
+87
-14
merkledag/merkledag.go
+43
-14
@@ -176,9 +176,8 @@ func GetNodes(ctx context.Context, ds DAGService, keys []key.Key) []NodeGetter {
176
}
177
178
promises := make([]NodeGetter, len(keys))
179
- sendChans := make([]chan<- *Node, len(keys))
179
for i := range keys {
181
- promises[i], sendChans[i] = newNodePromise(ctx)
180
+ promises[i] = newNodePromise(ctx)
181
}
182
183
dedupedKeys := dedupeKeys(keys)
@@ -199,7 +198,9 @@ func GetNodes(ctx context.Context, ds DAGService, keys []key.Key) []NodeGetter {
198
}
199
200
if opt.Err != nil {
202
- log.Error("error fetching: ", opt.Err)
201
+ for _, p := range promises {
202
+ p.Fail(opt.Err)
203
+ }
204
return
205
}
206
@@ -214,7 +215,7 @@ func GetNodes(ctx context.Context, ds DAGService, keys []key.Key) []NodeGetter {
215
is := FindLinks(keys, k, 0)
216
for _, i := range is {
217
count++
217
- sendChans[i] <- nd
218
+ promises[i].Send(nd)
219
}
220
case <-ctx.Done():
221
return
@@ -237,18 +238,18 @@ func dedupeKeys(ks []key.Key) []key.Key {
238
return out
239
}
240
240
-func newNodePromise(ctx context.Context) (NodeGetter, chan<- *Node) {
241
- ch := make(chan *Node, 1)
241
+func newNodePromise(ctx context.Context) NodeGetter {
242
return &nodePromise{
243
- recv: ch,
243
+ recv: make(chan *Node, 1),
244
ctx: ctx,
245
err: make(chan error, 1),
246
- }, ch
246
+ }
247
}
248
249
type nodePromise struct {
250
cache *Node
251
- recv <-chan *Node
251
+ clk sync.Mutex
252
+ recv chan *Node
253
ctx context.Context
254
err chan error
255
}
@@ -260,20 +261,49 @@ type nodePromise struct {
261
type NodeGetter interface {
262
Get(context.Context) (*Node, error)
263
Fail(err error)
264
+ Send(*Node)
265
}
266
267
func (np *nodePromise) Fail(err error) {
268
+ np.clk.Lock()
269
+ v := np.cache
270
+ np.clk.Unlock()
271
+
272
+ // if promise has a value, don't fail it
273
+ if v != nil {
274
+ return
275
+ }
276
+
277
np.err <- err
278
}
279
269
-func (np *nodePromise) Get(ctx context.Context) (*Node, error) {
280
+func (np *nodePromise) Send(nd *Node) {
281
+ var already bool
282
+ np.clk.Lock()
283
if np.cache != nil {
271
- return np.cache, nil
284
+ already = true
285
+ }
286
+ np.cache = nd
287
+ np.clk.Unlock()
288
+
289
+ if already {
290
+ panic("sending twice to the same promise is an error!")
291
+ }
292
+
293
+ np.recv <- nd
294
+}
295
+
296
+func (np *nodePromise) Get(ctx context.Context) (*Node, error) {
297
+ np.clk.Lock()
298
+ c := np.cache
299
+ np.clk.Unlock()
300
+ if c != nil {
301
+ return c, nil
302
}
303
304
select {
275
- case blk := <-np.recv:
276
- np.cache = blk
305
+ case nd := <-np.recv:
306
+ return nd, nil
307
case <-np.ctx.Done():
308
return nil, np.ctx.Err()
309
case <-ctx.Done():
@@ -281,7 +311,6 @@ func (np *nodePromise) Get(ctx context.Context) (*Node, error) {
311
case err := <-np.err:
312
return nil, err
313
}
284
- return np.cache, nil
314
}
315
316
type Batch struct {
merkledag/merkledag_test.go
+44
@@ -20,6 +20,7 @@ import (
20
imp "github.com/ipfs/go-ipfs/importer"
21
chunk "github.com/ipfs/go-ipfs/importer/chunk"
22
. "github.com/ipfs/go-ipfs/merkledag"
23
+ dstest "github.com/ipfs/go-ipfs/merkledag/test"
24
"github.com/ipfs/go-ipfs/pin"
25
uio "github.com/ipfs/go-ipfs/unixfs/io"
26
u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
@@ -323,3 +324,46 @@ func TestEnumerateChildren(t *testing.T) {
324
325
traverse(root)
326
}
327
+
328
+func TestFetchFailure(t *testing.T) {
329
+ ds := dstest.Mock()
330
+ ds_bad := dstest.Mock()
331
+
332
+ top := new(Node)
333
+ for i := 0; i < 10; i++ {
334
+ nd := &Node{Data: []byte{byte('a' + i)}}
335
+ _, err := ds.Add(nd)
336
+ if err != nil {
337
+ t.Fatal(err)
338
+ }
339
+
340
+ err = top.AddNodeLinkClean(fmt.Sprintf("AA%d", i), nd)
341
+ if err != nil {
342
+ t.Fatal(err)
343
+ }
344
+ }
345
+
346
+ for i := 0; i < 10; i++ {
347
+ nd := &Node{Data: []byte{'f', 'a' + byte(i)}}
348
+ _, err := ds_bad.Add(nd)
349
+ if err != nil {
350
+ t.Fatal(err)
351
+ }
352
+
353
+ err = top.AddNodeLinkClean(fmt.Sprintf("BB%d", i), nd)
354
+ if err != nil {
355
+ t.Fatal(err)
356
+ }
357
+ }
358
+
359
+ getters := GetDAG(context.Background(), ds, top)
360
+ for i, getter := range getters {
361
+ _, err := getter.Get(context.Background())
362
+ if err != nil && i < 10 {
363
+ t.Fatal(err)
364
+ }
365
+ if err == nil && i >= 10 {
366
+ t.Fatal("should have failed request")
367
+ }
368
+ }
369
+}