Skip to content

Add index to actionscheduler_actions table to support prioritized claims #1250

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from

Conversation

prettyboymp
Copy link
Contributor

Fixes #1104

This adds an index on wp_actionscheduler_actions that is able to support the query used to claim actions when running the queue. The current default query runs as follows:

UPDATE wp_actionscheduler_actions SET claim_id=XXX, last_attempt_gmt='XXXX-XX-XX XX:XX:XX', last_attempt_local='XXXX-XX-XX XX:XX:XX' WHERE claim_id = 0 AND scheduled_date_gmt <= 'XXXX-XX-XX XX:XX:XX' AND status='pending' ORDER BY priority ASC, attempts ASC, scheduled_date_gmt ASC, action_id ASC LIMIT XX 

However, there is no query to support the ordering of these actions causing the table to lock for longer periods of time.

#921 Added priorities to scheduled actions and set the default order by clause to include the priority of actions when being claimed, 'ORDER BY priority ASC, attempts ASC, scheduled_date_gmt ASC, action_id ASC'. Though, I believe the locking was already an issue before that because there was no support for the attempts column.

The new claim_id_status_priority_attempts_scheduled_date_gmt index replaces the previous claim_id_status_scheduled_date_gmt index. I didn't come across any usage where the latter was needed. Please help me verify.

Below is the analysis of both, the first using the new index while the second is the previous:

mysql> EXPLAIN ANALYZE select action_id from wp_actionscheduler_actions WHERE claim_id = 0 AND scheduled_date_gmt <= '2025-04-17 16:21:20' AND status='pending' ORDER BY priority ASC, attempts ASC, scheduled_date_gmt ASC, action_id ASC LIMIT 2\G
*************************** 1. row ***************************
EXPLAIN: -> Limit: 2 row(s)  (cost=119 rows=2) (actual time=0.121..0.136 rows=2 loops=1)
    -> Filter: ((wp_actionscheduler_actions.claim_id = 0) and (wp_actionscheduler_actions.scheduled_date_gmt <= TIMESTAMP'2025-04-17 16:21:20') and (wp_actionscheduler_actions.`status` = 'pending'))  (cost=119 rows=439) (actual time=0.117..0.131 rows=2 loops=1)
        -> Covering index skip scan on wp_actionscheduler_actions using claim_id_status_priority_attempts_scheduled_date_gmt over claim_id = 0, status = 'pending', NULL < scheduled_date_gmt <= '2025-04-17 16:21:20'  (cost=119 rows=439) (actual time=0.103..0.117 rows=2 loops=1)

1 row in set (0.00 sec)

mysql> EXPLAIN ANALYZE select action_id from wp_actionscheduler_actions USE INDEX (claim_id_status_scheduled_date_gmt) WHERE claim_id = 0 AND scheduled_date_gmt <= '2025-04-17 16:21:20' AND status='pending' ORDER BY priority ASC, attempts ASC, scheduled_date_gmt ASC, action_id ASC LIMIT 2\G
*************************** 1. row ***************************
EXPLAIN: -> Limit: 2 row(s)  (cost=1544 rows=2) (actual time=1.88..1.88 rows=2 loops=1)
    -> Sort: wp_actionscheduler_actions.priority, wp_actionscheduler_actions.attempts, wp_actionscheduler_actions.scheduled_date_gmt, wp_actionscheduler_actions.action_id, limit input to 2 row(s) per chunk  (cost=1544 rows=1320) (actual time=1.88..1.88 rows=2 loops=1)
        -> Index range scan on wp_actionscheduler_actions using claim_id_status_scheduled_date_gmt over (claim_id = 0 AND status = 'pending' AND NULL < scheduled_date_gmt <= '2025-04-17 16:21:20'), with index condition: ((wp_actionscheduler_actions.claim_id = 0) and (wp_actionscheduler_actions.scheduled_date_gmt <= TIMESTAMP'2025-04-17 16:21:20') and (wp_actionscheduler_actions.`status` = 'pending'))  (cost=1544 rows=1320) (actual time=0.173..1.62 rows=1320 loops=1)

1 row in set (0.00 sec)

Note, that DBDelta will not automatically delete the previous index from existing installs. This will prevent the index from being removed if any sites have already added filters to the order by clause to address the performance issue.

@prettyboymp prettyboymp marked this pull request as ready for review March 20, 2025 19:33
@prettyboymp
Copy link
Contributor Author

I spent some more time testing this today and while the current index made improvements, I'm still seeing locks occur that prevent other updates from being able to complete, e.g. ::log_execution() and unclaim_actions(). The latter was improved by the change in #1248.

Under heavy load, I have only been able to get the deadlocks from occurring is by completely getting rid of the order by clause in the claims query.

One option I tried was to:

  • drop the order by completely.
  • query for existing priorities that are due and then loop through priorities one by one to claim actions until we've reached the limit.

Initial testing showed this got rid of the deadlocks, but also appeared to increase CPU usage on MySQL. I need to validate whether the CPU usage was truly caused by this change or just happened to be that the actions my test site started processing happened to be more process intensive subscription renewals vs the pseudo actions I registered for the purpose testing the queue processing.

@prettyboymp
Copy link
Contributor Author

Abandoning this in favor or one of the options in #1251 as they both showed more promising results.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Slow claim actions update query causing lock wait timeouts
1 participant