@cryptotaxi247 / kubo / commits / cf7f5da42

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