Skip to content
This repository was archived by the owner on Jun 27, 2023. It is now read-only.

Commit efd7822

Browse files
authored
fix(directory): add unsharding logic for AddChild case (#103)
1 parent 6d48cca commit efd7822

File tree

1 file changed

+87
-38
lines changed

1 file changed

+87
-38
lines changed

io/directory.go

+87-38
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,32 @@ func (d *BasicDirectory) AddChild(ctx context.Context, name string, node ipld.No
196196
return d.addLinkChild(ctx, name, link)
197197
}
198198

199+
func (d *BasicDirectory) needsToSwitchToHAMTDir(name string, nodeToAdd ipld.Node) (bool, error) {
200+
if HAMTShardingSize == 0 { // Option disabled.
201+
return false, nil
202+
}
203+
204+
operationSizeChange := 0
205+
// Find if there is an old entry under that name that will be overwritten.
206+
entryToRemove, err := d.node.GetNodeLink(name)
207+
if err != mdag.ErrLinkNotFound {
208+
if err != nil {
209+
return false, err
210+
}
211+
operationSizeChange -= estimatedLinkSize(name, entryToRemove.Cid)
212+
}
213+
if nodeToAdd != nil {
214+
operationSizeChange += estimatedLinkSize(name, nodeToAdd.Cid())
215+
}
216+
217+
return d.estimatedSize+operationSizeChange >= HAMTShardingSize, nil
218+
}
219+
199220
// addLinkChild adds the link as an entry to this directory under the given
200221
// name. Plumbing function for the AddChild API.
201222
func (d *BasicDirectory) addLinkChild(ctx context.Context, name string, link *ipld.Link) error {
202-
// Remove old link (if it existed; ignore `ErrNotExist` otherwise).
223+
// Remove old link and account for size change (if it existed; ignore
224+
// `ErrNotExist` otherwise).
203225
err := d.RemoveChild(ctx, name)
204226
if err != nil && err != os.ErrNotExist {
205227
return err
@@ -294,7 +316,7 @@ func (d *BasicDirectory) GetCidBuilder() cid.Builder {
294316
}
295317

296318
// SwitchToSharding returns a HAMT implementation of this directory.
297-
func (d *BasicDirectory) SwitchToSharding(ctx context.Context) (Directory, error) {
319+
func (d *BasicDirectory) SwitchToSharding(ctx context.Context) (*HAMTDirectory, error) {
298320
hamtDir := new(HAMTDirectory)
299321
hamtDir.dserv = d.dserv
300322

@@ -422,33 +444,42 @@ func (d *HAMTDirectory) removeFromSizeChange(name string, linkCid cid.Cid) {
422444
d.sizeChange -= estimatedLinkSize(name, linkCid)
423445
}
424446

425-
// FIXME: Will be extended later to the `AddEntry` case.
426-
func (d *HAMTDirectory) needsToSwitchToBasicDir(ctx context.Context, nameToRemove string) (switchToBasic bool, err error) {
447+
// Evaluate a switch from HAMTDirectory to BasicDirectory in case the size will
448+
// go above the threshold when we are adding or removing an entry.
449+
// In both the add/remove operations any old name will be removed, and for the
450+
// add operation in particular a new entry will be added under that name (otherwise
451+
// nodeToAdd is nil). We compute both (potential) future subtraction and
452+
// addition to the size change.
453+
func (d *HAMTDirectory) needsToSwitchToBasicDir(ctx context.Context, name string, nodeToAdd ipld.Node) (switchToBasic bool, err error) {
427454
if HAMTShardingSize == 0 { // Option disabled.
428455
return false, nil
429456
}
430457

431-
entryToRemove, err := d.shard.Find(ctx, nameToRemove)
432-
if err == os.ErrNotExist {
433-
// Nothing to remove, no point in evaluating a switch.
434-
return false, nil
435-
} else if err != nil {
436-
return false, err
458+
operationSizeChange := 0
459+
460+
// Find if there is an old entry under that name that will be overwritten
461+
// (AddEntry) or flat out removed (RemoveEntry).
462+
entryToRemove, err := d.shard.Find(ctx, name)
463+
if err != os.ErrNotExist {
464+
if err != nil {
465+
return false, err
466+
}
467+
operationSizeChange -= estimatedLinkSize(name, entryToRemove.Cid)
468+
}
469+
470+
// For the AddEntry case compute the size addition of the new entry.
471+
if nodeToAdd != nil {
472+
operationSizeChange += estimatedLinkSize(name, nodeToAdd.Cid())
437473
}
438-
sizeToRemove := estimatedLinkSize(nameToRemove, entryToRemove.Cid)
439474

440-
if d.sizeChange-sizeToRemove >= 0 {
475+
if d.sizeChange+operationSizeChange >= 0 {
441476
// We won't have reduced the HAMT net size.
442477
return false, nil
443478
}
444479

445480
// We have reduced the directory size, check if went below the
446481
// HAMTShardingSize threshold to trigger a switch.
447-
belowThreshold, err := d.sizeBelowThreshold(ctx, -sizeToRemove)
448-
if err != nil {
449-
return false, err
450-
}
451-
return belowThreshold, nil
482+
return d.sizeBelowThreshold(ctx, operationSizeChange)
452483
}
453484

454485
// Evaluate directory size and a future sizeChange and check if it will be below
@@ -503,32 +534,50 @@ var _ Directory = (*UpgradeableDirectory)(nil)
503534
// AddChild implements the `Directory` interface. We check when adding new entries
504535
// if we should switch to HAMTDirectory according to global option(s).
505536
func (d *UpgradeableDirectory) AddChild(ctx context.Context, name string, nd ipld.Node) error {
506-
err := d.Directory.AddChild(ctx, name, nd)
537+
hamtDir, ok := d.Directory.(*HAMTDirectory)
538+
if ok {
539+
// We evaluate a switch in the HAMTDirectory case even for an AddChild
540+
// as it may overwrite an existing entry and end up actually reducing
541+
// the directory size.
542+
switchToBasic, err := hamtDir.needsToSwitchToBasicDir(ctx, name, nd)
543+
if err != nil {
544+
return err
545+
}
546+
547+
if switchToBasic {
548+
basicDir, err := hamtDir.switchToBasic(ctx)
549+
if err != nil {
550+
return err
551+
}
552+
err = basicDir.AddChild(ctx, name, nd)
553+
if err != nil {
554+
return err
555+
}
556+
d.Directory = basicDir
557+
return nil
558+
}
559+
560+
return d.Directory.AddChild(ctx, name, nd)
561+
}
562+
563+
// BasicDirectory
564+
basicDir := d.Directory.(*BasicDirectory)
565+
switchToHAMT, err := basicDir.needsToSwitchToHAMTDir(name, nd)
507566
if err != nil {
508567
return err
509568
}
510-
511-
// Evaluate possible HAMT upgrade.
512-
if HAMTShardingSize == 0 {
513-
return nil
569+
if !switchToHAMT {
570+
return basicDir.AddChild(ctx, name, nd)
514571
}
515-
basicDir, ok := d.Directory.(*BasicDirectory)
516-
if !ok {
517-
return nil
572+
hamtDir, err = basicDir.SwitchToSharding(ctx)
573+
if err != nil {
574+
return err
518575
}
519-
if basicDir.estimatedSize >= HAMTShardingSize {
520-
// Ideally to minimize performance we should check if this last
521-
// `AddChild` call would bring the directory size over the threshold
522-
// *before* executing it since we would end up switching anyway and
523-
// that call would be "wasted". This is a minimal performance impact
524-
// and we prioritize a simple code base.
525-
hamtDir, err := basicDir.SwitchToSharding(ctx)
526-
if err != nil {
527-
return err
528-
}
529-
d.Directory = hamtDir
576+
hamtDir.AddChild(ctx, name, nd)
577+
if err != nil {
578+
return err
530579
}
531-
580+
d.Directory = hamtDir
532581
return nil
533582
}
534583

@@ -557,7 +606,7 @@ func (d *UpgradeableDirectory) RemoveChild(ctx context.Context, name string) err
557606
return d.Directory.RemoveChild(ctx, name)
558607
}
559608

560-
switchToBasic, err := hamtDir.needsToSwitchToBasicDir(ctx, name)
609+
switchToBasic, err := hamtDir.needsToSwitchToBasicDir(ctx, name, nil)
561610
if err != nil {
562611
return err
563612
}

0 commit comments

Comments
 (0)