@@ -3,13 +3,10 @@ package io
3
3
import (
4
4
"context"
5
5
"fmt"
6
- "os"
7
- "time"
8
-
9
6
mdag "github.com/ipfs/go-merkledag"
10
-
11
7
format "github.com/ipfs/go-unixfs"
12
8
"github.com/ipfs/go-unixfs/hamt"
9
+ "os"
13
10
14
11
"github.com/ipfs/go-cid"
15
12
ipld "github.com/ipfs/go-ipld-format"
@@ -26,11 +23,6 @@ var log = logging.Logger("unixfs")
26
23
// ProtoNode doesn't use the Data field so this estimate is pretty accurate).
27
24
var HAMTShardingSize = 0
28
25
29
- // Time in seconds allowed to fetch the shards to compute the size before
30
- // returning an error.
31
- // FIXME: Adjust to sane value.
32
- var EvaluateHAMTTransitionTimeout = time .Duration (1 )
33
-
34
26
// DefaultShardWidth is the default value used for hamt sharding width.
35
27
var DefaultShardWidth = 256
36
28
@@ -438,15 +430,13 @@ func (d *HAMTDirectory) removeFromSizeChange(name string, linkCid cid.Cid) {
438
430
d .sizeChange -= estimatedLinkSize (name , linkCid )
439
431
}
440
432
441
- // Evaluate directory size and check if it's below HAMTShardingSize threshold
442
- // (to trigger a transition to a BasicDirectory). It returns two `bool`s:
443
- // * whether it's below (true) or equal/above (false)
444
- // * whether the passed timeout to compute the size has been exceeded
433
+ // Evaluate directory size and a future sizeChange and check if it will be below
434
+ // HAMTShardingSize threshold (to trigger a transition to a BasicDirectory).
445
435
// Instead of enumerating the entire tree we eagerly call EnumLinksAsync
446
- // until we either reach a value above the threshold (in that case no need)
447
- // to keep counting or the timeout runs out in which case the `below` return
448
- // value is not to be trusted as we didn't have time to count enough shards.
449
- func (d * HAMTDirectory ) sizeBelowThreshold (timeout time. Duration ) (below bool , timeoutExceeded bool ) {
436
+ // until we either reach a value above the threshold (in that case no need
437
+ // to keep counting) or an error occurs (like the context being canceled
438
+ // if we take too much time fetching the necessary shards) .
439
+ func (d * HAMTDirectory ) sizeBelowThreshold (ctx context. Context , sizeChange int ) (below bool , err error ) {
450
440
if HAMTShardingSize == 0 {
451
441
panic ("asked to compute HAMT size with HAMTShardingSize option off (0)" )
452
442
}
@@ -455,57 +445,54 @@ func (d *HAMTDirectory) sizeBelowThreshold(timeout time.Duration) (below bool, t
455
445
// end early if we already know we're above the threshold or run out of time.
456
446
partialSize := 0
457
447
458
- ctx , cancel := context .WithTimeout (context .Background (), time .Second * timeout )
448
+ // We stop the enumeration once we have enough information and exit this function.
449
+ ctx , cancel := context .WithCancel (ctx )
459
450
defer cancel ()
451
+
460
452
for linkResult := range d .EnumLinksAsync (ctx ) {
461
453
if linkResult .Err != nil {
462
- continue
463
- // The timeout exceeded errors will be coming through here but I'm
464
- // not sure if we can just compare against a generic DeadlineExceeded
465
- // error here to return early and avoid iterating the entire loop.
466
- // (We might confuse a specific DeadlineExceeded of an internal function
467
- // with our context here.)
468
- // Since *our* DeadlineExceeded will quickly propagate to any other
469
- // pending fetches it seems that iterating the entire loop won't add
470
- // much more cost anyway.
471
- // FIXME: Check the above reasoning.
454
+ return false , linkResult .Err
472
455
}
473
- if linkResult .Link == nil {
474
- panic ("empty link result (both values nil)" )
475
- // FIXME: Is this *ever* possible?
476
- }
477
- partialSize += estimatedLinkSize (linkResult .Link .Name , linkResult .Link .Cid )
478
456
479
- if partialSize >= HAMTShardingSize {
457
+ partialSize += estimatedLinkSize (linkResult .Link .Name , linkResult .Link .Cid )
458
+ if partialSize + sizeChange >= HAMTShardingSize {
480
459
// We have already fetched enough shards to assert we are
481
460
// above the threshold, so no need to keep fetching.
482
- return false , false
461
+ return false , nil
483
462
}
484
463
}
485
- // At this point either we enumerated all shards or run out of time.
486
- // Figure out which.
487
464
488
- if ctx .Err () == context .Canceled {
489
- panic ("the context was canceled but we're still evaluating a possible switch" )
465
+ // We enumerated *all* links in all shards and didn't reach the threshold.
466
+ return true , nil
467
+ }
468
+
469
+ // FIXME: Will be extended later to the `AddEntry` case.
470
+ func (d * HAMTDirectory ) needsToSwitchToBasicDir (ctx context.Context , nameToRemove string ) (switchToBasic bool , err error ) {
471
+ if HAMTShardingSize == 0 { // Option disabled.
472
+ return false , nil
490
473
}
491
- if partialSize >= HAMTShardingSize {
492
- panic ("we reach the threshold but we're still evaluating a possible switch" )
474
+
475
+ entryToRemove , err := d .shard .Find (ctx , nameToRemove )
476
+ if err == os .ErrNotExist {
477
+ // Nothing to remove, no point in evaluating a switch.
478
+ return false , nil
479
+ } else if err != nil {
480
+ return false , err
493
481
}
482
+ sizeToRemove := estimatedLinkSize (nameToRemove , entryToRemove .Cid )
494
483
495
- if ctx .Err () == context .DeadlineExceeded {
496
- return false , true
484
+ if d .sizeChange - sizeToRemove >= 0 {
485
+ // We won't have reduced the HAMT net size.
486
+ return false , nil
497
487
}
498
488
499
- // If we reach this then:
500
- // * We are below the threshold (we didn't return inside the EnumLinksAsync
501
- // loop).
502
- // * The context wasn't cancelled so we iterated *all* shards
503
- // and are sure that we have the full size.
504
- // FIXME: Can we actually verify the last claim here to be sure?
505
- // (Iterating all the shards in the HAMT as a plumbing function maybe.
506
- // If they're in memory it shouldn't be that expensive, we won't be
507
- // switching that often, probably.)
508
- return true , false
489
+ // We have reduced the directory size, check if went below the
490
+ // HAMTShardingSize threshold to trigger a switch.
491
+ belowThreshold , err := d .sizeBelowThreshold (ctx , - sizeToRemove )
492
+ if err != nil {
493
+ return false , err
494
+ }
495
+ return belowThreshold , nil
509
496
}
510
497
511
498
// UpgradeableDirectory wraps a Directory interface and provides extra logic
@@ -573,42 +560,21 @@ func (d *UpgradeableDirectory) getDagService() ipld.DAGService {
573
560
// sure we make good on the value). Finding the right margin can be tricky
574
561
// and very dependent on the use case so it might not be worth it.
575
562
func (d * UpgradeableDirectory ) RemoveChild (ctx context.Context , name string ) error {
576
- if err := d .Directory .RemoveChild (ctx , name ); err != nil {
577
- return err
578
- }
579
-
580
563
hamtDir , ok := d .Directory .(* HAMTDirectory )
581
- if ! ok { // BasicDirectory
582
- return nil
583
- }
584
-
585
- if HAMTShardingSize == 0 || // Option disabled.
586
- hamtDir .sizeChange >= 0 { // We haven't reduced the HAMT net size.
587
- return nil
588
- }
589
-
590
- // We have reduced the directory size, check if it didn't go under
591
- // the HAMTShardingSize threshold.
592
- belowThreshold , timeoutExceeded := hamtDir .sizeBelowThreshold (EvaluateHAMTTransitionTimeout )
593
-
594
- if timeoutExceeded {
595
- // We run out of time before confirming if we're indeed below the
596
- // threshold. When in doubt error to not return inconsistent structures.
597
- // FIXME: We could allow this to return without error and enforce this
598
- // timeout on a GetNode() call when we need to actually commit to a
599
- // structure/CID. (The downside is that GetNode() doesn't have a
600
- // context argument and we would have to break the API.)
601
- return fmt .Errorf ("not enought time to fetch shards" )
602
- // FIXME: Abstract in new error for testing.
603
- }
604
-
605
- if belowThreshold { // Switch.
606
- basicDir , err := hamtDir .switchToBasic (ctx )
564
+ if ok {
565
+ switchToBasic , err := hamtDir .needsToSwitchToBasicDir (ctx , name )
607
566
if err != nil {
608
567
return err
609
568
}
610
- d .Directory = basicDir
569
+
570
+ if switchToBasic {
571
+ basicDir , err := hamtDir .switchToBasic (ctx )
572
+ if err != nil {
573
+ return err
574
+ }
575
+ d .Directory = basicDir
576
+ }
611
577
}
612
578
613
- return nil
579
+ return d . Directory . RemoveChild ( ctx , name )
614
580
}
0 commit comments