If your WooCommerce store has gotten slower over the past few months — wp-admin drags, checkout lags, and your host keeps flagging high CPU — the culprit isn't always your theme or a rogue plugin. Very often it's a bloated Action Scheduler queue quietly grinding your database to a halt.
What Action Scheduler Actually Does
Action Scheduler is a background job library bundled with WooCommerce since version 3.0. It's what runs things like order emails, subscription renewals, stock sync, webhook deliveries, and abandoned-cart follow-ups without making the customer wait for them in real time. Plugins beyond WooCommerce use it too — Jetpack, WP All Import, and most subscription or CRM-sync plugins queue jobs through the same system.
Every job lives as a row in one of four custom tables: wp_actionscheduler_actions, wp_actionscheduler_logs, wp_actionscheduler_groups, and wp_actionscheduler_claims. Under normal conditions these stay small because completed actions get purged automatically after 30 days. When that cleanup stops happening, the tables just keep growing.
Symptom: How to Tell This Is Your Problem
- wp-admin is slow everywhere, not just on WooCommerce screens
- WooCommerce > Status > Scheduled Actions shows thousands of actions stuck as "pending" or "failed"
- Your host's MySQL slow-query log is full of queries against
wp_actionscheduler_actions - On shared cPanel hosting, you're hitting LVE resource limits (CPU/entry process faults) even though traffic hasn't grown
- Performance briefly improves after a server restart, then degrades again over hours
Confirm It From the Database
Open phpMyAdmin (or connect via SSH) and run:
SELECT status, COUNT(*) AS total
FROM wp_actionscheduler_actions
GROUP BY status
ORDER BY total DESC;A few hundred "pending" rows is normal on an active store. Tens of thousands — especially "failed" or "pending" rows dated weeks back — means the queue has stopped draining.
Cause: Why the Queue Piles Up
1. WP-Cron Isn't Actually Firing
WordPress's built-in cron isn't a real cron job — it fires on page visits. Low-traffic stores, or sites where DISABLE_WP_CRON was set to true without a real replacement, simply never trigger the runner. Actions queue up and nothing processes them.
2. A Plugin Is Scheduling Faster Than It Can Process
Subscription renewals, abandoned-cart emails, or a webhook integration that retries a failing API call can enqueue new actions faster than the runner clears old ones — especially if that third-party API has been down for days and nobody noticed.
3. Failed Actions Retry Indefinitely
When a scheduled action throws an error (a payment gateway timeout, an expired API key), Action Scheduler retries with backoff but keeps the failed log entries. Over months, a single flaky integration can leave tens of thousands of dead rows behind.
4. The Cleanup Job Itself Is Stuck
Action Scheduler purges old completed actions using — ironically — a scheduled action of its own. If the runner has already stalled, cleanup never fires either, and the tables just keep growing unchecked.
Fix: Clear the Queue and Get It Moving Again
Step 1 — Get WP-CLI Access
Use SSH, or the Terminal app in cPanel if SSH isn't enabled on your plan. Confirm WooCommerce's CLI commands are available:
wp action-scheduler action list --status=pending --per-page=0 --field=action_id | wc -l
wp action-scheduler action list --status=failed --per-page=0 --field=action_id | wc -lStep 2 — Find Which Hook Is Actually Responsible
Don't clear the queue blind — find out what's flooding it first, or you'll be back here in a month:
SELECT hook, COUNT(*) AS total
FROM wp_actionscheduler_actions
WHERE status = 'failed'
GROUP BY hook
ORDER BY total DESC
LIMIT 10;This almost always points straight at the offending plugin or integration.
Step 3 — Clear the Backlog
For a few hundred stuck actions, the UI is fine: go to WooCommerce > Status > Scheduled Actions, filter by status, and bulk-cancel. For tens of thousands of rows, the admin screen will time out — use WP-CLI instead:
wp action-scheduler action delete --status=failed --hook=<offending_hook_name>
wp action-scheduler action delete --status=pending --hook=<offending_hook_name>Take a database backup first. If a plugin turns out to be genuinely broken, deleting its queued retries is safe — it isn't going to succeed on attempt 40,000 either.
Step 4 — Fix Why the Runner Stalled
Stop relying on visit-triggered pseudo-cron. Add this to wp-config.php:
define( 'DISABLE_WP_CRON', true );Then add a real system cron job in cPanel's Cron Jobs tool to run every 5 minutes:
*/5 * * * * wget -q -O - "https://yourdomain.com/wp-cron.php?doing_wp_cron" >/dev/null 2>&1If WP-CLI is available on your plan, this is lighter on resources than a wget hit:
*/5 * * * * cd /home/username/public_html && wp cron event run --due-now --quietStep 5 — Raise the Batch Size for High-Volume Stores
If your store genuinely processes thousands of orders and the default runner can't keep up, increase concurrency in functions.php:
add_filter( 'action_scheduler_queue_runner_batch_size', function() {
return 50; // default is 25
});
add_filter( 'action_scheduler_queue_runner_concurrent_batches', function() {
return 3; // default is 1
});Only raise this on a VPS with headroom — on shared hosting it just moves the CPU spike somewhere else.
Prevention: Keep the Queue From Bloating Again
| Practice | Why It Matters |
|---|---|
| Check Scheduled Actions weekly | Catches a runaway integration before it reaches 50,000 rows |
| Audit new plugins/integrations on install | Payment, shipping, and CRM-sync plugins are the most common offenders |
| Alert on webhook/API failures fast | A silently-dead third-party API is the #1 cause of failed-action pileups |
Run OPTIMIZE TABLE after a big cleanup | InnoDB doesn't reclaim disk space from deletes on its own |
| Keep WooCommerce updated | Action Scheduler's own performance has improved significantly since the 3.x rewrite |
OPTIMIZE TABLE wp_actionscheduler_actions, wp_actionscheduler_logs;If you're on Getwebup's managed cPanel hosting and aren't sure whether SSH or WP-CLI is enabled on your plan, our support team can run this cleanup for you directly — just open a ticket with your domain name.