From fff7b22458ba2f6627f828e4f6780e38fac05e49 Mon Sep 17 00:00:00 2001 From: Sergey Katsubo Date: Mon, 29 Sep 2025 00:08:04 +0300 Subject: [PATCH 1/3] Fix slow SQL query in checkAlbumAccess caused by the array overlap operator && --- server/src/repositories/access.repository.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/server/src/repositories/access.repository.ts b/server/src/repositories/access.repository.ts index 5cceb6dbe0b4f..9439ab1afe829 100644 --- a/server/src/repositories/access.repository.ts +++ b/server/src/repositories/access.repository.ts @@ -144,11 +144,7 @@ class AssetAccess { .leftJoin('album_user as albumUsers', 'albumUsers.albumsId', 'album.id') .leftJoin('user', (join) => join.onRef('user.id', '=', 'albumUsers.usersId').on('user.deletedAt', 'is', null)) .select(['asset.id', 'asset.livePhotoVideoId']) - .where( - sql`array["asset"."id", "asset"."livePhotoVideoId"]`, - '&&', - sql`array[${sql.join([...assetIds])}]::uuid[] `, - ) + .where((eb) => eb.or([eb('asset.id', 'in', [...assetIds]), eb('asset.livePhotoVideoId', 'in', [...assetIds])])) .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('user.id', '=', userId)])) .where('album.deletedAt', 'is', null) .execute() From bea95ea6ff2fbd8a671121a37bfa7ad83822595c Mon Sep 17 00:00:00 2001 From: Sergey Katsubo Date: Mon, 29 Sep 2025 01:59:12 +0300 Subject: [PATCH 2/3] Update access.repository.sql --- server/src/queries/access.repository.sql | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/server/src/queries/access.repository.sql b/server/src/queries/access.repository.sql index 9aecaafb52712..a49177f5b4e5a 100644 --- a/server/src/queries/access.repository.sql +++ b/server/src/queries/access.repository.sql @@ -83,10 +83,13 @@ from left join "user" on "user"."id" = "albumUsers"."usersId" and "user"."deletedAt" is null where - array["asset"."id", "asset"."livePhotoVideoId"] && array[$1]::uuid[] + ( + "asset"."id" in ($1) + or "asset"."livePhotoVideoId" in ($2) + ) and ( - "album"."ownerId" = $2 - or "user"."id" = $3 + "album"."ownerId" = $3 + or "user"."id" = $4 ) and "album"."deletedAt" is null From c3a70a19ee7ff790b6f1ae22455ebe75d7ba192e Mon Sep 17 00:00:00 2001 From: Sergey Katsubo Date: Thu, 2 Oct 2025 01:16:08 +0300 Subject: [PATCH 3/3] Rewrite the query to pass assetIds once as a single array parameter --- server/src/queries/access.repository.sql | 14 ++++++++++---- server/src/repositories/access.repository.ts | 9 ++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/server/src/queries/access.repository.sql b/server/src/queries/access.repository.sql index a49177f5b4e5a..e98c5c6d98f95 100644 --- a/server/src/queries/access.repository.sql +++ b/server/src/queries/access.repository.sql @@ -71,6 +71,11 @@ where and "shared_link"."albumId" in ($2) -- AccessRepository.asset.checkAlbumAccess +with + "target" as ( + select + array[$1]::uuid[] as "ids" + ) select "asset"."id", "asset"."livePhotoVideoId" @@ -82,14 +87,15 @@ from left join "album_user" as "albumUsers" on "albumUsers"."albumsId" = "album"."id" left join "user" on "user"."id" = "albumUsers"."usersId" and "user"."deletedAt" is null + cross join "target" where ( - "asset"."id" in ($1) - or "asset"."livePhotoVideoId" in ($2) + "asset"."id" = any (target.ids) + or "asset"."livePhotoVideoId" = any (target.ids) ) and ( - "album"."ownerId" = $3 - or "user"."id" = $4 + "album"."ownerId" = $2 + or "user"."id" = $3 ) and "album"."deletedAt" is null diff --git a/server/src/repositories/access.repository.ts b/server/src/repositories/access.repository.ts index 9439ab1afe829..ca12ff040b60c 100644 --- a/server/src/repositories/access.repository.ts +++ b/server/src/repositories/access.repository.ts @@ -136,6 +136,7 @@ class AssetAccess { } return this.db + .with('target', (qb) => qb.selectNoFrom(sql`array[${sql.join([...assetIds])}]::uuid[]`.as('ids'))) .selectFrom('album') .innerJoin('album_asset as albumAssets', 'album.id', 'albumAssets.albumsId') .innerJoin('asset', (join) => @@ -143,8 +144,14 @@ class AssetAccess { ) .leftJoin('album_user as albumUsers', 'albumUsers.albumsId', 'album.id') .leftJoin('user', (join) => join.onRef('user.id', '=', 'albumUsers.usersId').on('user.deletedAt', 'is', null)) + .crossJoin('target') .select(['asset.id', 'asset.livePhotoVideoId']) - .where((eb) => eb.or([eb('asset.id', 'in', [...assetIds]), eb('asset.livePhotoVideoId', 'in', [...assetIds])])) + .where((eb) => + eb.or([ + eb('asset.id', '=', sql`any(target.ids)`), + eb('asset.livePhotoVideoId', '=', sql`any(target.ids)`), + ]), + ) .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('user.id', '=', userId)])) .where('album.deletedAt', 'is', null) .execute()