1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-11 02:13:41 +09:00

Merge pull request #1358 from anyproto/fix-bugs

Fix sync bugs and notification object indexing
This commit is contained in:
Anastasia Shemyakinskaya 2024-07-02 13:48:47 +02:00 committed by GitHub
commit e2c084a5ae
Signed by: github
GPG key ID: B5690EEEBB952194
11 changed files with 108 additions and 77 deletions

View file

@ -14,6 +14,7 @@ const (
Syncing SpaceSyncStatus = 1
Error SpaceSyncStatus = 2
Offline SpaceSyncStatus = 3
Unknown SpaceSyncStatus = 4
)
type ObjectSyncStatus int32

View file

@ -168,6 +168,9 @@ func (u *syncStatusUpdater) setObjectDetails(syncStatusDetails *syncStatusDetail
if !changed {
return nil
}
if !u.isLayoutSuitableForSyncRelations(record) {
return nil
}
spc, err := u.spaceService.Get(u.ctx, syncStatusDetails.spaceId)
if err != nil {
return err
@ -196,6 +199,18 @@ func (u *syncStatusUpdater) setObjectDetails(syncStatusDetails *syncStatusDetail
})
}
func (u *syncStatusUpdater) isLayoutSuitableForSyncRelations(details *types.Struct) bool {
layoutsWithoutSyncRelations := []float64{
float64(model.ObjectType_participant),
float64(model.ObjectType_dashboard),
float64(model.ObjectType_spaceView),
float64(model.ObjectType_space),
float64(model.ObjectType_date),
}
layout := details.Fields[bundle.RelationKeyLayout.String()].GetNumberValue()
return !slices.Contains(layoutsWithoutSyncRelations, layout)
}
func mapObjectSyncToSpaceSyncStatus(status domain.ObjectSyncStatus, syncError domain.SyncError) domain.SpaceSyncStatus {
switch status {
case domain.ObjectSynced:

View file

@ -7,6 +7,7 @@ import (
"github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/app/ocache"
"github.com/cheggaaa/mb/v3"
"github.com/gogo/protobuf/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
@ -18,6 +19,7 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
coresb "github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/space/clientspace/mock_clientspace"
"github.com/anyproto/anytype-heart/space/mock_space"
"github.com/anyproto/anytype-heart/tests/testutil"
@ -256,23 +258,33 @@ func TestSyncStatusUpdater_setSyncDetails(t *testing.T) {
})
}
func TestSyncStatusUpdater_updateDetails(t *testing.T) {
t.Run("update sync status and date - no changes", func(t *testing.T) {
func TestSyncStatusUpdater_isLayoutSuitableForSyncRelations(t *testing.T) {
t.Run("isLayoutSuitableForSyncRelations - participant details", func(t *testing.T) {
// given
fixture := newFixture(t)
space := mock_clientspace.NewMockSpace(t)
fixture.service.EXPECT().Get(fixture.updater.ctx, "spaceId").Return(space, nil)
fixture.storeFixture.AddObjects(t, []objectstore.TestObject{
{
bundle.RelationKeyId: pbtypes.String("id"),
bundle.RelationKeySpaceId: pbtypes.String("spaceId"),
},
})
space.EXPECT().DoLockedIfNotExists("id", mock.Anything).Return(nil)
// when
fixture.statusUpdater.EXPECT().SendUpdate(domain.MakeSyncStatus("spaceId", domain.Synced, domain.Null, domain.Objects))
fixture.updater.updateDetails(&syncStatusDetails{nil, domain.ObjectSynced, domain.Null, "spaceId"})
details := &types.Struct{Fields: map[string]*types.Value{
bundle.RelationKeyLayout.String(): pbtypes.Float64(float64(model.ObjectType_participant)),
}}
isSuitable := fixture.updater.isLayoutSuitableForSyncRelations(details)
// then
assert.False(t, isSuitable)
})
t.Run("isLayoutSuitableForSyncRelations - basic details", func(t *testing.T) {
// given
fixture := newFixture(t)
// when
details := &types.Struct{Fields: map[string]*types.Value{
bundle.RelationKeyLayout.String(): pbtypes.Float64(float64(model.ObjectType_basic)),
}}
isSuitable := fixture.updater.isLayoutSuitableForSyncRelations(details)
// then
assert.True(t, isSuitable)
})
}

View file

@ -79,7 +79,10 @@ func (f *FileState) SetSyncStatusAndErr(status domain.SpaceSyncStatus, syncErr d
func (f *FileState) GetSyncStatus(spaceId string) domain.SpaceSyncStatus {
f.Lock()
defer f.Unlock()
return f.fileSyncStatusBySpace[spaceId]
if status, ok := f.fileSyncStatusBySpace[spaceId]; ok {
return status
}
return domain.Unknown
}
func (f *FileState) GetSyncObjectCount(spaceId string) int {

View file

@ -56,7 +56,7 @@ func TestFileState_GetSyncStatus(t *testing.T) {
syncStatus := fileState.GetSyncStatus("spaceId")
// then
assert.Equal(t, domain.Synced, syncStatus)
assert.Equal(t, domain.Unknown, syncStatus)
})
}

View file

@ -158,51 +158,6 @@ func (_c *MockSpaceIdGetter_Name_Call) RunAndReturn(run func() string) *MockSpac
return _c
}
// PersonalSpaceId provides a mock function with given fields:
func (_m *MockSpaceIdGetter) PersonalSpaceId() string {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for PersonalSpaceId")
}
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// MockSpaceIdGetter_PersonalSpaceId_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PersonalSpaceId'
type MockSpaceIdGetter_PersonalSpaceId_Call struct {
*mock.Call
}
// PersonalSpaceId is a helper method to define mock.On call
func (_e *MockSpaceIdGetter_Expecter) PersonalSpaceId() *MockSpaceIdGetter_PersonalSpaceId_Call {
return &MockSpaceIdGetter_PersonalSpaceId_Call{Call: _e.mock.On("PersonalSpaceId")}
}
func (_c *MockSpaceIdGetter_PersonalSpaceId_Call) Run(run func()) *MockSpaceIdGetter_PersonalSpaceId_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockSpaceIdGetter_PersonalSpaceId_Call) Return(_a0 string) *MockSpaceIdGetter_PersonalSpaceId_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockSpaceIdGetter_PersonalSpaceId_Call) RunAndReturn(run func() string) *MockSpaceIdGetter_PersonalSpaceId_Call {
_c.Call.Return(run)
return _c
}
// TechSpaceId provides a mock function with given fields:
func (_m *MockSpaceIdGetter) TechSpaceId() string {
ret := _m.Called()

View file

@ -87,7 +87,10 @@ func (o *ObjectState) SetSyncStatusAndErr(status domain.SpaceSyncStatus, syncErr
func (o *ObjectState) GetSyncStatus(spaceId string) domain.SpaceSyncStatus {
o.Lock()
defer o.Unlock()
return o.objectSyncStatusBySpace[spaceId]
if status, ok := o.objectSyncStatusBySpace[spaceId]; ok {
return status
}
return domain.Unknown
}
func (o *ObjectState) GetSyncObjectCount(spaceId string) int {

View file

@ -55,7 +55,7 @@ func TestObjectState_GetSyncStatus(t *testing.T) {
syncStatus := objectState.GetSyncStatus("spaceId")
// then
assert.Equal(t, domain.Synced, syncStatus)
assert.Equal(t, domain.Unknown, syncStatus)
})
}

View file

@ -26,7 +26,6 @@ type Updater interface {
type SpaceIdGetter interface {
app.Component
TechSpaceId() string
PersonalSpaceId() string
AllSpaceIds() []string
}
@ -89,7 +88,7 @@ func (s *spaceSyncStatus) Run(ctx context.Context) (err error) {
close(s.finish)
return
} else {
s.sendStartEvent(s.spaceIdGetter.PersonalSpaceId())
s.sendStartEvent(s.spaceIdGetter.AllSpaceIds())
}
s.ctx, s.ctxCancel = context.WithCancel(context.Background())
go s.processEvents()
@ -106,14 +105,16 @@ func (s *spaceSyncStatus) sendEventToSession(spaceId, token string) {
})
}
func (s *spaceSyncStatus) sendStartEvent(spaceId string) {
s.eventSender.Broadcast(&pb.Event{
Messages: []*pb.EventMessage{{
Value: &pb.EventMessageValueOfSpaceSyncStatusUpdate{
SpaceSyncStatusUpdate: s.makeSpaceSyncEvent(spaceId),
},
}},
})
func (s *spaceSyncStatus) sendStartEvent(spaceIds []string) {
for _, id := range spaceIds {
s.eventSender.Broadcast(&pb.Event{
Messages: []*pb.EventMessage{{
Value: &pb.EventMessageValueOfSpaceSyncStatusUpdate{
SpaceSyncStatusUpdate: s.makeSpaceSyncEvent(id),
},
}},
})
}
}
func (s *spaceSyncStatus) sendLocalOnlyEvent() {
@ -183,7 +184,11 @@ func (s *spaceSyncStatus) isStatusNotChanged(status *domain.SpaceSync) bool {
return false
}
syncErrNotChanged := s.getError(status.SpaceId) == mapError(status.SyncError)
statusNotChanged := s.getSpaceSyncStatus(status.SpaceId) == status.Status
syncStatus := s.getSpaceSyncStatus(status.SpaceId)
if syncStatus == domain.Unknown {
return false
}
statusNotChanged := syncStatus == status.Status
if syncErrNotChanged && statusNotChanged {
return true
}
@ -221,6 +226,9 @@ func (s *spaceSyncStatus) getSpaceSyncStatus(spaceId string) domain.SpaceSyncSta
filesStatus := s.filesState.GetSyncStatus(spaceId)
objectsStatus := s.objectsState.GetSyncStatus(spaceId)
if s.isUnknown(filesStatus, objectsStatus) {
return domain.Unknown
}
if s.isOfflineStatus(filesStatus, objectsStatus) {
return domain.Offline
}
@ -276,6 +284,10 @@ func (s *spaceSyncStatus) getError(spaceId string) pb.EventSpaceSyncError {
return pb.EventSpace_Null
}
func (s *spaceSyncStatus) isUnknown(filesStatus domain.SpaceSyncStatus, objectsStatus domain.SpaceSyncStatus) bool {
return filesStatus == domain.Unknown && objectsStatus == domain.Unknown
}
func mapNetworkMode(mode pb.RpcAccountNetworkMode) pb.EventSpaceNetwork {
switch mode {
case pb.RpcAccount_LocalOnly:

View file

@ -42,7 +42,7 @@ func TestSpaceSyncStatus_Init(t *testing.T) {
// then
assert.Nil(t, err)
space.EXPECT().PersonalSpaceId().Return("personalId")
space.EXPECT().AllSpaceIds().Return([]string{"personalId"})
eventSender.EXPECT().Broadcast(&pb.Event{
Messages: []*pb.EventMessage{{
Value: &pb.EventMessageValueOfSpaceSyncStatusUpdate{
@ -380,10 +380,38 @@ func TestSpaceSyncStatus_updateSpaceSyncStatus(t *testing.T) {
// when
assert.Equal(t, domain.Synced, status.objectsState.GetSyncStatus("spaceId"))
assert.Equal(t, 0, status.objectsState.GetSyncObjectCount("spaceId"))
assert.Equal(t, domain.Synced, status.filesState.GetSyncStatus("spaceId"))
assert.Equal(t, domain.Unknown, status.filesState.GetSyncStatus("spaceId"))
assert.Equal(t, 0, status.filesState.GetSyncObjectCount("spaceId"))
assert.Equal(t, domain.Synced, status.getSpaceSyncStatus(syncStatus.SpaceId))
})
t.Run("send initial synced event", func(t *testing.T) {
// given
eventSender := mock_event.NewMockSender(t)
status := spaceSyncStatus{
eventSender: eventSender,
networkConfig: &config.Config{NetworkMode: pb.RpcAccount_CustomConfig},
batcher: mb.New[*domain.SpaceSync](0),
filesState: NewFileState(objectstore.NewStoreFixture(t)),
objectsState: NewObjectState(objectstore.NewStoreFixture(t)),
}
eventSender.EXPECT().Broadcast(&pb.Event{
Messages: []*pb.EventMessage{{
Value: &pb.EventMessageValueOfSpaceSyncStatusUpdate{
SpaceSyncStatusUpdate: &pb.EventSpaceSyncStatusUpdate{
Id: "spaceId",
Status: pb.EventSpace_Synced,
Network: pb.EventSpace_SelfHost,
Error: pb.EventSpace_Null,
SyncingObjectsCounter: 0,
},
},
}},
})
// then
syncStatus := domain.MakeSyncStatus("spaceId", domain.Synced, domain.Null, domain.Objects)
status.updateSpaceSyncStatus(syncStatus)
})
t.Run("not send not needed synced event", func(t *testing.T) {
// given
eventSender := mock_event.NewMockSender(t)
@ -394,8 +422,10 @@ func TestSpaceSyncStatus_updateSpaceSyncStatus(t *testing.T) {
filesState: NewFileState(objectstore.NewStoreFixture(t)),
objectsState: NewObjectState(objectstore.NewStoreFixture(t)),
}
// then
status.objectsState.SetSyncStatusAndErr(domain.Synced, domain.Null, "spaceId")
status.filesState.SetSyncStatusAndErr(domain.Synced, domain.Null, "spaceId")
// then
syncStatus := domain.MakeSyncStatus("spaceId", domain.Synced, domain.Null, domain.Objects)
status.updateSpaceSyncStatus(syncStatus)

View file

@ -68,7 +68,7 @@ func (sbt SmartBlockType) IsOneOf(sbts ...SmartBlockType) bool {
// Indexable determines if the object of specific type need to be proceeded by the indexer in order to appear in sets
func (sbt SmartBlockType) Indexable() (details, outgoingLinks bool) {
switch sbt {
case SmartBlockTypeDate, SmartBlockTypeAccountOld, SmartBlockTypeArchive, SmartBlockTypeHome:
case SmartBlockTypeDate, SmartBlockTypeAccountOld, SmartBlockTypeArchive, SmartBlockTypeHome, SmartBlockTypeNotificationObject:
return false, false
case SmartBlockTypeWidget:
return true, false