From be2794a3726bbd8d289106223efc1addd86c2a9c Mon Sep 17 00:00:00 2001 From: Alex <alex.tran1502@gmail.com> Date: Sun, 3 Apr 2022 12:31:45 -0500 Subject: [PATCH] Optimization/fix slow backup when asset list is long. (#104) * Handle pause/restart listening to event on_upload_success and reload asset list after navigating back from BackupControllerPage * Remove unused api endpoint --- mobile/ios/fastlane/Fastfile | 6 +-- .../modules/home/ui/immich_sliver_appbar.dart | 8 +++- mobile/lib/modules/home/views/home_page.dart | 8 +++- .../shared/providers/websocket.provider.dart | 14 ++++++ .../shared/views/backup_controller_page.dart | 3 ++ server/src/api-v1/asset/asset.controller.ts | 14 +----- server/src/api-v1/asset/asset.service.ts | 48 ------------------- 7 files changed, 35 insertions(+), 66 deletions(-) diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 8b62fd6f0f..fc4091e3c2 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -21,9 +21,9 @@ platform :ios do increment_version_number( version_number: "1.5.0" ) - increment_build_number({ - build_number: 0 - }) + increment_build_number( + build_number: latest_testflight_build_number + 1, + ) build_app(scheme: "Runner", workspace: "Runner.xcworkspace", xcargs: "-allowProvisioningUpdates") diff --git a/mobile/lib/modules/home/ui/immich_sliver_appbar.dart b/mobile/lib/modules/home/ui/immich_sliver_appbar.dart index 72d95b854c..185fb08b07 100644 --- a/mobile/lib/modules/home/ui/immich_sliver_appbar.dart +++ b/mobile/lib/modules/home/ui/immich_sliver_appbar.dart @@ -121,8 +121,12 @@ class ImmichSliverAppBar extends ConsumerWidget { ), child: const Icon(Icons.backup_rounded)), tooltip: 'Backup Controller', - onPressed: () { - AutoRouter.of(context).push(const BackupControllerRoute()); + onPressed: () async { + var onPop = await AutoRouter.of(context).push(const BackupControllerRoute()); + + if (onPop != null && onPop == true) { + onPopBack!(); + } }, ), _backupState.backupProgress == BackUpProgressEnum.inProgress diff --git a/mobile/lib/modules/home/views/home_page.dart b/mobile/lib/modules/home/views/home_page.dart index 792b286572..c96c578c9a 100644 --- a/mobile/lib/modules/home/views/home_page.dart +++ b/mobile/lib/modules/home/views/home_page.dart @@ -33,6 +33,10 @@ class HomePage extends HookConsumerWidget { return null; }, []); + void reloadAllAsset() { + ref.read(assetProvider.notifier).getAllAsset(); + } + Widget _buildBody() { if (assetGroupByDateTime.isNotEmpty) { int? lastMonth; @@ -86,7 +90,9 @@ class HomePage extends HookConsumerWidget { child: null, ), ) - : const ImmichSliverAppBar(), + : ImmichSliverAppBar( + onPopBack: reloadAllAsset, + ), duration: const Duration(milliseconds: 350), ), ..._imageGridGroup diff --git a/mobile/lib/shared/providers/websocket.provider.dart b/mobile/lib/shared/providers/websocket.provider.dart index 3a9c0190ec..23565feb33 100644 --- a/mobile/lib/shared/providers/websocket.provider.dart +++ b/mobile/lib/shared/providers/websocket.provider.dart @@ -106,6 +106,20 @@ class WebsocketNotifier extends StateNotifier<WebscoketState> { } } } + + stopListenToEvent(String eventName) { + debugPrint("[Websocket] Stop listening to event $eventName"); + state.socket?.off(eventName); + } + + listenUploadEvent() { + debugPrint("[Websocket] Start listening to event on_upload_success"); + state.socket?.on('on_upload_success', (data) { + var jsonString = jsonDecode(data.toString()); + ImmichAsset newAsset = ImmichAsset.fromMap(jsonString); + ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset); + }); + } } final websocketProvider = StateNotifierProvider<WebsocketNotifier, WebscoketState>((ref) { diff --git a/mobile/lib/shared/views/backup_controller_page.dart b/mobile/lib/shared/views/backup_controller_page.dart index 58b56dad66..3fd1399a10 100644 --- a/mobile/lib/shared/views/backup_controller_page.dart +++ b/mobile/lib/shared/views/backup_controller_page.dart @@ -6,6 +6,7 @@ import 'package:immich_mobile/modules/login/models/authentication_state.model.da import 'package:immich_mobile/shared/models/backup_state.model.dart'; import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; import 'package:immich_mobile/shared/providers/backup.provider.dart'; +import 'package:immich_mobile/shared/providers/websocket.provider.dart'; import 'package:percent_indicator/linear_percent_indicator.dart'; class BackupControllerPage extends HookConsumerWidget { @@ -23,6 +24,7 @@ class BackupControllerPage extends HookConsumerWidget { ref.read(backupProvider.notifier).getBackupInfo(); } + ref.watch(websocketProvider.notifier).stopListenToEvent('on_upload_success'); return null; }, []); @@ -107,6 +109,7 @@ class BackupControllerPage extends HookConsumerWidget { ), leading: IconButton( onPressed: () { + ref.watch(websocketProvider.notifier).listenUploadEvent(); AutoRouter.of(context).pop(true); }, icon: const Icon(Icons.arrow_back_ios_rounded)), diff --git a/server/src/api-v1/asset/asset.controller.ts b/server/src/api-v1/asset/asset.controller.ts index 5bf9fd6764..6e7f9b0ad0 100644 --- a/server/src/api-v1/asset/asset.controller.ts +++ b/server/src/api-v1/asset/asset.controller.ts @@ -115,18 +115,8 @@ export class AssetController { return this.assetService.searchAsset(authUser, searchAssetDto); } - @Get('/new') - async getNewAssets(@GetAuthUser() authUser: AuthUserDto, @Query(ValidationPipe) query: GetNewAssetQueryDto) { - return await this.assetService.getNewAssets(authUser, query.latestDate); - } - - @Get('/all') - async getAllAssets(@GetAuthUser() authUser: AuthUserDto, @Query(ValidationPipe) query: GetAllAssetQueryDto) { - return await this.assetService.getAllAssets(authUser, query); - } - @Get('/') - async getAllAssetsNoPagination(@GetAuthUser() authUser: AuthUserDto) { + async getAllAssets(@GetAuthUser() authUser: AuthUserDto) { return await this.assetService.getAllAssetsNoPagination(authUser); } @@ -137,7 +127,7 @@ export class AssetController { @Get('/assetById/:assetId') async getAssetById(@GetAuthUser() authUser: AuthUserDto, @Param('assetId') assetId) { - return this.assetService.getAssetById(authUser, assetId); + return await this.assetService.getAssetById(authUser, assetId); } @Delete('/') diff --git a/server/src/api-v1/asset/asset.service.ts b/server/src/api-v1/asset/asset.service.ts index a217681366..40d8664e02 100644 --- a/server/src/api-v1/asset/asset.service.ts +++ b/server/src/api-v1/asset/asset.service.ts @@ -76,42 +76,6 @@ export class AssetService { } } - public async getAllAssets(authUser: AuthUserDto, query: GetAllAssetQueryDto): Promise<GetAllAssetReponseDto> { - try { - const assets = await this.assetRepository - .createQueryBuilder('a') - .where('a."userId" = :userId', { userId: authUser.id }) - .andWhere('a."createdAt" < :lastQueryCreatedAt', { - lastQueryCreatedAt: query.nextPageKey || new Date().toISOString(), - }) - .orderBy('a."createdAt"::date', 'DESC') - .take(5000) - .getMany(); - - if (assets.length > 0) { - const data = _.groupBy(assets, (a) => new Date(a.createdAt).toISOString().slice(0, 10)); - const formattedData = []; - Object.keys(data).forEach((v) => formattedData.push({ date: v, assets: data[v] })); - - const response = new GetAllAssetReponseDto(); - response.count = assets.length; - response.data = formattedData; - response.nextPageKey = assets[assets.length - 1].createdAt; - - return response; - } else { - const response = new GetAllAssetReponseDto(); - response.count = 0; - response.data = []; - response.nextPageKey = 'null'; - - return response; - } - } catch (e) { - Logger.error(e, 'getAllAssets'); - } - } - public async findOne(authUser: AuthUserDto, deviceId: string, assetId: string): Promise<AssetEntity> { const rows = await this.assetRepository.query( 'SELECT * FROM assets a WHERE a."deviceAssetId" = $1 AND a."userId" = $2 AND a."deviceId" = $3', @@ -125,18 +89,6 @@ export class AssetService { return rows[0] as AssetEntity; } - public async getNewAssets(authUser: AuthUserDto, latestDate: string) { - return await this.assetRepository.find({ - where: { - userId: authUser.id, - createdAt: MoreThan(latestDate), - }, - order: { - createdAt: 'ASC', // ASC order to add existed asset the latest group first before creating a new date group. - }, - }); - } - public async getAssetById(authUser: AuthUserDto, assetId: string) { return await this.assetRepository.findOne({ where: {