Fix empty data tree

This commit is contained in:
mcrakhman 2023-04-27 18:24:45 +02:00
parent f3c9c64bce
commit da0e4f148a
No known key found for this signature in database
GPG Key ID: DED12CFEF5B8396B
6 changed files with 86 additions and 35 deletions

View File

@ -52,18 +52,6 @@ func (c *nonVerifiableChangeBuilder) Marshall(ch *Change) (raw *treechangeproto.
return c.ChangeBuilder.Marshall(ch) return c.ChangeBuilder.Marshall(ch)
} }
type emptyDataChangeBuilder struct {
ChangeBuilder
}
func (c *emptyDataChangeBuilder) Build(payload BuilderContent) (ch *Change, raw *treechangeproto.RawTreeChangeWithId, err error) {
panic("should not be called")
}
func (c *emptyDataChangeBuilder) Marshall(ch *Change) (raw *treechangeproto.RawTreeChangeWithId, err error) {
panic("should not be called")
}
type ChangeBuilder interface { type ChangeBuilder interface {
Unmarshall(rawIdChange *treechangeproto.RawTreeChangeWithId, verify bool) (ch *Change, err error) Unmarshall(rawIdChange *treechangeproto.RawTreeChangeWithId, verify bool) (ch *Change, err error)
Build(payload BuilderContent) (ch *Change, raw *treechangeproto.RawTreeChangeWithId, err error) Build(payload BuilderContent) (ch *Change, raw *treechangeproto.RawTreeChangeWithId, err error)
@ -79,18 +67,6 @@ type changeBuilder struct {
newChange newChangeFunc newChange newChangeFunc
} }
func NewEmptyDataBuilder(keys crypto.KeyStorage, rootChange *treechangeproto.RawTreeChangeWithId) ChangeBuilder {
return &emptyDataChangeBuilder{&changeBuilder{
rootChange: rootChange,
keys: keys,
newChange: func(id string, identity crypto.PubKey, ch *treechangeproto.TreeChange, signature []byte) *Change {
c := NewChange(id, identity, ch, nil)
c.Data = nil
return c
},
}}
}
func NewChangeBuilder(keys crypto.KeyStorage, rootChange *treechangeproto.RawTreeChangeWithId) ChangeBuilder { func NewChangeBuilder(keys crypto.KeyStorage, rootChange *treechangeproto.RawTreeChangeWithId) ChangeBuilder {
return &changeBuilder{keys: keys, rootChange: rootChange, newChange: NewChange} return &changeBuilder{keys: keys, rootChange: rootChange, newChange: NewChange}
} }

View File

@ -96,10 +96,11 @@ type objectTree struct {
treeBuilder *treeBuilder treeBuilder *treeBuilder
aclList list.AclList aclList list.AclList
id string removeDataOnAdd bool
rawRoot *treechangeproto.RawTreeChangeWithId id string
root *Change rawRoot *treechangeproto.RawTreeChangeWithId
tree *Tree root *Change
tree *Tree
keys map[string]crypto.SymKey keys map[string]crypto.SymKey
currentReadKey crypto.SymKey currentReadKey crypto.SymKey
@ -473,6 +474,11 @@ func (ot *objectTree) createAddResult(oldHeads []string, mode Mode, treeChangesA
var added []*treechangeproto.RawTreeChangeWithId var added []*treechangeproto.RawTreeChangeWithId
added, err = getAddedChanges(treeChangesAdded) added, err = getAddedChanges(treeChangesAdded)
if ot.removeDataOnAdd {
for _, ch := range treeChangesAdded {
ch.Data = nil
}
}
if err != nil { if err != nil {
return return
} }

View File

@ -6,6 +6,7 @@ import (
"github.com/anytypeio/any-sync/commonspace/object/acl/list" "github.com/anytypeio/any-sync/commonspace/object/acl/list"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto" "github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage" "github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
"github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"testing" "testing"
@ -46,9 +47,17 @@ func prepareTreeDeps(aclList list.AclList) (*MockChangeCreator, objectTreeDeps)
} }
func prepareTreeContext(t *testing.T, aclList list.AclList) testTreeContext { func prepareTreeContext(t *testing.T, aclList list.AclList) testTreeContext {
return prepareContext(t, aclList, BuildTestableTree)
}
func prepareEmptyDataTreeContext(t *testing.T, aclList list.AclList) testTreeContext {
return prepareContext(t, aclList, BuildEmptyDataTestableTree)
}
func prepareContext(t *testing.T, aclList list.AclList, objTreeBuilder BuildObjectTreeFunc) testTreeContext {
changeCreator := NewMockChangeCreator() changeCreator := NewMockChangeCreator()
treeStorage := changeCreator.CreateNewTreeStorage("0", aclList.Head().Id) treeStorage := changeCreator.CreateNewTreeStorage("0", aclList.Head().Id)
objTree, err := BuildTestableTree(aclList, treeStorage) objTree, err := objTreeBuilder(treeStorage, aclList)
require.NoError(t, err, "building tree should be without error") require.NoError(t, err, "building tree should be without error")
// check tree iterate // check tree iterate
@ -266,6 +275,58 @@ func TestObjectTree(t *testing.T) {
assert.Equal(t, true, objTree.(*objectTree).snapshotPathIsActual()) assert.Equal(t, true, objTree.(*objectTree).snapshotPathIsActual())
}) })
t.Run("test empty data tree", func(t *testing.T) {
ctx := prepareEmptyDataTreeContext(t, aclList)
changeCreator := ctx.changeCreator
objTree := ctx.objTree
rawChangesFirst := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRawWithData("1", aclList.Head().Id, "0", false, []byte("1"), "0"),
changeCreator.CreateRawWithData("2", aclList.Head().Id, "0", false, []byte("2"), "1"),
changeCreator.CreateRawWithData("3", aclList.Head().Id, "0", false, []byte("3"), "2"),
}
rawChangesSecond := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRawWithData("4", aclList.Head().Id, "0", false, []byte("4"), "2"),
changeCreator.CreateRawWithData("5", aclList.Head().Id, "0", false, []byte("5"), "1"),
changeCreator.CreateRawWithData("6", aclList.Head().Id, "0", false, []byte("6"), "3", "4", "5"),
}
// making them to be saved in unattached
_, err := objTree.AddRawChanges(context.Background(), RawChangesPayload{
NewHeads: []string{"6"},
RawChanges: rawChangesSecond,
})
require.NoError(t, err, "adding changes should be without error")
// attaching them
res, err := objTree.AddRawChanges(context.Background(), RawChangesPayload{
NewHeads: []string{"3"},
RawChanges: rawChangesFirst,
})
require.NoError(t, err, "adding changes should be without error")
require.Equal(t, "0", objTree.Root().Id)
require.Equal(t, []string{"6"}, objTree.Heads())
require.Equal(t, 6, len(res.Added))
// checking that added changes still have data
for _, ch := range res.Added {
unmarshallRaw := &treechangeproto.RawTreeChange{}
proto.Unmarshal(ch.RawChange, unmarshallRaw)
treeCh := &treechangeproto.TreeChange{}
proto.Unmarshal(unmarshallRaw.Payload, treeCh)
require.Equal(t, ch.Id, string(treeCh.ChangesData))
}
// checking that the tree doesn't have data in memory
err = objTree.IterateRoot(nil, func(change *Change) bool {
if change.Id == "0" {
return true
}
require.Nil(t, change.Data)
return true
})
})
t.Run("changes from tree after common snapshot complex", func(t *testing.T) { t.Run("changes from tree after common snapshot complex", func(t *testing.T) {
ctx := prepareTreeContext(t, aclList) ctx := prepareTreeContext(t, aclList)
changeCreator := ctx.changeCreator changeCreator := ctx.changeCreator

View File

@ -31,6 +31,7 @@ type objectTreeDeps struct {
validator ObjectTreeValidator validator ObjectTreeValidator
rawChangeLoader *rawChangeLoader rawChangeLoader *rawChangeLoader
aclList list.AclList aclList list.AclList
removeDataOnAdd bool
} }
type BuildObjectTreeFunc = func(treeStorage treestorage.TreeStorage, aclList list.AclList) (ObjectTree, error) type BuildObjectTreeFunc = func(treeStorage treestorage.TreeStorage, aclList list.AclList) (ObjectTree, error)
@ -57,7 +58,7 @@ func emptyDataTreeDeps(
rootChange *treechangeproto.RawTreeChangeWithId, rootChange *treechangeproto.RawTreeChangeWithId,
treeStorage treestorage.TreeStorage, treeStorage treestorage.TreeStorage,
aclList list.AclList) objectTreeDeps { aclList list.AclList) objectTreeDeps {
changeBuilder := NewEmptyDataBuilder(crypto.NewKeyStorage(), rootChange) changeBuilder := NewChangeBuilder(crypto.NewKeyStorage(), rootChange)
treeBuilder := newTreeBuilder(treeStorage, changeBuilder) treeBuilder := newTreeBuilder(treeStorage, changeBuilder)
return objectTreeDeps{ return objectTreeDeps{
changeBuilder: changeBuilder, changeBuilder: changeBuilder,
@ -66,6 +67,7 @@ func emptyDataTreeDeps(
validator: newTreeValidator(), validator: newTreeValidator(),
rawChangeLoader: newStorageLoader(treeStorage, changeBuilder), rawChangeLoader: newStorageLoader(treeStorage, changeBuilder),
aclList: aclList, aclList: aclList,
removeDataOnAdd: true,
} }
} }
@ -107,7 +109,7 @@ func BuildEmptyDataObjectTree(treeStorage treestorage.TreeStorage, aclList list.
return buildObjectTree(deps) return buildObjectTree(deps)
} }
func BuildTestableTree(aclList list.AclList, treeStorage treestorage.TreeStorage) (ObjectTree, error) { func BuildTestableTree(treeStorage treestorage.TreeStorage, aclList list.AclList) (ObjectTree, error) {
root, _ := treeStorage.Root() root, _ := treeStorage.Root()
changeBuilder := &nonVerifiableChangeBuilder{ changeBuilder := &nonVerifiableChangeBuilder{
ChangeBuilder: NewChangeBuilder(newMockKeyStorage(), root), ChangeBuilder: NewChangeBuilder(newMockKeyStorage(), root),
@ -124,10 +126,10 @@ func BuildTestableTree(aclList list.AclList, treeStorage treestorage.TreeStorage
return buildObjectTree(deps) return buildObjectTree(deps)
} }
func BuildEmptyDataTestableTree(aclList list.AclList, treeStorage treestorage.TreeStorage) (ObjectTree, error) { func BuildEmptyDataTestableTree(treeStorage treestorage.TreeStorage, aclList list.AclList) (ObjectTree, error) {
root, _ := treeStorage.Root() root, _ := treeStorage.Root()
changeBuilder := &nonVerifiableChangeBuilder{ changeBuilder := &nonVerifiableChangeBuilder{
ChangeBuilder: NewEmptyDataBuilder(newMockKeyStorage(), root), ChangeBuilder: NewChangeBuilder(newMockKeyStorage(), root),
} }
deps := objectTreeDeps{ deps := objectTreeDeps{
changeBuilder: changeBuilder, changeBuilder: changeBuilder,
@ -136,6 +138,7 @@ func BuildEmptyDataTestableTree(aclList list.AclList, treeStorage treestorage.Tr
rawChangeLoader: newStorageLoader(treeStorage, changeBuilder), rawChangeLoader: newStorageLoader(treeStorage, changeBuilder),
validator: &noOpTreeValidator{}, validator: &noOpTreeValidator{},
aclList: aclList, aclList: aclList,
removeDataOnAdd: true,
} }
return buildObjectTree(deps) return buildObjectTree(deps)
@ -251,6 +254,7 @@ func buildObjectTree(deps objectTreeDeps) (ObjectTree, error) {
difSnapshotBuf: make([]*treechangeproto.RawTreeChangeWithId, 0, 10), difSnapshotBuf: make([]*treechangeproto.RawTreeChangeWithId, 0, 10),
notSeenIdxBuf: make([]int, 0, 10), notSeenIdxBuf: make([]int, 0, 10),
newSnapshotsBuf: make([]*Change, 0, 10), newSnapshotsBuf: make([]*Change, 0, 10),
removeDataOnAdd: deps.removeDataOnAdd,
} }
err := objTree.rebuildFromStorage(nil, nil) err := objTree.rebuildFromStorage(nil, nil)

View File

@ -89,11 +89,15 @@ func (c *MockChangeCreator) CreateRoot(id, aclId string) *treechangeproto.RawTre
} }
func (c *MockChangeCreator) CreateRaw(id, aclId, snapshotId string, isSnapshot bool, prevIds ...string) *treechangeproto.RawTreeChangeWithId { func (c *MockChangeCreator) CreateRaw(id, aclId, snapshotId string, isSnapshot bool, prevIds ...string) *treechangeproto.RawTreeChangeWithId {
return c.CreateRawWithData(id, aclId, snapshotId, isSnapshot, nil, prevIds...)
}
func (c *MockChangeCreator) CreateRawWithData(id, aclId, snapshotId string, isSnapshot bool, data []byte, prevIds ...string) *treechangeproto.RawTreeChangeWithId {
aclChange := &treechangeproto.TreeChange{ aclChange := &treechangeproto.TreeChange{
TreeHeadIds: prevIds, TreeHeadIds: prevIds,
AclHeadId: aclId, AclHeadId: aclId,
SnapshotBaseId: snapshotId, SnapshotBaseId: snapshotId,
ChangesData: nil, ChangesData: data,
IsSnapshot: isSnapshot, IsSnapshot: isSnapshot,
} }
res, _ := aclChange.Marshal() res, _ := aclChange.Marshal()

View File

@ -296,7 +296,7 @@ func createStorage(treeId string, aclList list.AclList) treestorage.TreeStorage
} }
func createTestTree(aclList list.AclList, storage treestorage.TreeStorage) (objecttree.ObjectTree, error) { func createTestTree(aclList list.AclList, storage treestorage.TreeStorage) (objecttree.ObjectTree, error) {
return objecttree.BuildEmptyDataTestableTree(aclList, storage) return objecttree.BuildEmptyDataTestableTree(storage, aclList)
} }
type fixtureDeps struct { type fixtureDeps struct {