Skip to content

Optimize get_claim_count() query for large stores#1313

Merged
mcliwanow merged 2 commits intotrunkfrom
update/as-claims-query-performance-large-stores
Mar 18, 2026
Merged

Optimize get_claim_count() query for large stores#1313
mcliwanow merged 2 commits intotrunkfrom
update/as-claims-query-performance-large-stores

Conversation

@mcliwanow
Copy link
Contributor

@mcliwanow mcliwanow commented Mar 12, 2026

Description

Optimizes get_claim_count() in ActionScheduler_DBStore for stores with large actionscheduler_actions tables.

The original query scans the (potentially huge) actions table:

SELECT COUNT(DISTINCT claim_id)
FROM wp_actionscheduler_actions
WHERE claim_id != 0 AND status IN ('pending', 'in-progress')

On a store with ~3M action rows, this took ~28 seconds to return 0.

The new query starts from the small actionscheduler_claims table and uses EXISTS to check the actions table:

SELECT COUNT(*)
FROM wp_actionscheduler_claims c
WHERE EXISTS (
   SELECT 1 FROM wp_actionscheduler_actions a
   WHERE a.claim_id = c.claim_id
   AND a.status IN ('pending', 'in-progress')
)

This returns in ~0.078 seconds (350x faster), because the claims table only holds active claims (typically a handful of rows), and EXISTS stops scanning at the first match.

If the claims-table query fails (e.g. the table doesn't exist yet during migration), the method falls back to the original query shape and logs the error.

Ported from WCCOM

Test instructions

Setup

Run the unit tests to verify correctness:

./vendor/bin/phpunit tests/phpunit/jobstore/ActionScheduler_DBStore_Test.php \ -c tests/phpunit.xml.dist --filter test_get_claim_count

All 7 new test cases should pass (runs as 42 with timezone rotation).

Manual verification

Prerequisites: A local WooCommerce site. Copy the updated ActionScheduler_DBStore.php into wp-content/plugins/woocommerce/packages/action-scheduler/classes/data-stores/.

  1. Add this to wp-config.php to log queries:
define( 'SAVEQUERIES', true );
  1. Then add this temporary mu-plugin to wp-content/mu-plugins/log-claim-count.php:
<?php
add_action( 'my_test_hook', function( $i ) {
    global $wpdb;

    $store = ActionScheduler::store();
    $count = $store->get_claim_count();

    $claim_query = null;
    foreach ( $wpdb->queries as $q ) {
        if ( str_contains( $q[0], 'actionscheduler_claims' ) && str_contains( $q[0], 'COUNT' ) ) {
            $claim_query = $q;
        }
    }

    error_log( sprintf(
        'During action %d: get_claim_count() = %d | query time: %fs',
        $i,
        $count,
        $claim_query[1] ?? 0
    ) );
} );

 add_action( 'action_scheduler_after_process_queue', function() {
    $store = ActionScheduler::store();
    error_log( sprintf( 'After processing: get_claim_count() = %d', $store->get_claim_count() ) );
} );
  1. Test actions
  • Schedule test actions via WP-CLI or a snippet:
 for ( $i = 0; $i < 5; $i++ ) { as_enqueue_async_action( 'my_test_hook', array( $i ) ); }
  • Trigger processing — the log should show a non-zero count while actions run, then 0 after completion
image - next processing should just show claims with 0
  1. Cleanup
  • Remove the mu-plugin
  • Remove SAVEQUERIES from wp-config.php

Query the small claims table with EXISTS instead of scanning millions of
rows in the actions table. Falls back to the original query if the
claims-table path fails. Adds tests for the optimized method.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@mcliwanow mcliwanow self-assigned this Mar 12, 2026
@mcliwanow mcliwanow requested review from bor0 and jorgeatorres March 12, 2026 10:55
@mcliwanow mcliwanow marked this pull request as ready for review March 12, 2026 10:55
Copy link
Member

@bor0 bor0 left a comment

Choose a reason for hiding this comment

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

Approving based on previous WCCOM review and neat results 🎉

@mcliwanow
Copy link
Contributor Author

Hey @jorgeatorres 👋 Please let me know if you're the best person to review this PR. I'd appreciate someone else taking a look at it but I'm also happy to reassign review to someone else. Thanks!

Copy link
Member

@jorgeatorres jorgeatorres left a comment

Choose a reason for hiding this comment

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

@mcliwanow: thanks for the PR! This looks good to me and the improvements are quite nice. I left some feedback about the fallback query so we can discuss, but that's pretty much it. Thanks again!

This path wouldn't be reached anyway, it was overly defensive coding
@mcliwanow mcliwanow requested a review from jorgeatorres March 17, 2026 11:44
Copy link
Member

@jorgeatorres jorgeatorres left a comment

Choose a reason for hiding this comment

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

LGTM. Ran a few tests with concurrent batches and also with actions marked completed without releasing the claim to confirm that the get_claim_count() matched the expected values. Thanks for the PR @mcliwanow! 🥇


Copy the updated ActionScheduler_DBStore.php into wp content/plugins/woocommerce/packages/action-scheduler/classes/data-stores/ [...]

💡 For future reference, you can always clone this repo and add it to your test site as a normal WP plugin. While enabled, it'll override the version of A-S bundled in WC, so no need to modify WC itself (and no need to even have WC installed).

@mcliwanow
Copy link
Contributor Author

it'll override the version of A-S bundled in WC

Oh nice. That's good to know. Thanks!

@mcliwanow mcliwanow merged commit 08d69ea into trunk Mar 18, 2026
104 checks passed
@mcliwanow mcliwanow deleted the update/as-claims-query-performance-large-stores branch March 18, 2026 09:46
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.

3 participants