From a6d66c15a0f976f8d55d6c81d7b2305d9d8dafa9 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Sun, 7 May 2023 20:27:46 +0200 Subject: [PATCH] Add build full tree for history tree --- .../object/tree/objecttree/historytree.go | 43 ++++++++++++------- .../object/tree/objecttree/objecttree_test.go | 31 +++++++++++++ .../tree/objecttree/objecttreefactory.go | 3 +- .../object/tree/objecttree/treebuilder.go | 16 +++++++ commonspace/space.go | 6 ++- 5 files changed, 81 insertions(+), 18 deletions(-) diff --git a/commonspace/object/tree/objecttree/historytree.go b/commonspace/object/tree/objecttree/historytree.go index c5b78a95..fe3c381f 100644 --- a/commonspace/object/tree/objecttree/historytree.go +++ b/commonspace/object/tree/objecttree/historytree.go @@ -14,34 +14,47 @@ type historyTree struct { *objectTree } -func (h *historyTree) rebuildFromStorage(beforeId string, include bool) (err error) { - ot := h.objectTree - ot.treeBuilder.Reset() - if beforeId == ot.Id() && !include { +func (h *historyTree) rebuildFromStorage(params HistoryTreeParams) (err error) { + err = h.rebuild(params) + if err != nil { + return + } + h.aclList.RLock() + defer h.aclList.RUnlock() + state := h.aclList.AclState() + + return h.readKeysFromAclState(state) +} + +func (h *historyTree) rebuild(params HistoryTreeParams) (err error) { + var ( + beforeId = params.BeforeId + include = params.IncludeBeforeId + full = params.BuildFullTree + ) + h.treeBuilder.Reset() + if full { + h.tree, err = h.treeBuilder.BuildFull() + return + } + if beforeId == h.Id() && !include { return ErrLoadBeforeRoot } heads := []string{beforeId} if beforeId == "" { - heads, err = ot.treeStorage.Heads() + heads, err = h.treeStorage.Heads() if err != nil { return } } else if !include { - beforeChange, err := ot.treeBuilder.loadChange(beforeId) + beforeChange, err := h.treeBuilder.loadChange(beforeId) if err != nil { return err } heads = beforeChange.PreviousIds } - ot.tree, err = ot.treeBuilder.build(heads, nil, nil) - if err != nil { - return - } - ot.aclList.RLock() - defer ot.aclList.RUnlock() - state := ot.aclList.AclState() - - return ot.readKeysFromAclState(state) + h.tree, err = h.treeBuilder.build(heads, nil, nil) + return } diff --git a/commonspace/object/tree/objecttree/objecttree_test.go b/commonspace/object/tree/objecttree/objecttree_test.go index a60cfde6..9dc5e7be 100644 --- a/commonspace/object/tree/objecttree/objecttree_test.go +++ b/commonspace/object/tree/objecttree/objecttree_test.go @@ -674,6 +674,37 @@ func TestObjectTree(t *testing.T) { assert.Equal(t, "0", hTree.Root().Id) }) + t.Run("test history tree build full", func(t *testing.T) { + changeCreator, deps := prepareHistoryTreeDeps(aclList) + + // sequence of snapshots: 5->1->0 + rawChanges := []*treechangeproto.RawTreeChangeWithId{ + changeCreator.CreateRaw("1", aclList.Head().Id, "0", true, "0"), + changeCreator.CreateRaw("2", aclList.Head().Id, "1", false, "1"), + changeCreator.CreateRaw("3", aclList.Head().Id, "1", true, "2"), + changeCreator.CreateRaw("4", aclList.Head().Id, "1", false, "2"), + changeCreator.CreateRaw("5", aclList.Head().Id, "1", true, "3", "4"), + changeCreator.CreateRaw("6", aclList.Head().Id, "5", false, "5"), + } + deps.treeStorage.TransactionAdd(rawChanges, []string{"6"}) + hTree, err := buildHistoryTree(deps, HistoryTreeParams{ + BuildFullTree: true, + }) + require.NoError(t, err) + // check tree heads + assert.Equal(t, []string{"6"}, hTree.Heads()) + + // check tree iterate + var iterChangesId []string + err = hTree.IterateFrom(hTree.Root().Id, nil, func(change *Change) bool { + iterChangesId = append(iterChangesId, change.Id) + return true + }) + require.NoError(t, err, "iterate should be without error") + assert.Equal(t, []string{"0", "1", "2", "3", "4", "5", "6"}, iterChangesId) + assert.Equal(t, "0", hTree.Root().Id) + }) + t.Run("test history tree include", func(t *testing.T) { changeCreator, deps := prepareHistoryTreeDeps(aclList) diff --git a/commonspace/object/tree/objecttree/objecttreefactory.go b/commonspace/object/tree/objecttree/objecttreefactory.go index dc650bbb..0bc3f53c 100644 --- a/commonspace/object/tree/objecttree/objecttreefactory.go +++ b/commonspace/object/tree/objecttree/objecttreefactory.go @@ -22,6 +22,7 @@ type HistoryTreeParams struct { AclList list.AclList BeforeId string IncludeBeforeId bool + BuildFullTree bool } type objectTreeDeps struct { @@ -230,7 +231,7 @@ func buildHistoryTree(deps objectTreeDeps, params HistoryTreeParams) (ht History } hTree := &historyTree{objectTree: objTree} - err = hTree.rebuildFromStorage(params.BeforeId, params.IncludeBeforeId) + err = hTree.rebuildFromStorage(params) if err != nil { return nil, err } diff --git a/commonspace/object/tree/objecttree/treebuilder.go b/commonspace/object/tree/objecttree/treebuilder.go index 0b7ffe62..43e23042 100644 --- a/commonspace/object/tree/objecttree/treebuilder.go +++ b/commonspace/object/tree/objecttree/treebuilder.go @@ -51,6 +51,22 @@ func (tb *treeBuilder) Build(theirHeads []string, newChanges []*Change) (*Tree, return tb.build(heads, theirHeads, newChanges) } +func (tb *treeBuilder) BuildFull() (*Tree, error) { + defer func() { + tb.cache = make(map[string]*Change) + }() + tb.cache = make(map[string]*Change) + heads, err := tb.treeStorage.Heads() + if err != nil { + return nil, err + } + err = tb.buildTree(heads, tb.treeStorage.Id()) + if err != nil { + return nil, err + } + return tb.tree, nil +} + func (tb *treeBuilder) build(heads []string, theirHeads []string, newChanges []*Change) (*Tree, error) { defer func() { tb.cache = make(map[string]*Change) diff --git a/commonspace/space.go b/commonspace/space.go index 4997cc56..4ab103ff 100644 --- a/commonspace/space.go +++ b/commonspace/space.go @@ -323,8 +323,9 @@ type BuildTreeOpts struct { } type HistoryTreeOpts struct { - BeforeId string - Include bool + BeforeId string + Include bool + BuildFullTree bool } func (s *space) BuildTree(ctx context.Context, id string, opts BuildTreeOpts) (t objecttree.ObjectTree, err error) { @@ -364,6 +365,7 @@ func (s *space) BuildHistoryTree(ctx context.Context, id string, opts HistoryTre AclList: s.aclList, BeforeId: opts.BeforeId, IncludeBeforeId: opts.Include, + BuildFullTree: opts.BuildFullTree, } params.TreeStorage, err = s.storage.TreeStorage(id) if err != nil {