Skip to content

fix(mobile): run iOS bg task phases in parallel#28293

Open
santoshakil wants to merge 1 commit into
mainfrom
fix/mobile-ios-bg-task-parallel-phases
Open

fix(mobile): run iOS bg task phases in parallel#28293
santoshakil wants to merge 1 commit into
mainfrom
fix/mobile-ios-bg-task-parallel-phases

Conversation

@santoshakil
Copy link
Copy Markdown
Collaborator

Description

onIosUpload runs sync local → sync remote → hash → handle backup sequentially. on the bg refresh task path that's a 20s budget from iOS, and sync + hash usually eat all of it before backup gets a turn to enqueue any candidates. so the fire ends with setTaskCompleted(success: false) and zero new uploads.

the four phases don't actually depend on each other. local + remote sync touch different tables. hash works off whatever's already in drift. handle backup reads candidates from drift and just enqueues to URLSession bg. anything one phase produces in this fire shows up to the others on the next fire, and server-side dedup catches the rare race where backup enqueues something sync remote was about to mark as already-uploaded.

so this runs all four concurrently via Future.wait, with hash getting the full maxSeconds-1 budget instead of a fixed 5s. outer budget timeout still caps everything before iOS expires.

second small change: getAssetsToHash orders by createdAt DESC instead of id ASC to match getCandidates. when hash runs inside a refresh fire it processes recent photos first, so the next fire's queue gets what the user just took rather than random old assets.

tested over ~3 hours on iphone 15 pro with iOS 26.5 against a fresh server. 12 of 14 refresh fires reached the 20s budget cleanly, vs essentially zero useful enqueues before. server side: 330 new asset rows created from bg-only activity in those 3 hours, phone wasn't in foreground.

Please describe to which degree, if any, an LLM was used in creating this pull request.

None.

onIosUpload runs sync local, sync remote, hash and handle backup
sequentially. on the bg refresh task path that's a 20s budget from
iOS, and sync + hash usually eat all of it before backup gets a turn
to enqueue any candidates.

these phases don't actually depend on each other. local + remote sync
touch different tables. hash works off whatever's already in drift.
handle backup reads candidates and just enqueues to URLSession bg.
anything one phase produces in this fire shows up to the others on
the next fire, and server-side dedup catches the rare race where
backup enqueues something sync remote was about to mark as already
uploaded.

so this runs all four concurrently via Future.wait, with hash getting
the full maxSeconds-1 budget instead of a fixed 5s. outer budget
timeout still caps everything before iOS expires.

second small change: getAssetsToHash orders by createdAt DESC instead
of id ASC to match getCandidates. when hash runs inside a refresh
fire it processes recent photos first.
Copilot AI review requested due to automatic review settings May 8, 2026 10:42
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the mobile background upload flow on iOS to better utilize the limited BGAppRefresh execution window by running formerly sequential “phases” concurrently, and tweaks hashing order to prioritize recent assets.

Changes:

  • Run iOS background task phases (sync local, sync remote, hashing, backup enqueue) in parallel via Future.wait, with budget-based timeouts.
  • Increase refresh-mode hash time budget to roughly the full iOS refresh window instead of a fixed 5s.
  • Change getAssetsToHash ordering to createdAt DESC to process newer assets first (aligning with backup candidate ordering).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
mobile/lib/infrastructure/repositories/local_album.repository.dart Hash-queue query now orders un-hashed album assets by createdAt DESC.
mobile/lib/domain/services/background_worker.service.dart iOS background upload now runs sync/hash/backup concurrently under a shared time budget.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 238 to 246
Future<List<LocalAsset>> getAssetsToHash(String albumId) {
final query =
_db.localAlbumAssetEntity.select().join([
innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)),
])
..where(_db.localAlbumAssetEntity.albumId.equals(albumId) & _db.localAssetEntity.checksum.isNull())
..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]);
..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]);

return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get();
@alextran1502
Copy link
Copy Markdown
Member

IIRC, we switched to this specific synchronous mechanism because when it was running in parallel, the database locked up very often in the background and caused the foreground to also lock up. Am I remembering this correctly?

We will need to spend sometimes running this PR to make sure it doesn't happen

@shenlong-tanwen
Copy link
Copy Markdown
Member

IIRC, we switched to this specific synchronous mechanism because when it was running in parallel, the database locked up very often in the background and caused the foreground to also lock up. Am I remembering this correctly?

That is how I remember it as well, yes. But we've bumped our dependencies and things have changed a lot so it might not be bad idea to revisit this now

@santoshakil
Copy link
Copy Markdown
Collaborator Author

4 days running on my iphone, last 2 days i didnt even open the app. around 7k photos in the server from 180 fires. no fg wedge when i do open it.

phases were already in their own isolates via runInIsolateGentle so sequential was just awaiting them one at a time. drift has WAL + 30s busy_timeout + shareAcrossIsolates set. ill keep it running. I think this should not cause any issue. let's see more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants