Add sync loop to settings document
This commit is contained in:
parent
a5fe46e50e
commit
a6507db992
@ -65,6 +65,7 @@ func (s *service) Run(ctx context.Context) (err error) {
|
|||||||
mux.HandleFunc("/loadSpace", s.loadSpace)
|
mux.HandleFunc("/loadSpace", s.loadSpace)
|
||||||
mux.HandleFunc("/allSpaceIds", s.allSpaceIds)
|
mux.HandleFunc("/allSpaceIds", s.allSpaceIds)
|
||||||
mux.HandleFunc("/createDocument", s.createDocument)
|
mux.HandleFunc("/createDocument", s.createDocument)
|
||||||
|
mux.HandleFunc("/deleteDocument", s.deleteDocument)
|
||||||
mux.HandleFunc("/allDocumentIds", s.allDocumentIds)
|
mux.HandleFunc("/allDocumentIds", s.allDocumentIds)
|
||||||
mux.HandleFunc("/addText", s.addText)
|
mux.HandleFunc("/addText", s.addText)
|
||||||
mux.HandleFunc("/dumpDocumentTree", s.dumpDocumentTree)
|
mux.HandleFunc("/dumpDocumentTree", s.dumpDocumentTree)
|
||||||
@ -134,6 +135,18 @@ func (s *service) createDocument(w http.ResponseWriter, req *http.Request) {
|
|||||||
sendText(w, http.StatusOK, id)
|
sendText(w, http.StatusOK, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) deleteDocument(w http.ResponseWriter, req *http.Request) {
|
||||||
|
query := req.URL.Query()
|
||||||
|
spaceId := query.Get("spaceId")
|
||||||
|
documentId := query.Get("documentId")
|
||||||
|
err := s.controller.DeleteDocument(spaceId, documentId)
|
||||||
|
if err != nil {
|
||||||
|
sendText(w, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sendText(w, http.StatusOK, documentId)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *service) allDocumentIds(w http.ResponseWriter, req *http.Request) {
|
func (s *service) allDocumentIds(w http.ResponseWriter, req *http.Request) {
|
||||||
query := req.URL.Query()
|
query := req.URL.Query()
|
||||||
spaceId := query.Get("spaceId")
|
spaceId := query.Get("spaceId")
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import (
|
|||||||
type Service interface {
|
type Service interface {
|
||||||
app.Component
|
app.Component
|
||||||
CreateDocument(spaceId string) (id string, err error)
|
CreateDocument(spaceId string) (id string, err error)
|
||||||
|
DeleteDocument(spaceId, documentId string) (err error)
|
||||||
AllDocumentIds(spaceId string) (ids []string, err error)
|
AllDocumentIds(spaceId string) (ids []string, err error)
|
||||||
AddText(spaceId, documentId, text string) (err error)
|
AddText(spaceId, documentId, text string) (err error)
|
||||||
DumpDocumentTree(spaceId, documentId string) (dump string, err error)
|
DumpDocumentTree(spaceId, documentId string) (dump string, err error)
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/nodeconf"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/nodeconf"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/ldiff"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/ldiff"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/periodicsync"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -25,7 +26,7 @@ type DiffService interface {
|
|||||||
|
|
||||||
type diffService struct {
|
type diffService struct {
|
||||||
spaceId string
|
spaceId string
|
||||||
periodicSync PeriodicSync
|
periodicSync periodicsync.PeriodicSync
|
||||||
storage storage.SpaceStorage
|
storage storage.SpaceStorage
|
||||||
diff ldiff.Diff
|
diff ldiff.Diff
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
@ -46,7 +47,7 @@ func NewDiffService(
|
|||||||
l := log.With(zap.String("spaceId", spaceId))
|
l := log.With(zap.String("spaceId", spaceId))
|
||||||
factory := spacesyncproto.ClientFactoryFunc(spacesyncproto.NewDRPCSpaceClient)
|
factory := spacesyncproto.ClientFactoryFunc(spacesyncproto.NewDRPCSpaceClient)
|
||||||
syncer := newDiffSyncer(spaceId, diff, confConnector, cache, storage, factory, l)
|
syncer := newDiffSyncer(spaceId, diff, confConnector, cache, storage, factory, l)
|
||||||
periodicSync := newPeriodicSync(syncPeriod, syncer, l)
|
periodicSync := periodicsync.NewPeriodicSync(syncPeriod, syncer.Sync, l)
|
||||||
|
|
||||||
return &diffService{
|
return &diffService{
|
||||||
spaceId: spaceId,
|
spaceId: spaceId,
|
||||||
|
|||||||
@ -3,3 +3,9 @@ package diffservice
|
|||||||
type HeadNotifiable interface {
|
type HeadNotifiable interface {
|
||||||
UpdateHeads(id string, heads []string)
|
UpdateHeads(id string, heads []string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HeadNotifiableFunc func(id string, heads []string)
|
||||||
|
|
||||||
|
func (h HeadNotifiableFunc) UpdateHeads(id string, heads []string) {
|
||||||
|
h(id, heads)
|
||||||
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ type SettingsDocument interface {
|
|||||||
tree.ObjectTree
|
tree.ObjectTree
|
||||||
Refresh()
|
Refresh()
|
||||||
DeleteObject(id string) (err error)
|
DeleteObject(id string) (err error)
|
||||||
|
NotifyObjectUpdate(id string)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BuildTreeFunc func(ctx context.Context, id string, listener updatelistener.UpdateListener) (t tree.ObjectTree, err error)
|
type BuildTreeFunc func(ctx context.Context, id string, listener updatelistener.UpdateListener) (t tree.ObjectTree, err error)
|
||||||
@ -33,6 +34,7 @@ type Deps struct {
|
|||||||
TreeGetter treegetter.TreeGetter
|
TreeGetter treegetter.TreeGetter
|
||||||
Store spacestorage.SpaceStorage
|
Store spacestorage.SpaceStorage
|
||||||
RemoveFunc RemoveObjectsFunc
|
RemoveFunc RemoveObjectsFunc
|
||||||
|
// prov exists mainly for the ease of testing
|
||||||
prov deletedIdsProvider
|
prov deletedIdsProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,9 +72,10 @@ func NewSettingsDocument(ctx context.Context, deps Deps, spaceId string) (doc Se
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *settingsDocument) NotifyHeadsUpdate(id string) {
|
func (s *settingsDocument) NotifyObjectUpdate(id string) {
|
||||||
s.deletionStateLock.Lock()
|
s.deletionStateLock.Lock()
|
||||||
if _, exists := s.deletionState[id]; exists {
|
if state, exists := s.deletionState[id]; exists && state == DeletionStateDeleted {
|
||||||
|
// marking the document as queued, that means that document appeared later than we checked the storage for deletion
|
||||||
s.deletionState[id] = DeletionStateQueued
|
s.deletionState[id] = DeletionStateQueued
|
||||||
}
|
}
|
||||||
s.deletionStateLock.Unlock()
|
s.deletionStateLock.Unlock()
|
||||||
@ -109,8 +112,7 @@ func (s *settingsDocument) toBeDeleted(ids []string) {
|
|||||||
s.deletionStateLock.Unlock()
|
s.deletionStateLock.Unlock()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// if not already deleted
|
// if the document is not in storage it can happen that it will appear later, for that we have NotifyObjectUpdate method
|
||||||
// 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 {
|
if _, err := s.store.TreeStorage(id); err == nil {
|
||||||
s.deletionState[id] = DeletionStateQueued
|
s.deletionState[id] = DeletionStateQueued
|
||||||
s.deletionStateLock.Unlock()
|
s.deletionStateLock.Unlock()
|
||||||
@ -121,14 +123,13 @@ func (s *settingsDocument) toBeDeleted(ids []string) {
|
|||||||
// TODO: add logging
|
// TODO: add logging
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// TODO: add loop to double check that everything that should be deleted is actually deleted
|
|
||||||
s.deletionStateLock.Lock()
|
s.deletionStateLock.Lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
s.deletionState[id] = DeletionStateDeleted
|
s.deletionState[id] = DeletionStateDeleted
|
||||||
s.deletionStateLock.Unlock()
|
s.deletionStateLock.Unlock()
|
||||||
}
|
}
|
||||||
// notifying about removal
|
// notifying diff service that the ids should not be synced anymore
|
||||||
s.removeNotifyFunc(ids)
|
s.removeNotifyFunc(ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/tree"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/tree"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/asymmetric/encryptionkey"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/asymmetric/encryptionkey"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/asymmetric/signingkey"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/asymmetric/signingkey"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/periodicsync"
|
||||||
"github.com/zeebo/errs"
|
"github.com/zeebo/errs"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"sync"
|
"sync"
|
||||||
@ -41,7 +42,10 @@ type SpaceCreatePayload struct {
|
|||||||
ReplicationKey uint64
|
ReplicationKey uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
const SpaceTypeDerived = "derived.space"
|
const (
|
||||||
|
SpaceTypeDerived = "derived.space"
|
||||||
|
SettingsSyncPeriodSeconds = 10
|
||||||
|
)
|
||||||
|
|
||||||
type SpaceDerivePayload struct {
|
type SpaceDerivePayload struct {
|
||||||
SigningKey signingkey.PrivKey
|
SigningKey signingkey.PrivKey
|
||||||
@ -92,6 +96,8 @@ type space struct {
|
|||||||
aclList *syncacl.SyncACL
|
aclList *syncacl.SyncACL
|
||||||
configuration nodeconf.Configuration
|
configuration nodeconf.Configuration
|
||||||
settingsDocument settingsdocument.SettingsDocument
|
settingsDocument settingsdocument.SettingsDocument
|
||||||
|
settingsSync periodicsync.PeriodicSync
|
||||||
|
headNotifiable diffservice.HeadNotifiable
|
||||||
|
|
||||||
isClosed atomic.Bool
|
isClosed atomic.Bool
|
||||||
}
|
}
|
||||||
@ -156,11 +162,19 @@ func (s *space) Init(ctx context.Context) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
s.headNotifiable = diffservice.HeadNotifiableFunc(func(id string, heads []string) {
|
||||||
|
s.diffService.UpdateHeads(id, heads)
|
||||||
|
s.settingsDocument.NotifyObjectUpdate(id)
|
||||||
|
})
|
||||||
s.settingsDocument.Refresh()
|
s.settingsDocument.Refresh()
|
||||||
s.aclList = syncacl.NewSyncACL(aclList, s.syncService.StreamPool())
|
s.aclList = syncacl.NewSyncACL(aclList, s.syncService.StreamPool())
|
||||||
objectGetter := newCommonSpaceGetter(s.id, s.aclList, s.cache, s.settingsDocument)
|
objectGetter := newCommonSpaceGetter(s.id, s.aclList, s.cache, s.settingsDocument)
|
||||||
s.syncService.Init(objectGetter)
|
s.syncService.Init(objectGetter)
|
||||||
s.diffService.Init(initialIds)
|
s.diffService.Init(initialIds)
|
||||||
|
s.settingsSync = periodicsync.NewPeriodicSync(SettingsSyncPeriodSeconds, func(ctx context.Context) error {
|
||||||
|
s.settingsDocument.Refresh()
|
||||||
|
return nil
|
||||||
|
}, log)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +204,7 @@ func (s *space) DeriveTree(ctx context.Context, payload tree.ObjectTreeCreatePay
|
|||||||
Payload: payload,
|
Payload: payload,
|
||||||
StreamPool: s.syncService.StreamPool(),
|
StreamPool: s.syncService.StreamPool(),
|
||||||
Configuration: s.configuration,
|
Configuration: s.configuration,
|
||||||
HeadNotifiable: s.diffService,
|
HeadNotifiable: s.headNotifiable,
|
||||||
Listener: listener,
|
Listener: listener,
|
||||||
AclList: s.aclList,
|
AclList: s.aclList,
|
||||||
CreateStorage: s.storage.CreateTreeStorage,
|
CreateStorage: s.storage.CreateTreeStorage,
|
||||||
@ -208,7 +222,7 @@ func (s *space) CreateTree(ctx context.Context, payload tree.ObjectTreeCreatePay
|
|||||||
Payload: payload,
|
Payload: payload,
|
||||||
StreamPool: s.syncService.StreamPool(),
|
StreamPool: s.syncService.StreamPool(),
|
||||||
Configuration: s.configuration,
|
Configuration: s.configuration,
|
||||||
HeadNotifiable: s.diffService,
|
HeadNotifiable: s.headNotifiable,
|
||||||
Listener: listener,
|
Listener: listener,
|
||||||
AclList: s.aclList,
|
AclList: s.aclList,
|
||||||
CreateStorage: s.storage.CreateTreeStorage,
|
CreateStorage: s.storage.CreateTreeStorage,
|
||||||
@ -225,7 +239,7 @@ func (s *space) BuildTree(ctx context.Context, id string, listener updatelistene
|
|||||||
SpaceId: s.id,
|
SpaceId: s.id,
|
||||||
StreamPool: s.syncService.StreamPool(),
|
StreamPool: s.syncService.StreamPool(),
|
||||||
Configuration: s.configuration,
|
Configuration: s.configuration,
|
||||||
HeadNotifiable: s.diffService,
|
HeadNotifiable: s.headNotifiable,
|
||||||
Listener: listener,
|
Listener: listener,
|
||||||
AclList: s.aclList,
|
AclList: s.aclList,
|
||||||
SpaceStorage: s.storage,
|
SpaceStorage: s.storage,
|
||||||
@ -250,6 +264,7 @@ func (s *space) Close() error {
|
|||||||
if err := s.syncService.Close(); err != nil {
|
if err := s.syncService.Close(); err != nil {
|
||||||
mError.Add(err)
|
mError.Add(err)
|
||||||
}
|
}
|
||||||
|
s.settingsSync.Close()
|
||||||
if err := s.settingsDocument.Close(); err != nil {
|
if err := s.settingsDocument.Close(); err != nil {
|
||||||
mError.Add(err)
|
mError.Add(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package diffservice
|
package periodicsync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -11,7 +11,9 @@ type PeriodicSync interface {
|
|||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPeriodicSync(periodSeconds int, syncer DiffSyncer, l *zap.Logger) *periodicSync {
|
type SyncerFunc func(ctx context.Context) error
|
||||||
|
|
||||||
|
func NewPeriodicSync(periodSeconds int, syncer SyncerFunc, l *zap.Logger) PeriodicSync {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
return &periodicSync{
|
return &periodicSync{
|
||||||
syncer: syncer,
|
syncer: syncer,
|
||||||
@ -25,7 +27,7 @@ func newPeriodicSync(periodSeconds int, syncer DiffSyncer, l *zap.Logger) *perio
|
|||||||
|
|
||||||
type periodicSync struct {
|
type periodicSync struct {
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
syncer DiffSyncer
|
syncer SyncerFunc
|
||||||
syncCtx context.Context
|
syncCtx context.Context
|
||||||
syncCancel context.CancelFunc
|
syncCancel context.CancelFunc
|
||||||
syncLoopDone chan struct{}
|
syncLoopDone chan struct{}
|
||||||
@ -42,7 +44,7 @@ func (p *periodicSync) syncLoop(periodSeconds int) {
|
|||||||
doSync := func() {
|
doSync := func() {
|
||||||
ctx, cancel := context.WithTimeout(p.syncCtx, time.Minute)
|
ctx, cancel := context.WithTimeout(p.syncCtx, time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if err := p.syncer.Sync(ctx); err != nil {
|
if err := p.syncer(ctx); err != nil {
|
||||||
p.log.Warn("periodic sync error", zap.Error(err))
|
p.log.Warn("periodic sync error", zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,9 +1,10 @@
|
|||||||
package diffservice
|
package periodicsync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/diffservice/mock_diffservice"
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -14,25 +15,34 @@ func TestPeriodicSync_Run(t *testing.T) {
|
|||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
|
|
||||||
l := logger.NewNamed("sync")
|
l := logger.NewNamed("sync")
|
||||||
diffSyncer := mock_diffservice.NewMockDiffSyncer(ctrl)
|
|
||||||
t.Run("diff syncer 1 time", func(t *testing.T) {
|
t.Run("diff syncer 1 time", func(t *testing.T) {
|
||||||
secs := 0
|
secs := 0
|
||||||
pSync := newPeriodicSync(secs, diffSyncer, l)
|
times := 0
|
||||||
|
diffSyncer := func(ctx context.Context) (err error) {
|
||||||
diffSyncer.EXPECT().Sync(gomock.Any()).Times(1).Return(nil)
|
times += 1
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pSync := NewPeriodicSync(secs, diffSyncer, l)
|
||||||
|
|
||||||
pSync.Run()
|
pSync.Run()
|
||||||
pSync.Close()
|
pSync.Close()
|
||||||
|
require.Equal(t, 1, times)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("diff syncer 2 times", func(t *testing.T) {
|
t.Run("diff syncer 2 times", func(t *testing.T) {
|
||||||
secs := 1
|
secs := 1
|
||||||
|
|
||||||
pSync := newPeriodicSync(secs, diffSyncer, l)
|
times := 0
|
||||||
diffSyncer.EXPECT().Sync(gomock.Any()).Times(2).Return(nil)
|
diffSyncer := func(ctx context.Context) (err error) {
|
||||||
|
times += 1
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pSync := NewPeriodicSync(secs, diffSyncer, l)
|
||||||
|
|
||||||
pSync.Run()
|
pSync.Run()
|
||||||
time.Sleep(time.Second * time.Duration(secs))
|
time.Sleep(time.Second * time.Duration(secs))
|
||||||
pSync.Close()
|
pSync.Close()
|
||||||
|
require.Equal(t, 2, times)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user