Action Scheduler 4.0.0: What Changes for Your WooCommerce Store
If your WooCommerce store is slowing down for no obvious reason and your database keeps swelling month after month, there’s a good chance Action Scheduler is partly responsible. It’s the component that runs background work: subscription payments, webhooks, emails and a host of other deferred tasks. Quiet, but its tables have quietly become some of the largest in many WooCommerce databases.
Version 4.0.0 is largely about fixing that. It’s now available on WordPress.org and will be bundled with WooCommerce 11.0, currently scheduled for July 28, 2026. Because some of the changes are deliberately backwards incompatible (breaking changes), now is a good time to understand what’s coming and test your site before updating.
What is Action Scheduler again?
Action Scheduler is a job queue for WordPress. Think of it as an extension of do_action() that can delay and repeat the execution of a hook. Each task is stored in dedicated tables (wp_actionscheduler_actions and wp_actionscheduler_logs), run at the right time, then marked complete, canceled or failed.
On an active store, that’s a huge amount of writes. According to the official docs, Action Scheduler processes millions of payments every month for WooCommerce Subscriptions, and has been seen clearing queues of more than 50,000 jobs at a sustained rate of over 10,000 per hour (source: 4.0.0 readme). Multiply that by months of history kept in the database, and you see why these tables grow. They aren’t the only tables that balloon like this: your WordPress autoloaded options often play the same quiet role in performance.
Why jump straight to 4.0.0?
You’ll notice the version goes from 3.9.3 to 4.0.0, with no 3.10. That’s expected: Action Scheduler follows WordPress-style versioning, where there is no 3.10 after 3.9. Rather than ship breaking changes under a routine-looking 3.9.4, the team bumped to 4.0.0 to flag them clearly. The version number is itself a warning.
Change #1: failed actions are finally purged
This is the most important fix for the health of your database.
Until now, Action Scheduler automatically cleaned up completed and canceled actions, but never failed ones. They piled up indefinitely, along with their logs. On a high-volume store, that meant tables that grew without bound and never recovered on their own.
From 4.0.0, failed actions are automatically removed once they are older than 3 months. That window isn’t arbitrary: it lines up with a typical quarterly accounting cycle and gives you a reasonable window to investigate failures, while still bounding table growth.
In practice, if you’ve never maintained these tables, expect a noticeable cleanup after updating. That’s good news, but if you need to keep failure history longer (audit, compliance), you can adjust or disable the behavior:
// Adjust the retention window for failed actions (in seconds).
add_filter(
'action_scheduler_retention_period_for_failed',
function () {
return 6 * MONTH_IN_SECONDS;
}
);
// Or restore the pre-4.0.0 behavior and never purge failed actions.
add_filter( 'action_scheduler_enable_failed_action_cleanup', '__return_false' );
Note: if you were already using the action_scheduler_default_cleaner_statuses filter to include failed actions in cleanup, your setting takes precedence and keeps applying as before.
Change #2: unique actions now consider their arguments
This one is subtler, but it can change how your code behaves if you (or one of your plugins) schedule unique tasks.
Functions like as_enqueue_async_action() and as_schedule_*_action() accept a $unique parameter. When set to true, Action Scheduler won’t create a new action if a matching pending or running one already exists.
The catch, before 4.0.0: “matching” meant the same hook and group, ignoring the arguments. Two tasks with the same hook but different arguments would block each other. The second was silently dropped, even though it may have related to entirely different work (a different order, a different customer…).
In 4.0.0, the uniqueness check includes the arguments. Only a genuinely identical action (same hook, same group and same arguments) now counts as a duplicate.
If any of your code relied on the old “hook + group” deduplication to prevent multiple executions, expect more actions to be created than before. Review that code carefully before updating: this is the change most likely to cause unexpected behavior (duplicate emails, duplicate syncs, etc.).
Change #3: cleanup becomes a dedicated daily job
Previously, deletion of old actions ran inline, in small slices, on every batch the queue processed. On high-volume stores, that cleanup often fell behind and never caught up with the accumulation.
In 4.0.0, cleanup becomes a task of its own that runs once a day at 3 a.m. (site time), deletes in larger batches (at least 250 per run) and keeps going until the backlog is clear. The cleanup is off the task-processing path, which finally lets it catch up, even on large tables.
The batch size remains customizable:
add_filter(
'action_scheduler_cleanup_batch_size',
function ( $size ) {
return 500;
}
);
This change isn’t strictly breaking, but it changes when cleanup happens. If you absolutely needed inline cleanup on every batch like before, you have to supply a custom queue cleaner: as soon as Action Scheduler detects a cleaner other than its built-in ActionScheduler_QueueCleaner, it reverts to the inline cycle. Most sites won’t need this.
Other useful improvements
The full changelog includes other worthwhile performance and robustness fixes:
- Corrupted actions auto-canceled: actions with corrupted data are automatically canceled on detection, and their excess logs cleaned up in batches. This prevents the queue from stalling and the log table from bloating.
- Optimized
get_claim_count()query for large stores. SKIP LOCKEDwhen claiming actions (since 3.9.3), reducing lock contention when several processes work the queue in parallel.- A fix for a fatal error related to the
WP_CLIclass, plus various display fixes in the scheduled-actions list.
On requirements: 4.0.0 now requires WordPress 6.8 or newer and is marked compatible up to WordPress 7.0.
Should you act, and how?
For the vast majority of stores, 4.0.0 is excellent news: less database bloat, cleanup that finally keeps pace, all without lifting a finger. That said, slimming these tables is no substitute for deeper WooCommerce optimization, any more than a cache plugin makes up for a heavy database. So here’s what I check before letting the update through on a client site:
First, look at the size of your Action Scheduler tables right now. This query gives you each table’s size and row count (adjust the wp_ prefix if yours differs). Run it from your usual SQL client or, more conveniently on a server, from the command line with WP-CLI:
SELECT
table_name AS 'Table',
table_rows AS 'Rows',
ROUND((data_length + index_length) / 1024 / 1024, 1) AS 'Size (MB)'
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name LIKE '%actionscheduler%'
ORDER BY (data_length + index_length) DESC;
And this one counts your actions by status, to spot a backlog of failures:
SELECT status, COUNT(*) AS total
FROM wp_actionscheduler_actions
GROUP BY status
ORDER BY total DESC;
If wp_actionscheduler_actions or wp_actionscheduler_logs weigh several hundred megabytes, or if the failed row runs into the tens of thousands, you have a backlog of unpurged failures. The first run of the new cleanup will do some housekeeping — which is desirable, but worth anticipating on a large database (back up first, and ideally run it outside peak hours).
Next, audit your code and plugins that use $unique = true. If hook-only deduplication was (intentionally or not) a safeguard against multiple executions, moving to argument-based deduplication can surface duplicates. This is the point that deserves the most attention.
Finally, test on a staging environment before updating production, especially if you use WooCommerce Subscriptions or extensions heavy on background tasks. And if you bundle Action Scheduler in your own extension, now is the time to test against 4.0.0 and report anything unexpected on GitHub.
Not sure how heavy your Action Scheduler tables are, or whether your code depends on the old behavior? Request a free audit — the state of the tables and background jobs is one of the first things I check on a WooCommerce store.
Need a WooCommerce store audit?
I'll send you a personalized report with the top priorities to improve. Free, no strings attached.