From 65947f8ff3dd15390bfd71078870f648f3561514 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Mon, 16 Oct 2023 15:11:02 +0200 Subject: [PATCH] GO-2059 WIP deletion controller --- space/deletion.go | 20 +++++--- space/deletioncontroller.go | 96 +++++++++++++++++++++++++++++++++---- space/load.go | 4 ++ space/service.go | 16 ++++--- space/status.go | 10 ++++ 5 files changed, 125 insertions(+), 21 deletions(-) diff --git a/space/deletion.go b/space/deletion.go index 49f8bd990..adf01978f 100644 --- a/space/deletion.go +++ b/space/deletion.go @@ -9,14 +9,16 @@ import ( ) func (s *service) Delete(ctx context.Context, id string) error { + s.mu.Lock() status := s.getStatus(id) status.AccountStatus = spaceinfo.AccountStatusDeleted err := s.setStatus(ctx, status) + s.mu.Unlock() if err != nil { return err } if status.RemoteStatus != spaceinfo.RemoteStatusDeleted || status.RemoteStatus != spaceinfo.RemoteStatusWaitingDeletion { - err = s.delController.NetworkDelete(ctx, id) + _, err := s.spaceCore.Delete(ctx, id) if err != nil { log.Warn("network delete error", zap.Error(err), zap.String("spaceId", id)) } @@ -28,18 +30,24 @@ func (s *service) Delete(ctx context.Context, id string) error { if err != nil { return err } + s.mu.Lock() + status = s.getStatus(id) status.LocalStatus = spaceinfo.LocalStatusMissing - return nil + err = s.setStatus(ctx, status) + s.mu.Unlock() + return err } func (s *service) offload(ctx context.Context, id string) (err error) { sp, err := s.Get(ctx, id) - if err != nil { + if err != nil && err != ErrSpaceDeleted { return err } - err = sp.Close(ctx) - if err != nil { - return + if err == nil { + err = sp.Close(ctx) + if err != nil { + return + } } err = s.storageService.DeleteSpaceStorage(ctx, id) if err != nil { diff --git a/space/deletioncontroller.go b/space/deletioncontroller.go index c2e5894a8..027b49dcf 100644 --- a/space/deletioncontroller.go +++ b/space/deletioncontroller.go @@ -3,24 +3,102 @@ package space import ( "context" "time" + + "github.com/anyproto/any-sync/coordinator/coordinatorclient" + "github.com/anyproto/any-sync/coordinator/coordinatorproto" + "github.com/anyproto/any-sync/util/periodicsync" + "go.uber.org/zap" + + "github.com/anyproto/anytype-heart/space/spacecore" + "github.com/anyproto/anytype-heart/space/spaceinfo" ) -type spaceDeleter interface { - Delete(ctx context.Context, id string, deletionPeriod time.Duration) (err error) +const ( + loopPeriodSecs = 60 + loopTimeout = time.Second * 10 +) + +type loopAction int + +const ( + loopActionNothing = iota + loopActionDeleteLocally + loopActionDeleteRemotely +) + +type localDeleter interface { + Delete(ctx context.Context, id string) (err error) + allStatuses() (statuses []spaceinfo.SpaceInfo) +} + +type remoteDeleter interface { + Delete(ctx context.Context, spaceID string) (payload spacecore.NetworkStatus, err error) } type deletionController struct { - deleter spaceDeleter + localDeleter localDeleter + remoteDeleter remoteDeleter + client coordinatorclient.CoordinatorClient + + periodicCall periodicsync.PeriodicSync } -func newDeletionController(deleter spaceDeleter) *deletionController { - return &deletionController{deleter} +func newDeletionController( + localDeleter localDeleter, + remoteDeleter remoteDeleter, + client coordinatorclient.CoordinatorClient) *deletionController { + d := &deletionController{ + localDeleter: localDeleter, + remoteDeleter: remoteDeleter, + client: client, + } + d.periodicCall = periodicsync.NewPeriodicSync(loopPeriodSecs, loopTimeout, d.loopIterate, log) + return d } -func (d *deletionController) Run(ctx context.Context) (err error) { - return nil +func (d *deletionController) Run() { + d.periodicCall.Run() } -func (d *deletionController) NetworkDelete(ctx context.Context, id string) (err error) { - return nil +func (d *deletionController) Close() { + d.periodicCall.Close() +} + +func (d *deletionController) loopIterate(ctx context.Context) (err error) { + var spaceIDs []string + localStatuses := d.localDeleter.allStatuses() + for _, status := range localStatuses { + spaceIDs = append(spaceIDs, status.SpaceID) + } + remoteStatuses, err := d.client.StatusCheckMany(ctx, spaceIDs) + if err != nil { + return + } + for idx, remoteStatus := range remoteStatuses { + localStatus := localStatuses[idx] + action := d.compareStatuses(localStatuses[idx], remoteStatus.Status) + switch action { + case loopActionDeleteLocally: + err = d.localDeleter.Delete(ctx, localStatus.SpaceID) + if err != nil { + log.Warn("local delete error", zap.Error(err), zap.String("spaceId", localStatus.SpaceID)) + } + case loopActionDeleteRemotely: + _, err = d.remoteDeleter.Delete(ctx, localStatus.SpaceID) + if err != nil { + log.Warn("remote delete error", zap.Error(err), zap.String("spaceId", localStatus.SpaceID)) + } + } + } + return +} + +func (d *deletionController) compareStatuses(localStatus spaceinfo.SpaceInfo, remoteStatus coordinatorproto.SpaceStatus) loopAction { + if localStatus.LocalStatus == spaceinfo.LocalStatusOk && remoteStatus == coordinatorproto.SpaceStatus_SpaceStatusDeleted { + return loopActionDeleteLocally + } + if localStatus.AccountStatus == spaceinfo.AccountStatusDeleted && remoteStatus != coordinatorproto.SpaceStatus_SpaceStatusDeleted { + return loopActionDeleteRemotely + } + return loopActionNothing } diff --git a/space/load.go b/space/load.go index 482880f00..a36b3afdf 100644 --- a/space/load.go +++ b/space/load.go @@ -18,6 +18,10 @@ func (s *service) startLoad(ctx context.Context, spaceID string) (err error) { if status.LocalStatus != spaceinfo.LocalStatusUnknown { return nil } + // If space is not loading, but it is deleted, return error + if status.AccountStatus == spaceinfo.AccountStatusDeleted { + return ErrSpaceDeleted + } exists, err := s.techSpace.SpaceViewExists(ctx, spaceID) if err != nil { diff --git a/space/service.go b/space/service.go index e7c8c7e13..8f3cf8384 100644 --- a/space/service.go +++ b/space/service.go @@ -11,6 +11,7 @@ import ( "github.com/anyproto/any-sync/accountservice" "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/app/logger" + "github.com/anyproto/any-sync/coordinator/coordinatorclient" "github.com/gogo/protobuf/types" "go.uber.org/zap" @@ -26,10 +27,10 @@ const CName = "client.space" var log = logger.NewNamed(CName) var ( - ErrIncorrectSpaceID = errors.New("incorrect space id") - ErrSpaceNotExists = errors.New("space not exists") - ErrSpaceWaitingForDeletion = errors.New("space waiting for deletion") - ErrStatusUnkown = errors.New("space status is unknown") + ErrIncorrectSpaceID = errors.New("incorrect space id") + ErrSpaceNotExists = errors.New("space not exists") + ErrSpaceDeleted = errors.New("space is deleted") + ErrStatusUnkown = errors.New("space status is unknown") ) func New() Service { @@ -96,7 +97,8 @@ func (s *service) Init(a *app.App) (err error) { s.bundledObjectsInstaller = app.MustComponent[bundledObjectsInstaller](a) s.newAccount = app.MustComponent[isNewAccount](a).IsNewAccount() s.storageService = app.MustComponent[storage.ClientStorage](a) - s.delController = newDeletionController(s) + coordClient := app.MustComponent[coordinatorclient.CoordinatorClient](a) + s.delController = newDeletionController(s, s.spaceCore, coordClient) s.createdSpaces = map[string]struct{}{} s.statuses = map[string]spaceinfo.SpaceInfo{} s.loading = map[string]*loadingSpace{} @@ -127,6 +129,7 @@ func (s *service) Run(ctx context.Context) (err error) { if err != nil { return fmt.Errorf("init personal space: %w", err) } + s.delController.Run() return nil } @@ -195,7 +198,8 @@ func (s *service) Close(ctx context.Context) (err error) { log.Error("close space", zap.String("spaceId", sp.Id()), zap.Error(err)) } } - return nil + s.delController.Close() + return } func getRepKey(spaceID string) (uint64, error) { diff --git a/space/status.go b/space/status.go index fb8971d4e..5a6f7fad0 100644 --- a/space/status.go +++ b/space/status.go @@ -20,3 +20,13 @@ func (s *service) setStatus(ctx context.Context, info spaceinfo.SpaceInfo) (err s.statuses[info.SpaceID] = info return nil } + +func (s *service) allStatuses() (statuses []spaceinfo.SpaceInfo) { + s.mu.Lock() + defer s.mu.Unlock() + statuses = make([]spaceinfo.SpaceInfo, 0, len(s.statuses)) + for _, status := range s.statuses { + statuses = append(statuses, status) + } + return +}