Fix: dedupe deferred wcpay_track_order scheduling before ActionScheduler init (WOOPMNT-3789)#11609
Open
ChrissiePollock wants to merge 1 commit intodevelopfrom
Open
Conversation
…ler init schedule_job() previously registered a new anonymous closure on action_scheduler_init every time it was called before ActionScheduler had initialized. When woocommerce_update_order fires multiple times during a single order-creation request, each call queued its own closure with its own captured timestamp. Because the timestamps differ by fractions of a second, schedule_action_and_prevent_duplicates() cannot reliably collapse them, so several wcpay_track_new_order actions end up scheduled per order. On one site this produced ~350k scheduled actions for ~1300 orders and caused Action Scheduler table deadlocks. Track deferred jobs in $deferred_jobs keyed by hook+args+group so only one action_scheduler_init callback is registered per unique combination. Repeat calls just update the stored timestamp; the single callback reads the latest value when ActionScheduler initializes, and schedule_action_and_prevent_duplicates() remains as a second safety net. Closes WOOPMNT-3789
Contributor
Test the buildOption 1. Jetpack Beta
Option 2. Jurassic Ninja - available for logged-in A12s🚀 Launch a JN site with this branch 🚀 ℹ️ Install this Tampermonkey script to get more options. Build info:
Note: the build is updated when a new commit is pushed to this PR. |
Contributor
|
Size Change: 0 B Total Size: 963 kB ℹ️ View Unchanged
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #8219
Linear ticket: WOOPMNT-3789
Changes proposed in this Pull Request
WC_Payments_Action_Scheduler_Service::schedule_job()previously registered a new anonymous closure onaction_scheduler_initevery time it was called while ActionScheduler was still uninitialized.schedule_order_tracking()is hooked towoocommerce_update_order, which fires multiple times during a single order-creation request — so each call queued its own closure, each with its own captured$timestamp(fromstrtotime('+5 seconds')at slightly different moments).When
action_scheduler_initthen fires, every queued closure executes. Because the timestamps differ by fractions of a second,schedule_action_and_prevent_duplicates()cannot reliably collapse them: one closure unschedules and reschedules, then the next closure does the same thing with its own timestamp, and so on. The_new_order_tracking_completemeta guard doesn't help either, because it's set only after the tracking action runs, not when it's scheduled.Result in the wild: ~350k
wcpay_track_new_orderactions scheduled across ~1300 orders, causing Action Scheduler table deadlocks and driving the DB server offline. A third-party plugin (LearnPress - WooCommerce Payment Methods Integration) was also observed looping throughwoocommerce_update_order, amplifying the issue.This PR adds a
$deferred_jobsproperty keyed bymd5( hook + args + group ):action_scheduler_initcallback.$deferred_jobs[$key]with the latest$timestamp.action_scheduler_initfires, the single callback reads the latest stored timestamp and hands it off toschedule_action_and_prevent_duplicates().Net effect: at most one scheduled action per unique hook+args+group per request, regardless of how many times
schedule_job()was called before ActionScheduler initialized.schedule_action_and_prevent_duplicates()is kept as a second safety net for the post-init path.Minimal change — no new files, no architectural changes, just deduplication of the deferred closures.
Questions for PR author:
schedule_action_and_prevent_duplicates()unschedules and reschedules, so this preserves existing semantics while avoiding the intermediate churn.$keyuses the same inputs thatschedule_action_and_prevent_duplicates()uses to identify duplicates, so dedup logic is consistent across the pre-init and post-init paths.Testing instructions
mu-pluginssnippet) that callsdo_action( 'woocommerce_update_order', $order_id )several times during a single request — e.g. fire it 3× inside awoocommerce_new_orderhook.wcpay_track_new_order._new_order_tracking_completeis set.wcpay_track_update_orderaction.action_scheduler_inithook), the else-branch still runsschedule_action_and_prevent_duplicates()directly.npm run changelogto add a changelog file, choosepatchto leave it empty if the change is not significant. You can add multiple changelog files in one PR by running this command a few times.Post merge