@@ -430,29 +430,42 @@ func (d *HAMTDirectory) removeFromSizeChange(name string, linkCid cid.Cid) {
430430 d .sizeChange -= estimatedLinkSize (name , linkCid )
431431}
432432
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 ) {
435440 if HAMTShardingSize == 0 { // Option disabled.
436441 return false , nil
437442 }
438443
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 ())
445459 }
446- sizeToRemove := estimatedLinkSize (nameToRemove , entryToRemove .Cid )
447460
448- if d .sizeChange - sizeToRemove >= 0 {
461+ if d .sizeChange + operationSizeChange >= 0 {
449462 // We won't have reduced the HAMT net size.
450463 return false , nil
451464 }
452465
453466 // We have reduced the directory size, check if went below the
454467 // HAMTShardingSize threshold to trigger a switch.
455- belowThreshold , err := d .sizeBelowThreshold (ctx , - sizeToRemove )
468+ belowThreshold , err := d .sizeBelowThreshold (ctx , operationSizeChange )
456469 if err != nil {
457470 return false , err
458471 }
@@ -511,7 +524,29 @@ var _ Directory = (*UpgradeableDirectory)(nil)
511524// AddChild implements the `Directory` interface. We check when adding new entries
512525// if we should switch to HAMTDirectory according to global option(s).
513526func (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 )
515550 if err != nil {
516551 return err
517552 }
@@ -520,10 +555,6 @@ func (d *UpgradeableDirectory) AddChild(ctx context.Context, name string, nd ipl
520555 if HAMTShardingSize == 0 {
521556 return nil
522557 }
523- basicDir , ok := d .Directory .(* BasicDirectory )
524- if ! ok {
525- return nil
526- }
527558 if basicDir .estimatedSize >= HAMTShardingSize {
528559 // Ideally to minimize performance we should check if this last
529560 // `AddChild` call would bring the directory size over the threshold
@@ -562,7 +593,7 @@ func (d *UpgradeableDirectory) getDagService() ipld.DAGService {
562593func (d * UpgradeableDirectory ) RemoveChild (ctx context.Context , name string ) error {
563594 hamtDir , ok := d .Directory .(* HAMTDirectory )
564595 if ok {
565- switchToBasic , err := hamtDir .needsToSwitchToBasicDir (ctx , name )
596+ switchToBasic , err := hamtDir .needsToSwitchToBasicDir (ctx , name , nil )
566597 if err != nil {
567598 return err
568599 }
0 commit comments