From 546581036fed585f10d5dc2af9567d73ca70a506 Mon Sep 17 00:00:00 2001 From: Wes Rasada Date: Tue, 18 Aug 2026 15:54:55 -0700 Subject: [PATCH] Add index for action/instance lookups on the events table Lookups by action + instance can't use ts_action_instance_status, since that key leads with timestamp and those queries don't filter on it. They fall back to scanning the whole table, which shows up on any site with a large pending queue. Adds KEY action_instance_status_ts (action(191), instance, status, timestamp), bumps DB_VERSION, and adds a version check so existing installs pick up the new index. DB_VERSION was previously written on install but never read back, so a schema change alone would only have reached new installs. Co-Authored-By: Claude Opus 5 (1M context) --- includes/class-events-store.php | 38 +++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/includes/class-events-store.php b/includes/class-events-store.php index 90a9695..a447bc2 100644 --- a/includes/class-events-store.php +++ b/includes/class-events-store.php @@ -10,7 +10,7 @@ class Events_Store extends Singleton { const TABLE_SUFFIX = 'a8c_cron_control_jobs'; - const DB_VERSION = 1; + const DB_VERSION = 2; const DB_VERSION_OPTION = 'a8c_cron_control_db_version'; const STATUS_PENDING = 'pending'; @@ -26,6 +26,9 @@ protected function class_init() { // Keep trying in case we weren't around during the site installation. add_action( 'shutdown', array( $this, 'maybe_install_during_shutdown' ) ); + } elseif ( self::needs_upgrade() ) { + // Table exists but the schema is behind, bring it up to date. + add_action( 'shutdown', array( $this, 'maybe_upgrade_during_shutdown' ) ); } // Handle adding/removing tables when subsites are created/deleted. @@ -109,6 +112,36 @@ public function maybe_install_during_shutdown() { } } + /** + * Check if the installed schema is behind the current version. + */ + public static function needs_upgrade(): bool { + return (int) get_option( self::DB_VERSION_OPTION, 0 ) < self::DB_VERSION; + } + + /** + * For certain requests, bring an out of date schema up to date on shutdown. + */ + public function maybe_upgrade_during_shutdown() { + $is_cron_or_cli = wp_doing_cron() || ( defined( 'WP_CLI' ) && WP_CLI ); + $is_admin = is_admin() && ! wp_doing_ajax(); + + if ( ! $is_cron_or_cli && ! $is_admin ) { + // Not a request we should try to upgrade on. + return; + } + + if ( ! self::needs_upgrade() ) { + // Must have been upgraded earlier on in this request already. + return; + } + + if ( wp_cache_add( 'upgrade_lock', true, 'cron-control', \MINUTE_IN_SECONDS ) ) { + // We've claimed the lock, run the upgrade. dbDelta() adds any missing indexes. + $this->_prepare_table(); + } + } + /** * Create the plugin's DB table when necessary */ @@ -141,7 +174,8 @@ protected function _prepare_table() { PRIMARY KEY (`ID`), UNIQUE KEY `ts_action_instance_status` (`timestamp`, `action` (191), `instance`, `status`), - KEY `status` (`status`) + KEY `status` (`status`), + KEY `action_instance_status_ts` (`action` (191), `instance`, `status`, `timestamp`) ) ENGINE=InnoDB;\n"; dbDelta( $schema, true );