Skip to content

Conversation

@staskus
Copy link
Contributor

@staskus staskus commented Oct 14, 2025

WOOMOB-1095
Continuation of #16205

Description

The goal of this PR is to implement the logic of the dispatching system background tasks to make sure:

  • Existing order and dashboard sync is run
  • Incremental catalog sync is done once an hour
  • Full sync is done once an hour

Not in scope:

  • Additional criteria of running tasks, such as when the POS was last open, POS eligibility, etc...
  • Foreground task running

Exploration

I began my explorations by looking for a universal way of scheduling foreground and background tasks for POS Catalog (and OrderDashboard sync) with clever prioritization. I've done such a solution locally, but it felt too large and complicated with no clear benefits. Theoretically, we could have one scheduler that:

  • Defines tasks separately with their priorities, execution logic, and persists the ideal and last run times
  • Calculates priority based on last run time, failures, etc
  • Calculates the same next run time when switching between background and foreground
  • etc..

However, given BGAppRefreshTask already doesn't guarantee when and if it's going to run, the precision is not that important. Therefore, I decided to keep the existing BackgroundTaskRefreshDispatcher and implement a foreground runner to run separately, independent of BackgroundTaskRefreshDispatcher and specifically for POS Catalog. I will share that in another PR.

Solution

  • Merge posCatalogFullSync and posCatalogIncrementalSync tasks created in [Local Catalog] Refactor background task dispatcher to support POS catalog sync #16205 into a single posCatalogSync task that runs every hour. The motivation is that the system already doesn't guarantee if and when this is going to run. Therefore, posCatalogSync can check if a full sync is available right before running a task, otherwise, run incremental sync.
  • Created lightweight BackgroundTaskSchedule to implement a couple of simple methods to pick the next task based on the run period and the last schedule date. Asked AI to generate comprehensive test cases based on my scenarios.
  • Using BackgroundTaskSchedule within BackgroundTaskRefreshDispatcher

Steps to reproduce

Prerequisites:

  • Before testing, go to BackgroundTaskSchedule and make periods shorter, e.g 30 and 60 seconds
  • Use a real device
  1. Launch the app with a debugger
  2. Background it
  3. Confirm Scheduled orderDashboardSync with time is logged
  4. Pause the debug (CMD+^+Y)
  5. Type lldb command e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.automattic.woocommerce.refresh"]
  6. Continue the debug (CMD+^+Y)
  7. Confirm you see logs of dashboard sync executed
  8. Confirm you see log of the next task scheduled. If the time has passed so the next posCatalogSync is sooner than the next order dashboard sync, you should see Scheduled posCatalogSync
  9. Pause the debug (CMD+^+Y)
  10. Type lldb command e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.automattic.woocommerce.refresh.pos.catalog.sync"]
  11. Continue the debug (CMD+^+Y)
  12. Confirm logs show incremental or full sync done
  13. Confirm the next task is scheduled

Testing information

Tested on iPad Air M2 device

Screenshots

Running session

testing.flow.tasks.mov

  • I have considered if this change warrants user-facing release notes and have added them to RELEASE-NOTES.txt if necessary.

Simplify posCatalogSync tasks by merging into one and then checking within the task whether full or incremental sync needs to be done.

Since we have no guarantee when the sync runs, it's more convenient to call a refresh task every hour and then decide whether we do incremental or full.
BackgroundTaskSchedule uses a simple logic to provide the next task to schedule.
- When app goes to background, sets the preferred refresh date based on the predefined periods (30 min for order sync and 60 for pos catalog sync)
- Provides the next task based on the earliest time
- The preferred time is only reset when app goes into background ensuring that when the app is longer in the background, eventually all tasks will get executed
@staskus staskus added this to the 23.5 milestone Oct 14, 2025
@staskus staskus added type: task An internally driven task. feature: POS labels Oct 14, 2025
@wpmobilebot
Copy link
Collaborator

wpmobilebot commented Oct 14, 2025

App Icon📲 You can test the changes from this Pull Request in WooCommerce iOS Prototype by scanning the QR code below to install the corresponding build.

App NameWooCommerce iOS Prototype
Build Numberpr16244-571f3cf
Version23.4
Bundle IDcom.automattic.alpha.woocommerce
Commit571f3cf
Installation URL6a7rau9s08dbo
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@iamgabrielma iamgabrielma self-assigned this Oct 15, 2025
@dangermattic
Copy link
Collaborator

