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

Commit 7a7204f

Browse files
committed
fix(directory): add unsharding logic for AddChild case
1 parent 56dc9ca commit 7a7204f

File tree

1 file changed

+87
-38
lines changed

1 file changed

+87
-38
lines changed

io/directory.go

Lines changed: 87 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -200,10 +200,32 @@ func (d *BasicDirectory) AddChild(ctx context.Context, name string, node ipld.No
200200
return d.addLinkChild(ctx, name, link)
201201
}
202202

203+
func (d *BasicDirectory) needsToSwitchToHAMTDir(name string, nodeToAdd ipld.Node) (bool, error) {
204+
if HAMTShardingSize == 0 { // Option disabled.
205+
return false, nil
206+
}
207+
208+
operationSizeChange := 0
209+
// Find if there is an old entry under that name that will be overwritten.
210+
entryToRemove, err := d.node.GetNodeLink(name)
211+
if err != mdag.ErrLinkNotFound {
212+
if err != nil {
213+
return false, err
214+
}
215+
operationSizeChange -= estimatedLinkSize(name, entryToRemove.Cid)
216+
}
217+
if nodeToAdd != nil {
218+
operationSizeChange += estimatedLinkSize(name, nodeToAdd.Cid())
219+
}
220+
221+
return d.estimatedSize+operationSizeChange >= HAMTShardingSize, nil
222+
}
223+
203224
// addLinkChild adds the link as an entry to this directory under the given
204225
// name. Plumbing function for the AddChild API.
205226
func (d *BasicDirectory) addLinkChild(ctx context.Context, name string, link *ipld.Link) error {
206-
// Remove old link (if it existed; ignore `ErrNotExist` otherwise).
227+
// Remove old link and account for size change (if it existed; ignore
228+
// `ErrNotExist` otherwise).
207229
err := d.RemoveChild(ctx, name)
208230
if err != nil && err != os.ErrNotExist {
209231
return err
@@ -298,7 +320,7 @@ func (d *BasicDirectory) GetCidBuilder() cid.Builder {
298320
}
299321

300322
// SwitchToSharding returns a HAMT implementation of this directory.
301-
func (d *BasicDirectory) SwitchToSharding(ctx context.Context) (Directory, error) {
323+
func (d *BasicDirectory) SwitchToSharding(ctx context.Context) (*HAMTDirectory, error) {
302324
hamtDir := new(HAMTDirectory)
303325
hamtDir.dserv = d.dserv
304326

@@ -430,33 +452,42 @@ func (d *HAMTDirectory) removeFromSizeChange(name string, linkCid cid.Cid) {
430452
d.sizeChange -= estimatedLinkSize(name, linkCid)
431453
}
432454

433-
// FIXME: Will be extended later to the `AddEntry` case.
434-
func (d *HAMTDirectory) needsToSwitchToBasicDir(ctx context.Context, nameToRemove string) (switchToBasic bool, err error) {
455+
// Evaluate a switch from HAMTDirectory to BasicDirectory in case the size will
456+
// go above the threshold when we are adding or removing an entry.
457+
// In both the add/remove operations any old name will be removed, and for the
458+
// add operation in particular a new entry will be added under that name (otherwise
459+
// nodeToAdd is nil). We compute both (potential) future subtraction and
460+
// addition to the size change.
461+
func (d *HAMTDirectory) needsToSwitchToBasicDir(ctx context.Context, name string, nodeToAdd ipld.Node) (switchToBasic bool, err error) {
435462
if HAMTShardingSize == 0 { // Option disabled.
436463
return false, nil
437464
}
438465

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
466+
operationSizeChange := 0
467+
468+
// Find if there is an old entry under that name that will be overwritten
469+
// (AddEntry) or flat out removed (RemoveEntry).
470+
entryToRemove, err := d.shard.Find(ctx, name)
471+
if err != os.ErrNotExist {
472+
if err != nil {
473+
return false, err
474+
}
475+
operationSizeChange -= estimatedLinkSize(name, entryToRemove.Cid)
476+
}
477+
478+
// For the AddEntry case compute the size addition of the new entry.
479+
if nodeToAdd != nil {
480+
operationSizeChange += estimatedLinkSize(name, nodeToAdd.Cid())
445481
}
446-
sizeToRemove := estimatedLinkSize(nameToRemove, entryToRemove.Cid)
447482

448-
if d.sizeChange-sizeToRemove >= 0 {
483+
if d.sizeChange+operationSizeChange >= 0 {
449484
// We won't have reduced the HAMT net size.
450485
return false, nil
451486
}
452487

453488
// We have reduced the directory size, check if went below the
454489
// HAMTShardingSize threshold to trigger a switch.
455-
belowThreshold, err := d.sizeBelowThreshold(ctx, -sizeToRemove)
456-
if err != nil {
457-
return false, err
458-
}
459-
return belowThreshold, nil
490+
return d.sizeBelowThreshold(ctx, operationSizeChange)
460491
}
461492

462493
// Evaluate directory size and a future sizeChange and check if it will be below
@@ -511,32 +542,50 @@ var _ Directory = (*UpgradeableDirectory)(nil)
511542
// AddChild implements the `Directory` interface. We check when adding new entries
512543
// if we should switch to HAMTDirectory according to global option(s).
513544
func (d *UpgradeableDirectory) AddChild(ctx context.Context, name string, nd ipld.Node) error {
514-
err := d.Directory.AddChild(ctx, name, nd)
545+
hamtDir, ok := d.Directory.(*HAMTDirectory)
546+
if ok {
547+
// We evaluate a switch in the HAMTDirectory case even for an AddChild
548+
// as it may overwrite an existing entry and end up actually reducing
549+
// the directory size.
550+
switchToBasic, err := hamtDir.needsToSwitchToBasicDir(ctx, name, nd)
551+
if err != nil {
552+
return err
553+
}
554+
555+
if switchToBasic {
556+
basicDir, err := hamtDir.switchToBasic(ctx)
557+
if err != nil {
558+
return err
559+
}
560+
err = basicDir.AddChild(ctx, name, nd)
561+
if err != nil {
562+
return err
563+
}
564+
d.Directory = basicDir
565+
return nil
566+
}
567+
568+
return d.Directory.AddChild(ctx, name, nd)
569+
}
570+
571+
// BasicDirectory
572+
basicDir := d.Directory.(*BasicDirectory)
573+
switchToHAMT, err := basicDir.needsToSwitchToHAMTDir(name, nd)
515574
if err != nil {
516575
return err
517576
}
518-
519-
// Evaluate possible HAMT upgrade.
520-
if HAMTShardingSize == 0 {
521-
return nil
577+
if !switchToHAMT {
578+
return basicDir.AddChild(ctx, name, nd)
522579
}
523-
basicDir, ok := d.Directory.(*BasicDirectory)
524-
if !ok {
525-
return nil
580+
hamtDir, err = basicDir.SwitchToSharding(ctx)
581+
if err != nil {
582+
return err
526583
}
527-
if basicDir.estimatedSize >= HAMTShardingSize {
528-
// Ideally to minimize performance we should check if this last
529-
// `AddChild` call would bring the directory size over the threshold
530-
// *before* executing it since we would end up switching anyway and
531-
// that call would be "wasted". This is a minimal performance impact
532-
// and we prioritize a simple code base.
533-
hamtDir, err := basicDir.SwitchToSharding(ctx)
534-
if err != nil {
535-
return err
536-
}
537-
d.Directory = hamtDir
584+
hamtDir.AddChild(ctx, name, nd)
585+
if err != nil {
586+
return err
538587
}
539-
588+
d.Directory = hamtDir
540589
return nil
541590
}
542591

@@ -565,7 +614,7 @@ func (d *UpgradeableDirectory) RemoveChild(ctx context.Context, name string) err
565614
return d.Directory.RemoveChild(ctx, name)
566615
}
567616

568-
switchToBasic, err := hamtDir.needsToSwitchToBasicDir(ctx, name)
617+
switchToBasic, err := hamtDir.needsToSwitchToBasicDir(ctx, name, nil)
569618
if err != nil {
570619
return err
571620
}

0 commit comments

Comments
 (0)