diff --git a/common/pkg/acl/tree/treebuilder.go b/common/pkg/acl/tree/treebuilder.go index e7997e86..f359d00e 100644 --- a/common/pkg/acl/tree/treebuilder.go +++ b/common/pkg/acl/tree/treebuilder.go @@ -42,29 +42,44 @@ func (tb *treeBuilder) Reset() { func (tb *treeBuilder) Build(theirHeads []string, newChanges []*Change) (*Tree, error) { var proposedHeads []string + tb.cache = make(map[string]*Change) heads, err := tb.treeStorage.Heads() if err != nil { return nil, err } - tb.cache = make(map[string]*Change) - proposedHeads = append(proposedHeads, heads...) + + // TODO: we can actually get this from database + // getting old common snapshot + oldBreakpoint, err := tb.findBreakpoint(heads, true) + if err != nil { + // this should never error out, because otherwise we have broken data + return nil, fmt.Errorf("findBreakpoint error: %v", err) + } + if len(theirHeads) > 0 { proposedHeads = append(proposedHeads, theirHeads...) } for _, ch := range newChanges { - // we don't know what new heads are, so every change can be head if len(theirHeads) == 0 { + // in this case we don't know what new heads are, so every change can be head proposedHeads = append(proposedHeads, ch.Id) } tb.cache[ch.Id] = ch } - log.With(zap.Strings("heads", proposedHeads)).Debug("building tree") - breakpoint, err := tb.findBreakpoint(proposedHeads) + // getting common snapshot for new heads + breakpoint, err := tb.findBreakpoint(proposedHeads, false) if err != nil { - return nil, fmt.Errorf("findBreakpoint error: %v", err) + breakpoint = oldBreakpoint + } else { + breakpoint, err = tb.findCommonForTwoSnapshots(oldBreakpoint, breakpoint) + if err != nil { + breakpoint = oldBreakpoint + } } + proposedHeads = append(proposedHeads, heads...) + log.With(zap.Strings("heads", proposedHeads)).Debug("building tree") if err = tb.buildTree(proposedHeads, breakpoint); err != nil { return nil, fmt.Errorf("buildTree error: %v", err) } @@ -78,13 +93,12 @@ func (tb *treeBuilder) buildTree(heads []string, breakpoint string) (err error) return } tb.tree.AddFast(ch) - changes, err := tb.dfs(heads, breakpoint) - + changes := tb.dfs(heads, breakpoint) tb.tree.AddFast(changes...) return } -func (tb *treeBuilder) dfs(heads []string, breakpoint string) (buf []*Change, err error) { +func (tb *treeBuilder) dfs(heads []string, breakpoint string) []*Change { // initializing buffers tb.idStack = tb.idStack[:0] tb.loadBuffer = tb.loadBuffer[:0] @@ -118,7 +132,7 @@ func (tb *treeBuilder) dfs(heads []string, breakpoint string) (buf []*Change, er tb.idStack = append(tb.idStack, prev) } } - return tb.loadBuffer, nil + return tb.loadBuffer } func (tb *treeBuilder) loadChange(id string) (ch *Change, err error) { @@ -143,18 +157,34 @@ func (tb *treeBuilder) loadChange(id string) (ch *Change, err error) { return ch, nil } -func (tb *treeBuilder) findBreakpoint(heads []string) (breakpoint string, err error) { +func (tb *treeBuilder) findBreakpoint(heads []string, noError bool) (breakpoint string, err error) { var ( ch *Change snapshotIds []string ) for _, head := range heads { if ch, err = tb.loadChange(head); err != nil { - return + if noError { + return + } + + log.With(zap.String("head", head), zap.Error(err)).Debug("couldn't find head") + continue } + shId := ch.SnapshotId if ch.IsSnapshot { shId = ch.Id + } else { + _, err = tb.loadChange(shId) + if err != nil { + if noError { + return + } + + log.With(zap.String("snapshot id", shId), zap.Error(err)).Debug("couldn't find head's snapshot") + continue + } } if slice.FindPos(snapshotIds, shId) == -1 { snapshotIds = append(snapshotIds, shId) @@ -170,6 +200,7 @@ func (tb *treeBuilder) findCommonSnapshot(snapshotIds []string) (snapshotId stri return "", fmt.Errorf("snapshots not found") } + // TODO: use divide and conquer to find the snapshot, then we will have only logN findCommonForTwoSnapshots calls for len(snapshotIds) > 1 { l := len(snapshotIds) shId, e := tb.findCommonForTwoSnapshots(snapshotIds[l-2], snapshotIds[l-1])