浏览代码

fix(syncing-server): log syncing errors

Karol Sójko 1 年之前
父节点
当前提交
b9c9f74d0c

+ 1 - 0
packages/syncing-server/src/Bootstrap/Container.ts

@@ -673,6 +673,7 @@ export class ContainerConfigLoader {
           container.get(TYPES.Sync_GetSharedVaultInvitesSentToUser),
           container.get(TYPES.Sync_GetMessagesSentToUser),
           container.get(TYPES.Sync_GetUserNotifications),
+          container.get(TYPES.Sync_Timer),
         ),
       )
     container.bind<CheckIntegrity>(TYPES.Sync_CheckIntegrity).toDynamicValue((context: interfaces.Context) => {

+ 37 - 0
packages/syncing-server/src/Domain/UseCase/Syncing/SyncItems/SyncItems.spec.ts

@@ -14,6 +14,7 @@ import { GetMessagesSentToUser } from '../../Messaging/GetMessagesSentToUser/Get
 import { GetUserNotifications } from '../../Messaging/GetUserNotifications/GetUserNotifications'
 import { GetSharedVaultInvitesSentToUser } from '../../SharedVaults/GetSharedVaultInvitesSentToUser/GetSharedVaultInvitesSentToUser'
 import { ItemRepositoryResolverInterface } from '../../../Item/ItemRepositoryResolverInterface'
+import { Logger } from 'winston'
 
 describe('SyncItems', () => {
   let getItemsUseCase: GetItems
@@ -28,6 +29,7 @@ describe('SyncItems', () => {
   let getSharedVaultInvitesSentToUserUseCase: GetSharedVaultInvitesSentToUser
   let getMessagesSentToUser: GetMessagesSentToUser
   let getUserNotifications: GetUserNotifications
+  let logger: Logger
 
   const createUseCase = () =>
     new SyncItems(
@@ -38,9 +40,15 @@ describe('SyncItems', () => {
       getSharedVaultInvitesSentToUserUseCase,
       getMessagesSentToUser,
       getUserNotifications,
+      logger,
     )
 
   beforeEach(() => {
+    logger = {} as jest.Mocked<Logger>
+    logger.info = jest.fn()
+    logger.debug = jest.fn()
+    logger.error = jest.fn()
+
     item1 = Item.create(
       {
         userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
@@ -188,6 +196,35 @@ describe('SyncItems', () => {
     })
   })
 
+  it('should log error if sync items throws an error', async () => {
+    getItemsUseCase.execute = jest.fn().mockImplementation(() => {
+      throw new Error('error')
+    })
+
+    let caughtError = null
+    try {
+      await createUseCase().execute({
+        userUuid: '1-2-3',
+        onGoingRevisionsTransition: false,
+        itemHashes: [itemHash],
+        computeIntegrityHash: false,
+        syncToken: 'foo',
+        cursorToken: 'bar',
+        limit: 10,
+        readOnlyAccess: false,
+        contentType: 'Note',
+        apiVersion: ApiVersion.v20200115,
+        sessionUuid: null,
+        snjsVersion: '1.2.3',
+        roleNames: [RoleName.NAMES.CoreUser],
+      })
+    } catch (error) {
+      caughtError = error
+    }
+
+    expect(caughtError).not.toBeNull()
+  })
+
   it('should sync items and return items keys on top for first sync that is not a shared vault exclusive sync', async () => {
     const result = await createUseCase().execute({
       userUuid: '1-2-3',

+ 100 - 87
packages/syncing-server/src/Domain/UseCase/Syncing/SyncItems/SyncItems.ts

@@ -11,6 +11,7 @@ import { GetSharedVaultInvitesSentToUser } from '../../SharedVaults/GetSharedVau
 import { GetMessagesSentToUser } from '../../Messaging/GetMessagesSentToUser/GetMessagesSentToUser'
 import { GetUserNotifications } from '../../Messaging/GetUserNotifications/GetUserNotifications'
 import { ItemRepositoryResolverInterface } from '../../../Item/ItemRepositoryResolverInterface'
+import { Logger } from 'winston'
 
 export class SyncItems implements UseCaseInterface<SyncItemsResponse> {
   constructor(
@@ -21,99 +22,111 @@ export class SyncItems implements UseCaseInterface<SyncItemsResponse> {
     private getSharedVaultInvitesSentToUserUseCase: GetSharedVaultInvitesSentToUser,
     private getMessagesSentToUser: GetMessagesSentToUser,
     private getUserNotifications: GetUserNotifications,
+    private logger: Logger,
   ) {}
 
   async execute(dto: SyncItemsDTO): Promise<Result<SyncItemsResponse>> {
-    const roleNamesOrError = RoleNameCollection.create(dto.roleNames)
-    if (roleNamesOrError.isFailed()) {
-      return Result.fail(roleNamesOrError.getError())
-    }
-    const roleNames = roleNamesOrError.getValue()
-
-    const getItemsResultOrError = await this.getItemsUseCase.execute({
-      userUuid: dto.userUuid,
-      syncToken: dto.syncToken,
-      cursorToken: dto.cursorToken,
-      limit: dto.limit,
-      contentType: dto.contentType,
-      sharedVaultUuids: dto.sharedVaultUuids,
-      roleNames: dto.roleNames,
-    })
-    if (getItemsResultOrError.isFailed()) {
-      return Result.fail(getItemsResultOrError.getError())
-    }
-    const getItemsResult = getItemsResultOrError.getValue()
-
-    const saveItemsResultOrError = await this.saveItemsUseCase.execute({
-      itemHashes: dto.itemHashes,
-      userUuid: dto.userUuid,
-      apiVersion: dto.apiVersion,
-      readOnlyAccess: dto.readOnlyAccess,
-      sessionUuid: dto.sessionUuid,
-      snjsVersion: dto.snjsVersion,
-      roleNames: dto.roleNames,
-      onGoingRevisionsTransition: dto.onGoingRevisionsTransition,
-    })
-    if (saveItemsResultOrError.isFailed()) {
-      return Result.fail(saveItemsResultOrError.getError())
-    }
-    const saveItemsResult = saveItemsResultOrError.getValue()
-
-    let retrievedItems = this.filterOutSyncConflictsForConsecutiveSyncs(getItemsResult.items, saveItemsResult.conflicts)
-    const isSharedVaultExclusiveSync = dto.sharedVaultUuids && dto.sharedVaultUuids.length > 0
-    if (this.isFirstSync(dto) && !isSharedVaultExclusiveSync) {
-      retrievedItems = await this.frontLoadKeysItemsToTop(dto.userUuid, roleNames, retrievedItems)
-    }
-
-    const sharedVaultsOrError = await this.getSharedVaultsUseCase.execute({
-      userUuid: dto.userUuid,
-      lastSyncTime: getItemsResult.lastSyncTime ?? undefined,
-    })
-    if (sharedVaultsOrError.isFailed()) {
-      return Result.fail(sharedVaultsOrError.getError())
-    }
-    const sharedVaults = sharedVaultsOrError.getValue()
-
-    const sharedVaultInvitesOrError = await this.getSharedVaultInvitesSentToUserUseCase.execute({
-      userUuid: dto.userUuid,
-      lastSyncTime: getItemsResult.lastSyncTime ?? undefined,
-    })
-    if (sharedVaultInvitesOrError.isFailed()) {
-      return Result.fail(sharedVaultInvitesOrError.getError())
-    }
-    const sharedVaultInvites = sharedVaultInvitesOrError.getValue()
+    try {
+      const roleNamesOrError = RoleNameCollection.create(dto.roleNames)
+      if (roleNamesOrError.isFailed()) {
+        return Result.fail(roleNamesOrError.getError())
+      }
+      const roleNames = roleNamesOrError.getValue()
+
+      const getItemsResultOrError = await this.getItemsUseCase.execute({
+        userUuid: dto.userUuid,
+        syncToken: dto.syncToken,
+        cursorToken: dto.cursorToken,
+        limit: dto.limit,
+        contentType: dto.contentType,
+        sharedVaultUuids: dto.sharedVaultUuids,
+        roleNames: dto.roleNames,
+      })
+      if (getItemsResultOrError.isFailed()) {
+        return Result.fail(getItemsResultOrError.getError())
+      }
+      const getItemsResult = getItemsResultOrError.getValue()
+
+      const saveItemsResultOrError = await this.saveItemsUseCase.execute({
+        itemHashes: dto.itemHashes,
+        userUuid: dto.userUuid,
+        apiVersion: dto.apiVersion,
+        readOnlyAccess: dto.readOnlyAccess,
+        sessionUuid: dto.sessionUuid,
+        snjsVersion: dto.snjsVersion,
+        roleNames: dto.roleNames,
+        onGoingRevisionsTransition: dto.onGoingRevisionsTransition,
+      })
+      if (saveItemsResultOrError.isFailed()) {
+        return Result.fail(saveItemsResultOrError.getError())
+      }
+      const saveItemsResult = saveItemsResultOrError.getValue()
+
+      let retrievedItems = this.filterOutSyncConflictsForConsecutiveSyncs(
+        getItemsResult.items,
+        saveItemsResult.conflicts,
+      )
+      const isSharedVaultExclusiveSync = dto.sharedVaultUuids && dto.sharedVaultUuids.length > 0
+      if (this.isFirstSync(dto) && !isSharedVaultExclusiveSync) {
+        retrievedItems = await this.frontLoadKeysItemsToTop(dto.userUuid, roleNames, retrievedItems)
+      }
 
-    const messagesOrError = await this.getMessagesSentToUser.execute({
-      recipientUuid: dto.userUuid,
-      lastSyncTime: getItemsResult.lastSyncTime ?? undefined,
-    })
-    if (messagesOrError.isFailed()) {
-      return Result.fail(messagesOrError.getError())
-    }
-    const messages = messagesOrError.getValue()
+      const sharedVaultsOrError = await this.getSharedVaultsUseCase.execute({
+        userUuid: dto.userUuid,
+        lastSyncTime: getItemsResult.lastSyncTime ?? undefined,
+      })
+      if (sharedVaultsOrError.isFailed()) {
+        return Result.fail(sharedVaultsOrError.getError())
+      }
+      const sharedVaults = sharedVaultsOrError.getValue()
+
+      const sharedVaultInvitesOrError = await this.getSharedVaultInvitesSentToUserUseCase.execute({
+        userUuid: dto.userUuid,
+        lastSyncTime: getItemsResult.lastSyncTime ?? undefined,
+      })
+      if (sharedVaultInvitesOrError.isFailed()) {
+        return Result.fail(sharedVaultInvitesOrError.getError())
+      }
+      const sharedVaultInvites = sharedVaultInvitesOrError.getValue()
+
+      const messagesOrError = await this.getMessagesSentToUser.execute({
+        recipientUuid: dto.userUuid,
+        lastSyncTime: getItemsResult.lastSyncTime ?? undefined,
+      })
+      if (messagesOrError.isFailed()) {
+        return Result.fail(messagesOrError.getError())
+      }
+      const messages = messagesOrError.getValue()
+
+      const notificationsOrError = await this.getUserNotifications.execute({
+        userUuid: dto.userUuid,
+        lastSyncTime: getItemsResult.lastSyncTime ?? undefined,
+      })
+      if (notificationsOrError.isFailed()) {
+        return Result.fail(notificationsOrError.getError())
+      }
+      const notifications = notificationsOrError.getValue()
+
+      const syncResponse: SyncItemsResponse = {
+        retrievedItems,
+        syncToken: saveItemsResult.syncToken,
+        savedItems: saveItemsResult.savedItems,
+        conflicts: saveItemsResult.conflicts,
+        cursorToken: getItemsResult.cursorToken,
+        sharedVaultInvites,
+        sharedVaults,
+        messages,
+        notifications,
+      }
 
-    const notificationsOrError = await this.getUserNotifications.execute({
-      userUuid: dto.userUuid,
-      lastSyncTime: getItemsResult.lastSyncTime ?? undefined,
-    })
-    if (notificationsOrError.isFailed()) {
-      return Result.fail(notificationsOrError.getError())
-    }
-    const notifications = notificationsOrError.getValue()
-
-    const syncResponse: SyncItemsResponse = {
-      retrievedItems,
-      syncToken: saveItemsResult.syncToken,
-      savedItems: saveItemsResult.savedItems,
-      conflicts: saveItemsResult.conflicts,
-      cursorToken: getItemsResult.cursorToken,
-      sharedVaultInvites,
-      sharedVaults,
-      messages,
-      notifications,
+      return Result.ok(syncResponse)
+    } catch (error) {
+      const itemHashUuids = dto.itemHashes.map((itemHash) => itemHash.props.uuid)
+      this.logger.error(
+        `Sync error for user ${dto.userUuid} syncing items ${itemHashUuids.join(',')}: ${(error as Error).message}`,
+      )
+      throw error
     }
-
-    return Result.ok(syncResponse)
   }
 
   private isFirstSync(dto: SyncItemsDTO): boolean {