diff --git a/client/api/controller.go b/client/api/controller.go index 9e897dac..0e6b886c 100644 --- a/client/api/controller.go +++ b/client/api/controller.go @@ -23,6 +23,8 @@ type Controller interface { // CreateDocument creates new document in space CreateDocument(spaceId string) (id string, err error) + // DeleteDocument deletes a document from space + DeleteDocument(spaceId, documentId string) (err error) // AllDocumentIds gets all ids of documents in space AllDocumentIds(spaceId string) (ids []string, err error) // AddText adds text to space document @@ -97,6 +99,10 @@ func (c *controller) CreateDocument(spaceId string) (id string, err error) { return c.docService.CreateDocument(spaceId) } +func (c *controller) DeleteDocument(spaceId, documentId string) (err error) { + return c.docService.DeleteDocument(spaceId, documentId) +} + func (c *controller) AllDocumentIds(spaceId string) (ids []string, err error) { return c.docService.AllDocumentIds(spaceId) } diff --git a/client/document/service.go b/client/document/service.go index 399363b8..57e1cd98 100644 --- a/client/document/service.go +++ b/client/document/service.go @@ -53,10 +53,18 @@ func (s *service) CreateDocument(spaceId string) (id string, err error) { if err != nil { return } - id = doc.Tree().ID() + id = doc.ID() return } +func (s *service) DeleteDocument(spaceId, documentId string) (err error) { + space, err := s.spaceService.GetSpace(context.Background(), spaceId) + if err != nil { + return + } + return space.DeleteTree(context.Background(), documentId) +} + func (s *service) AllDocumentIds(spaceId string) (ids []string, err error) { space, err := s.spaceService.GetSpace(context.Background(), spaceId) if err != nil { @@ -79,5 +87,5 @@ func (s *service) DumpDocumentTree(spaceId, documentId string) (dump string, err if err != nil { return } - return doc.Tree().DebugDump() + return doc.DebugDump() } diff --git a/common/commonspace/diffservice/diffservice.go b/common/commonspace/diffservice/diffservice.go index b4d05667..a0369ece 100644 --- a/common/commonspace/diffservice/diffservice.go +++ b/common/commonspace/diffservice/diffservice.go @@ -69,10 +69,7 @@ func (d *diffService) HandleRangeRequest(ctx context.Context, req *spacesyncprot } func (d *diffService) UpdateHeads(id string, heads []string) { - d.diff.Set(ldiff.Element{ - Id: id, - Head: concatStrings(heads), - }) + d.syncer.UpdateHeads(id, heads) } func (d *diffService) AllIds() []string { @@ -80,9 +77,6 @@ func (d *diffService) AllIds() []string { } func (d *diffService) RemoveObjects(ids []string) { - for _, id := range ids { - d.diff.RemoveId(id) - } d.syncer.RemoveObjects(ids) } diff --git a/common/commonspace/diffservice/diffsyncer.go b/common/commonspace/diffservice/diffsyncer.go index abe360f5..31c5071a 100644 --- a/common/commonspace/diffservice/diffsyncer.go +++ b/common/commonspace/diffservice/diffsyncer.go @@ -18,6 +18,7 @@ import ( type DiffSyncer interface { Sync(ctx context.Context) error RemoveObjects(ids []string) + UpdateHeads(id string, heads []string) } func newDiffSyncer( @@ -56,10 +57,23 @@ func (d *diffSyncer) RemoveObjects(ids []string) { d.Lock() defer d.Unlock() for _, id := range ids { + d.diff.RemoveId(id) d.removedIds[id] = struct{}{} } } +func (d *diffSyncer) UpdateHeads(id string, heads []string) { + d.Lock() + defer d.Unlock() + if _, exists := d.removedIds[id]; exists { + return + } + d.diff.Set(ldiff.Element{ + Id: id, + Head: concatStrings(heads), + }) +} + func (d *diffSyncer) Sync(ctx context.Context) error { st := time.Now() // diffing with responsible peers according to configuration diff --git a/common/commonspace/settingsdocument/settingsdocument.go b/common/commonspace/settingsdocument/settingsdocument.go index b4a4c8b5..a93a4fae 100644 --- a/common/commonspace/settingsdocument/settingsdocument.go +++ b/common/commonspace/settingsdocument/settingsdocument.go @@ -8,6 +8,7 @@ import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree/updatelistener" "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter" "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/tree" + "sync" ) type DeletionState int @@ -19,7 +20,7 @@ const ( type SettingsDocument interface { tree.ObjectTree - Init() + Refresh() DeleteObject(id string) (err error) } @@ -37,14 +38,15 @@ type Deps struct { type settingsDocument struct { tree.ObjectTree - account account.Service - spaceId string - deletionState map[string]DeletionState - treeGetter treegetter.TreeGetter - store spacestorage.SpaceStorage - lastChangeId string - prov deletedIdsProvider - removeNotifyFunc RemoveObjectsFunc + account account.Service + spaceId string + deletionState map[string]DeletionState + treeGetter treegetter.TreeGetter + store spacestorage.SpaceStorage + lastChangeId string + prov deletedIdsProvider + removeNotifyFunc RemoveObjectsFunc + deletionStateLock sync.Mutex } func NewSettingsDocument(ctx context.Context, deps Deps, spaceId string) (doc SettingsDocument, err error) { @@ -68,6 +70,14 @@ func NewSettingsDocument(ctx context.Context, deps Deps, spaceId string) (doc Se return } +func (s *settingsDocument) NotifyHeadsUpdate(id string) { + s.deletionStateLock.Lock() + if _, exists := s.deletionState[id]; exists { + s.deletionState[id] = DeletionStateQueued + } + s.deletionStateLock.Unlock() +} + func (s *settingsDocument) Update(tr tree.ObjectTree) { ids, lastId, err := s.prov.ProvideIds(tr, s.lastChangeId) if err != nil { @@ -86,7 +96,7 @@ func (s *settingsDocument) Rebuild(tr tree.ObjectTree) { s.toBeDeleted(ids) } -func (s *settingsDocument) Init() { +func (s *settingsDocument) Refresh() { s.Lock() defer s.Unlock() s.Rebuild(s) @@ -94,26 +104,37 @@ func (s *settingsDocument) Init() { func (s *settingsDocument) toBeDeleted(ids []string) { for _, id := range ids { + s.deletionStateLock.Lock() if state, exists := s.deletionState[id]; exists && state == DeletionStateDeleted { + s.deletionStateLock.Unlock() continue } // if not already deleted + // TODO: here we can possibly have problems if the document is synced later, maybe we should block syncing with deleted documents if _, err := s.store.TreeStorage(id); err == nil { s.deletionState[id] = DeletionStateQueued + s.deletionStateLock.Unlock() + // doing this without lock err := s.treeGetter.DeleteTree(context.Background(), s.spaceId, id) if err != nil { // TODO: some errors may tell us that the tree is actually deleted, so we should have more checks here // TODO: add logging continue } + // TODO: add loop to double check that everything that should be deleted is actually deleted + s.deletionStateLock.Lock() } + s.deletionState[id] = DeletionStateDeleted + s.deletionStateLock.Unlock() } // notifying about removal s.removeNotifyFunc(ids) } func (s *settingsDocument) DeleteObject(id string) (err error) { + s.Lock() + defer s.Unlock() content := &spacesyncproto.SpaceSettingsContent_ObjectDelete{ ObjectDelete: &spacesyncproto.ObjectDelete{Id: id}, } @@ -127,8 +148,6 @@ func (s *settingsDocument) DeleteObject(id string) (err error) { if err != nil { return } - s.Lock() - defer s.Unlock() _, err = s.AddContent(context.Background(), tree.SignableChangeContent{ Data: res, Key: s.account.Account().SignKey, diff --git a/common/commonspace/space.go b/common/commonspace/space.go index f6d4087d..55d9f338 100644 --- a/common/commonspace/space.go +++ b/common/commonspace/space.go @@ -156,7 +156,7 @@ func (s *space) Init(ctx context.Context) (err error) { if err != nil { return } - s.settingsDocument.Init() + s.settingsDocument.Refresh() s.aclList = syncacl.NewSyncACL(aclList, s.syncService.StreamPool()) objectGetter := newCommonSpaceGetter(s.id, s.aclList, s.cache, s.settingsDocument) s.syncService.Init(objectGetter)