1 Warning
⚠️ This PR is assigned to the milestone 23.5. This milestone is due in less than 2 days.
Please make sure to get it merged by then or assign it to a milestone with a later deadline.

Generated by 🚫 Danger

Copy link
Contributor

@iamgabrielma iamgabrielma left a comment

Choose a reason for hiding this comment

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

LGTM! 🚀

Sorry it took me a bit longer than usual, a bunch of stuff to unpack here.

Merge posCatalogFullSync and posCatalogIncrementalSync tasks

I don't have the full context of the project for this change, but it sounds like a good idea to me for these tasks, specially when we cannot guarantee precision and is handled by the system.

Minor suggestions for the logs, just from a debugging perspective when we need them:

  • We should add timezone for easier debugging later on, ie to add an UTC here
🔄 Starting incremental catalog sync for site ID: 215063064, modifiedAfter: 2025-10-16 03:33:35 +0000
  • This one was a bit confusing regarding what's happening with variations, but unrelated to this PR:
💾 Persisting incremental catalog with 0 products and 0 variations
✅ Incremental catalog persistence complete
Total after incremental update: 86 products, 47 product images, 21 product attributes, 106 variations, 95 variation images, 258 variation attributes
  • When there is no updates to the local catalog (ie "Persisted 0 products and 0 variations to database for siteID xyz") perhaps we should add the reason.

@Test func performSmartSync_performs_full_sync_when_last_full_sync_older_than_threshold() async throws {
// Given - last full sync was 25 hours ago (older than 24 hour threshold)
let twentyFiveHoursAgo = Date().addingTimeInterval(-25 * 60 * 60)
try createSiteInDatabase(siteID: sampleSiteID, lastFullSyncDate: twentyFiveHoursAgo)
Copy link
Contributor

Choose a reason for hiding this comment

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

TIL about PersistedSite, just for context this seems to be the table that holds each site's ID and associated catalog and sync details, in GRDB?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, Persisted prefix is used for GRDB entities. I'm not sure if that's great naming. We discussed that we may move all the GRDB stuff into a separate module, maybe we could ditch the prefixes.

///
func scheduleAppRefresh() {
scheduleTask(type: .ordersAndDashboardSync, earliestBeginDate: Date(timeIntervalSinceNow: 30 * 60))
BGTaskScheduler.shared.cancelAllTaskRequests()
Copy link
Contributor

Choose a reason for hiding this comment

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

Could "cancelling pending tasks" each time we enter in background, introduce any problem with either POS or the dashboard? ie: A long-planned task, let's say a couple of days, is never triggered since we background the app more often than that. My understanding that this is currently handled in setDefaultPreferredTaskDates(), so just raising it 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't foresee any problems. What I want to ensure is that we only end up scheduling a single thing we're intending to schedule. Right now the mechanism is made naive (setDefaultPreferredTaskDates). Maybe in the future we want to synchronize dates between both foreground and background sync, so that should be a source of truth.

@staskus
Copy link
Contributor Author

staskus commented Oct 16, 2025

Thanks for testing, @iamgabrielma!

Sorry it took me a bit longer than usual, a bunch of stuff to unpack here.

Understandable. It took me time to develop and test as well. One of the reasons I stayed with the current dispatcher class is that I know that it works. So I only integrated within it.

Merge posCatalogFullSync and posCatalogIncrementalSync tasks
I don't have the full context of the project for this change, but it sounds like a good idea to me for these tasks, specially when we cannot guarantee precision and is handled by the system.

I'll try to get feedback from @jaclync on that as well.

Minor suggestions for the logs, just from a debugging perspective when we need them:
We should add timezone for easier debugging later on, ie to add an UTC here

Good suggestion! I'll do it.

This one was a bit confusing regarding what's happening with variations, but unrelated to this PR:
💾 Persisting incremental catalog with 0 products and 0 variations
✅ Incremental catalog persistence complete

It means that there were 0 new products and 0 new variations. If you make a change in between the updates, this should reflect the number of new updates. However, I will update the logs to be clear about that.

@staskus staskus merged commit f5bca54 into trunk Oct 16, 2025
13 checks passed
@staskus staskus deleted the woomob-1095-woo-poslocal-catalog-bg-app-refresh-task-dispatching branch October 16, 2025 10:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature: POS type: task An internally driven task.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants