@@ -430,29 +430,42 @@ func (d *HAMTDirectory) removeFromSizeChange(name string, linkCid cid.Cid) {
430
430
d .sizeChange -= estimatedLinkSize (name , linkCid )
431
431
}
432
432
433
- // FIXME: Will be extended later to the `AddEntry` case.
434
- func (d * HAMTDirectory ) needsToSwitchToBasicDir (ctx context.Context , nameToRemove string ) (switchToBasic bool , err error ) {
433
+ // Evaluate a switch from HAMTDirectory to BasicDirectory in case the size will
434
+ // go above the threshold when we are adding or removing an entry.
435
+ // In both the add/remove operations any old name will be removed, and for the
436
+ // add operation in particular a new entry will be added under that name (otherwise
437
+ // nodeToAdd is nil). We compute both (potential) future subtraction and
438
+ // addition to the size change.
439
+ func (d * HAMTDirectory ) needsToSwitchToBasicDir (ctx context.Context , name string , nodeToAdd ipld.Node ) (switchToBasic bool , err error ) {
435
440
if HAMTShardingSize == 0 { // Option disabled.
436
441
return false , nil
437
442
}
438
443
439
- entryToRemove , err := d .shard .Find (ctx , nameToRemove )
440
- if err == os .ErrNotExist {
441
- // Nothing to remove, no point in evaluating a switch.
442
- return false , nil
443
- } else if err != nil {
444
- return false , err
444
+ operationSizeChange := 0
445
+
446
+ // Find if there is an old entry under that name that will be overwritten
447
+ // (AddEntry) or flat out removed (RemoveEntry).
448
+ entryToRemove , err := d .shard .Find (ctx , name )
449
+ if err != os .ErrNotExist {
450
+ if err != nil {
451
+ return false , err
452
+ }
453
+ operationSizeChange -= estimatedLinkSize (name , entryToRemove .Cid )
454
+ }
455
+
456
+ // For the AddEntry case compute the size addition of the new entry.
457
+ if nodeToAdd != nil {
458
+ operationSizeChange += estimatedLinkSize (name , nodeToAdd .Cid ())
445
459
}
446
- sizeToRemove := estimatedLinkSize (nameToRemove , entryToRemove .Cid )
447
460
448
- if d .sizeChange - sizeToRemove >= 0 {
461
+ if d .sizeChange + operationSizeChange >= 0 {
449
462
// We won't have reduced the HAMT net size.
450
463
return false , nil
451
464
}
452
465
453
466
// We have reduced the directory size, check if went below the
454
467
// HAMTShardingSize threshold to trigger a switch.
455
- belowThreshold , err := d .sizeBelowThreshold (ctx , - sizeToRemove )
468
+ belowThreshold , err := d .sizeBelowThreshold (ctx , operationSizeChange )
456
469
if err != nil {
457
470
return false , err
458
471
}
@@ -511,7 +524,29 @@ var _ Directory = (*UpgradeableDirectory)(nil)
511
524
// AddChild implements the `Directory` interface. We check when adding new entries
512
525
// if we should switch to HAMTDirectory according to global option(s).
513
526
func (d * UpgradeableDirectory ) AddChild (ctx context.Context , name string , nd ipld.Node ) error {
514
- err := d .Directory .AddChild (ctx , name , nd )
527
+ hamtDir , ok := d .Directory .(* HAMTDirectory )
528
+ if ok {
529
+ // We evaluate a switch in the HAMTDirectory case even for an AddChild
530
+ // as it may overwrite an existing entry and end up actually reducing
531
+ // the directory size.
532
+ switchToBasic , err := hamtDir .needsToSwitchToBasicDir (ctx , name , nd )
533
+ if err != nil {
534
+ return err
535
+ }
536
+
537
+ if switchToBasic {
538
+ basicDir , err := hamtDir .switchToBasic (ctx )
539
+ if err != nil {
540
+ return err
541
+ }
542
+ d .Directory = basicDir
543
+ }
544
+ return d .Directory .AddChild (ctx , name , nd )
545
+ }
546
+
547
+ // BasicDirectory
548
+ basicDir := d .Directory .(* BasicDirectory )
549
+ err := basicDir .AddChild (ctx , name , nd )
515
550
if err != nil {
516
551
return err
517
552
}
@@ -520,10 +555,6 @@ func (d *UpgradeableDirectory) AddChild(ctx context.Context, name string, nd ipl
520
555
if HAMTShardingSize == 0 {
521
556
return nil
522
557
}
523
- basicDir , ok := d .Directory .(* BasicDirectory )
524
- if ! ok {
525
- return nil
526
- }
527
558
if basicDir .estimatedSize >= HAMTShardingSize {
528
559
// Ideally to minimize performance we should check if this last
529
560
// `AddChild` call would bring the directory size over the threshold
@@ -562,7 +593,7 @@ func (d *UpgradeableDirectory) getDagService() ipld.DAGService {
562
593
func (d * UpgradeableDirectory ) RemoveChild (ctx context.Context , name string ) error {
563
594
hamtDir , ok := d .Directory .(* HAMTDirectory )
564
595
if ok {
565
- switchToBasic , err := hamtDir .needsToSwitchToBasicDir (ctx , name )
596
+ switchToBasic , err := hamtDir .needsToSwitchToBasicDir (ctx , name , nil )
566
597
if err != nil {
567
598
return err
568
599
}
0 commit comments