diff --git a/inc/Engine/Admin/RocketInsights/Queue/QueueRunner.php b/inc/Engine/Admin/RocketInsights/Queue/QueueRunner.php new file mode 100644 index 0000000000..7cdf6f51c9 --- /dev/null +++ b/inc/Engine/Admin/RocketInsights/Queue/QueueRunner.php @@ -0,0 +1,107 @@ + 30, // in seconds. + 'display' => __( 'Every thirty seconds', 'rocket' ), + ]; + + return $schedules; + } + + /** + * Get the cron hook name. + * + * @since 3.20 + * + * @return string + */ + protected function get_wp_cron_hook(): string { + return self::WP_CRON_HOOK; + } + + /** + * Get the cron schedule interval. + * + * @since 3.20 + * + * @return string + */ + protected function get_wp_cron_schedule(): string { + return self::WP_CRON_SCHEDULE; + } + + /** + * Get the queue group name. + * + * @since 3.20 + * + * @return string + */ + protected function get_group(): string { + return 'rocket-insights'; + } +} diff --git a/inc/Engine/Admin/RocketInsights/ServiceProvider.php b/inc/Engine/Admin/RocketInsights/ServiceProvider.php index 6b637ac260..80315c5ff7 100644 --- a/inc/Engine/Admin/RocketInsights/ServiceProvider.php +++ b/inc/Engine/Admin/RocketInsights/ServiceProvider.php @@ -15,6 +15,7 @@ Jobs\Manager as RIManager, Managers\Plan, Queue\Queue as RIQueue, + Queue\QueueRunner as RIQueueRunner, URLLimit\Subscriber as URLLimitSubscriber, Settings\Controller as SettingsController, Settings\Subscriber as SettingsSubscriber, @@ -40,6 +41,7 @@ class ServiceProvider extends AbstractServiceProvider { 'ri_manager', 'ri_factory', 'ri_queue', + 'ri_queue_runner', 'ri_processor', 'ri_render', 'ri_controller', @@ -154,6 +156,7 @@ public function register(): void { // Queue layer. $this->getContainer()->add( 'ri_queue', RIQueue::class ); + $this->getContainer()->addShared( 'ri_queue_runner', RIQueueRunner::class ); $this->getContainer()->add( 'ri_rest', Rest::class ) ->addArguments( [ @@ -180,9 +183,7 @@ public function register(): void { 'ri_manager', 'ri_plan', ] - ); - - // URL Limit subscriber. + ); // URL Limit subscriber. $this->getContainer()->addShared( 'ri_url_limit_subscriber', URLLimitSubscriber::class ) ->addArguments( [ diff --git a/inc/Engine/Admin/RocketInsights/Subscriber.php b/inc/Engine/Admin/RocketInsights/Subscriber.php index d598aaeda9..a24237b382 100644 --- a/inc/Engine/Admin/RocketInsights/Subscriber.php +++ b/inc/Engine/Admin/RocketInsights/Subscriber.php @@ -9,6 +9,7 @@ Managers\Plan, Jobs\Manager, Queue\Queue, + Queue\QueueRunner as RIQueueRunner, }; use WP_Rocket\Admin\Options_Data; use WP_Rocket\Event_Management\Subscriber_Interface; @@ -85,7 +86,6 @@ class Subscriber implements Subscriber_Interface, LoggerAwareInterface { * @var Plan */ private $plan; - /** * Constructor. * @@ -153,12 +153,14 @@ public static function get_subscribed_events(): array { 'admin_init' => [ [ 'flush_license_cache', 8 ], [ 'check_upgrade' ], + [ 'initialize_ri_queue_runner', 10 ], [ 'schedule_jobs', 11 ], ], 'admin_post_rocket_rocket_insights_add_homepage' => 'add_homepage_from_widget', 'rocket_deactivation' => [ [ 'cancel_scheduled_jobs' ], [ 'remove_current_plan' ], + [ 'cleanup_queue_runner_cron' ], ], 'rocket_options_changed' => 'maybe_cancel_automatic_retest_job', 'rocket_insights_retest' => 'retest_all_pages', @@ -425,6 +427,25 @@ public function check_upgrade() { $this->plan->check_upgrade(); } + /** + * Initialize the queue runner for Rocket Insights. + * + * @since 3.20 + * + * @return void + */ + public function initialize_ri_queue_runner() { + if ( ! $this->context->is_allowed() ) { + // Clean up cron event when disabled. + if ( wp_next_scheduled( 'action_scheduler_run_queue_rocket_insights', [ 'WP Cron' ] ) ) { + wp_clear_scheduled_hook( 'action_scheduler_run_queue_rocket_insights', [ 'WP Cron' ] ); + } + return; + } + + RIQueueRunner::instance()->init(); + } + /** * Remove current plan with plugin deactivation. * @@ -434,6 +455,17 @@ public function remove_current_plan() { $this->plan->remove_current_plan(); } + /** + * Clean up queue runner cron on deactivation. + * + * @since 3.20 + * + * @return void + */ + public function cleanup_queue_runner_cron() { + wp_clear_scheduled_hook( 'action_scheduler_run_queue_rocket_insights', [ 'WP Cron' ] ); + } + /** * Maybe show upgrade notice. * diff --git a/inc/Engine/Common/JobManager/JobProcessor.php b/inc/Engine/Common/JobManager/JobProcessor.php index 41222009d6..786e9e6dca 100644 --- a/inc/Engine/Common/JobManager/JobProcessor.php +++ b/inc/Engine/Common/JobManager/JobProcessor.php @@ -20,13 +20,6 @@ class JobProcessor implements LoggerAwareInterface { */ private $factories; - /** - * Queue instance. - * - * @var Queue - */ - private $queue; - /** * Retry Strategy Factory * @@ -45,18 +38,15 @@ class JobProcessor implements LoggerAwareInterface { * Instantiate the class. * * @param array $factories Array of factories. - * @param Queue $queue Queue instance. * @param StrategyFactory $strategy_factory Strategy Factory. * @param WPRClock $clock Clock object instance. */ public function __construct( array $factories, - Queue $queue, StrategyFactory $strategy_factory, WPRClock $clock ) { $this->factories = $factories; - $this->queue = $queue; $this->strategy_factory = $strategy_factory; $this->wpr_clock = $clock; } @@ -134,7 +124,9 @@ public function process_pending_jobs() { $optimization_type = $this->get_optimization_type( $row ); // Change status to in-progress. $this->make_status_inprogress( $row->url, $row->is_mobile, $optimization_type ); - $this->queue->add_job_status_check_async( $row->url, $row->is_mobile, $optimization_type ); + // Use the appropriate queue for the optimization type to ensure correct group assignment. + $queue = Queue::get_for_optimization_type( $optimization_type ); + $queue->add_job_status_check_async( $row->url, $row->is_mobile, $optimization_type ); } } diff --git a/inc/Engine/Common/JobManager/Queue/Queue.php b/inc/Engine/Common/JobManager/Queue/Queue.php index 0f842de121..2e3f090407 100644 --- a/inc/Engine/Common/JobManager/Queue/Queue.php +++ b/inc/Engine/Common/JobManager/Queue/Queue.php @@ -21,6 +21,15 @@ class Queue extends AbstractASQueue { */ protected $group = 'rocket-rucss'; + /** + * Constructor. + * + * @param string $group Queue group name. + */ + public function __construct( string $group = 'rocket-rucss' ) { + $this->group = $group; + } + /** * Pending jobs cron hook. * @@ -76,4 +85,39 @@ public function add_job_status_check_async( string $url, bool $is_mobile, string ] ); } + + /** + * Create a queue instance for RUCSS optimization. + * + * @return Queue + */ + public static function create_for_rucss(): Queue { + return new Queue( 'rocket-rucss' ); + } + + /** + * Create a queue instance for Rocket Insights optimization. + * + * @return Queue + */ + public static function create_for_rocket_insights(): Queue { + return new Queue( 'rocket-insights' ); + } + + /** + * Get the appropriate queue instance for an optimization type. + * + * @param string $optimization_type The optimization type. + * + * @return Queue + */ + public static function get_for_optimization_type( string $optimization_type ): Queue { + switch ( $optimization_type ) { + case 'rocket_insights': + return self::create_for_rocket_insights(); + case 'rucss': + default: + return self::create_for_rucss(); + } + } } diff --git a/inc/Engine/Common/JobManager/ServiceProvider.php b/inc/Engine/Common/JobManager/ServiceProvider.php index 28817be1d9..8c06eea2f9 100644 --- a/inc/Engine/Common/JobManager/ServiceProvider.php +++ b/inc/Engine/Common/JobManager/ServiceProvider.php @@ -62,7 +62,6 @@ public function register(): void { ->addArguments( [ $factories, - 'queue', 'retry_strategy_factory', 'wpr_clock', ] diff --git a/inc/Engine/Common/Queue/AbstractQueueRunner.php b/inc/Engine/Common/Queue/AbstractQueueRunner.php new file mode 100644 index 0000000000..a62029a5d7 --- /dev/null +++ b/inc/Engine/Common/Queue/AbstractQueueRunner.php @@ -0,0 +1,352 @@ +get_group() ); + $cleaner = new Cleaner( $store, $batch_size, $this->get_group() ); + } + parent::__construct( $store, $monitor, $cleaner ); + + if ( is_null( $async_request ) ) { + $async_request = new \ActionScheduler_AsyncRequest_QueueRunner( $this->store ); + } + + $this->async_request = $async_request; + } + + /** + * Initialize the queue runner. + * + * @since 3.20 + * + * @return void + */ + public function init() { + // phpcs:ignore WordPress.WP.CronInterval.CronSchedulesInterval + add_filter( 'cron_schedules', [ $this, 'add_wp_cron_schedule' ] ); + + // Check for and remove any WP Cron hook scheduled by Action Scheduler < 3.0.0, which didn't include the $context param. + $next_timestamp = wp_next_scheduled( $this->get_wp_cron_hook() ); + if ( $next_timestamp ) { + wp_unschedule_event( $next_timestamp, $this->get_wp_cron_hook() ); + } + + $cron_context = [ 'WP Cron' ]; + + if ( ! wp_next_scheduled( $this->get_wp_cron_hook(), $cron_context ) ) { + /** + * Filters the schedule for action scheduler. + * + * @since 3.20 + * + * @param string $schedule The schedule name. + * + * @return string + */ + $schedule = wpm_apply_filters_typed( 'string', 'rocket_action_scheduler_run_schedule', $this->get_wp_cron_schedule() ); + wp_schedule_event( time(), $schedule, $this->get_wp_cron_hook(), $cron_context ); + } + + // @phpstan-ignore-next-line Action callback returns int but should not return anything. + add_action( $this->get_wp_cron_hook(), [ $this, 'run' ] ); + $this->hook_dispatch_async_request(); + } + + /** + * Hook check for dispatching an async request. + * + * @since 3.20 + * + * @return void + */ + public function hook_dispatch_async_request() { + add_action( 'shutdown', [ $this, 'maybe_dispatch_async_request' ] ); + } + + /** + * Unhook check for dispatching an async request. + * + * @since 3.20 + * + * @return void + */ + public function unhook_dispatch_async_request() { + remove_action( 'shutdown', [ $this, 'maybe_dispatch_async_request' ] ); + } + + /** + * Check if we should dispatch an async request to process actions. + * + * This method is attached to 'shutdown', so is called frequently. To avoid slowing down + * the site, it mitigates the work performed in each request by: + * 1. checking if it's in the admin context and then + * 2. haven't run on the 'shutdown' hook within the lock time (60 seconds by default) + * 3. haven't exceeded the number of allowed batches. + * + * The order of these checks is important, because they run from a check on a value: + * 1. in memory - is_admin() maps to $GLOBALS or the WP_ADMIN constant + * 2. in memory - transients use autoloaded options by default + * 3. from a database query - has_maximum_concurrent_batches() run the query + * $this->store->get_claim_count() to find the current number of claims in the DB. + * + * If all of these conditions are met, then we request an async runner check whether it + * should dispatch a request to process pending actions. + * + * @since 3.20 + * + * @return void + */ + public function maybe_dispatch_async_request() { + if ( is_admin() && ! \ActionScheduler::lock()->is_locked( 'async-request-runner' ) ) { + // Only start an async queue at most once every 60 seconds. + \ActionScheduler::lock()->set( 'async-request-runner' ); + $this->async_request->maybe_dispatch(); + } + } + + /** + * Process actions in the queue. Attached to get_wp_cron_hook() hook. + * + * The $context param of this method defaults to 'WP Cron', because prior to Action Scheduler 3.0.0 + * that was the only context in which this method was run, and the hook had no context + * passed along with it. New code calling this method directly, or by triggering the hook, + * should set a context as the first parameter. + * + * @since 3.20 + * + * @param mixed $context Optional identifier for the context in which this action is being processed, e.g. 'WP CLI' or 'WP Cron' + * Generally, this should be capitalised and not localised as it's a proper noun. + * + * @return int The number of actions processed. + */ + public function run( $context = 'WP Cron' ) { + \ActionScheduler_Compatibility::raise_memory_limit(); + \ActionScheduler_Compatibility::raise_time_limit( $this->get_time_limit() ); + do_action( 'action_scheduler_before_process_queue' );// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound + $this->run_cleanup(); + $processed_actions = 0; + if ( false === $this->has_maximum_concurrent_batches() ) { + /** + * Filters the batch size for action scheduler queue runner. + * + * @since 3.20 + * + * @param int $batch_size Batch size. + * + * @return int + */ + $batch_size = wpm_apply_filters_typed( 'integer', 'action_scheduler_queue_runner_batch_size', 25 );// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound + do { + $processed_actions_in_batch = $this->do_batch( $batch_size, $context ); + $processed_actions += $processed_actions_in_batch; + } while ( $processed_actions_in_batch > 0 && ! $this->batch_limits_exceeded( $processed_actions ) ); // keep going until we run out of actions, time, or memory. + } + + do_action( 'action_scheduler_after_process_queue' );// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound + + return $processed_actions; + } + + /** + * Process a batch of actions pending in the queue. + * + * @since 3.20 + * + * @param int $size The maximum number of actions to process. + * @param string $context Optional identifier for the context in which this action is being processed. + * + * @throws \Throwable When an exception occurs during batch processing. + * + * @return int Number of actions processed. + */ + protected function do_batch( $size = 100, $context = '' ) { + // Set group filter if the store supports it. + if ( method_exists( $this->store, 'set_claim_filter' ) ) { + $this->store->set_claim_filter( 'group', $this->get_group() ); + } + + $claim = null; + $processed_actions = 0; + + try { + $claim = $this->store->stake_claim( $size ); + $this->monitor->attach( $claim ); + + foreach ( $claim->get_actions() as $action_id ) { + // bail if we lost the claim. + if ( ! in_array( $action_id, $this->store->find_actions_by_claim_id( $claim->get_id() ), true ) ) { + break; + } + $this->process_action( $action_id, $context ); + ++$processed_actions; + + if ( $this->batch_limits_exceeded( $processed_actions ) ) { + break; + } + } + } catch ( \Throwable $e ) { + // Log the exception if Logger is available. + if ( class_exists( '\WP_Rocket\Logger\Logger' ) ) { + Logger::error( 'Exception in do_batch: ' . $e->getMessage(), [ 'exception' => $e ] ); + } + // Re-throw to maintain existing error handling behavior. + throw $e; + } finally { + if ( $claim ) { + $this->store->release_claim( $claim ); + } + $this->monitor->detach(); + // Reset group filter. + $this->reset_group(); + // Clear caches to prevent memory issues. + $this->clear_caches(); + } + + return $processed_actions; + } + + /** + * Reset group in store's claim filter. + * + * @since 3.20 + * + * @return void + */ + private function reset_group() { + if ( ! method_exists( $this->store, 'set_claim_filter' ) ) { + return; + } + $this->store->set_claim_filter( 'group', '' ); + } + + /** + * Running large batches can eat up memory, as WP adds data to its object cache. + * + * If using a persistent object store, this has the side effect of flushing that + * as well, so this is disabled by default. To enable: + * + * add_filter( 'action_scheduler_queue_runner_flush_cache', '__return_true' ); + * + * @since 3.20 + * + * @return void + */ + protected function clear_caches() { + /** + * Filters whether to flush cache in action scheduler queue runner. + * + * @since 3.20 + * + * @param bool $flush_cache Whether to flush cache. + * + * @return bool + */ + if ( ! wp_using_ext_object_cache() || wpm_apply_filters_typed( 'boolean', 'action_scheduler_queue_runner_flush_cache', false ) ) {// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound + wp_cache_flush(); + } + } + + /** + * Add the cron schedule. + * + * @since 3.20 + * + * @param array $schedules Array of current schedules. + * + * @return array + */ + abstract public function add_wp_cron_schedule( $schedules ); + + /** + * Get the number of concurrent batches a runner allows. + * + * @since 3.20 + * + * @return int + */ + public function get_allowed_concurrent_batches() { + return 2; + } + + /** + * Get the cron hook name. + * + * @since 3.20 + * + * @return string + */ + abstract protected function get_wp_cron_hook(): string; + + /** + * Get the cron schedule interval. + * + * @since 3.20 + * + * @return string + */ + abstract protected function get_wp_cron_schedule(): string; + + /** + * Get the queue group name. + * + * @since 3.20 + * + * @return string + */ + abstract protected function get_group(): string; +} diff --git a/inc/Engine/Common/Queue/RUCSSQueueRunner.php b/inc/Engine/Common/Queue/RUCSSQueueRunner.php index 2af712f130..b0848c1fc5 100644 --- a/inc/Engine/Common/Queue/RUCSSQueueRunner.php +++ b/inc/Engine/Common/Queue/RUCSSQueueRunner.php @@ -4,12 +4,11 @@ namespace WP_Rocket\Engine\Common\Queue; use WP_Rocket\Logger\Logger; -use ActionScheduler_Abstract_QueueRunner; use ActionScheduler_Store; use ActionScheduler_FatalErrorMonitor; use ActionScheduler_AsyncRequest_QueueRunner; -class RUCSSQueueRunner extends ActionScheduler_Abstract_QueueRunner { +class RUCSSQueueRunner extends AbstractQueueRunner { /** * Cron hook name. @@ -21,14 +20,6 @@ class RUCSSQueueRunner extends ActionScheduler_Abstract_QueueRunner { */ const WP_CRON_SCHEDULE = 'every_minute'; - /** - * Async Request Queue Runner instance. - * We used the default one from AS. - * - * @var \ActionScheduler_AsyncRequest_QueueRunner Instance. - */ - protected $async_request; - /** * Current runner instance. * @@ -36,13 +27,6 @@ class RUCSSQueueRunner extends ActionScheduler_Abstract_QueueRunner { */ private static $runner = null; - /** - * Current queue group. - * - * @var string - */ - private $group = 'rocket-rucss'; - /** * Get singleton instance. * @@ -56,231 +40,55 @@ public static function instance() { } /** - * ActionScheduler_QueueRunner constructor. - * - * @param ActionScheduler_Store|null $store Store Instance. - * @param ActionScheduler_FatalErrorMonitor|null $monitor Fatal Error monitor instance. - * @param Cleaner|null $cleaner Cleaner instance. - * @param ActionScheduler_AsyncRequest_QueueRunner|null $async_request Async Request Queue Runner instance. - */ - public function __construct( ?ActionScheduler_Store $store = null, ?ActionScheduler_FatalErrorMonitor $monitor = null, ?Cleaner $cleaner = null, ?ActionScheduler_AsyncRequest_QueueRunner $async_request = null ) { - if ( is_null( $cleaner ) ) { - /** - * Filters the clean batch size. - * - * @since 3.11.0.5 - * - * @param int $batch_size Batch size. - * @param string $group The group name. - * - * @return int - */ - $batch_size = (int) apply_filters( 'rocket_action_scheduler_clean_batch_size', 100, $this->group ); - $cleaner = new Cleaner( $store, $batch_size, $this->group ); - } - - parent::__construct( $store, $monitor, $cleaner ); - - if ( is_null( $async_request ) ) { - $async_request = new \ActionScheduler_AsyncRequest_QueueRunner( $this->store ); - } - - $this->async_request = $async_request; - } - - /** - * Initialize the queue runner. - */ - public function init() { - - // phpcs:ignore WordPress.WP.CronInterval.CronSchedulesInterval - add_filter( 'cron_schedules', [ self::instance(), 'add_wp_cron_schedule' ] ); - - // Check for and remove any WP Cron hook scheduled by Action Scheduler < 3.0.0, which didn't include the $context param. - $next_timestamp = wp_next_scheduled( self::WP_CRON_HOOK ); - if ( $next_timestamp ) { - wp_unschedule_event( $next_timestamp, self::WP_CRON_HOOK ); - } - - $cron_context = [ 'WP Cron' ]; - - if ( ! wp_next_scheduled( self::WP_CRON_HOOK, $cron_context ) ) { - $schedule = apply_filters( 'rocket_action_scheduler_run_schedule', self::WP_CRON_SCHEDULE ); - wp_schedule_event( time(), $schedule, self::WP_CRON_HOOK, $cron_context ); - } - - // @phpstan-ignore-next-line Action callback returns int but should not return anything. - add_action( self::WP_CRON_HOOK, [ self::instance(), 'run' ] ); - $this->hook_dispatch_async_request(); - } - - /** - * Hook check for dispatching an async request. - */ - public function hook_dispatch_async_request() { - add_action( 'shutdown', [ $this, 'maybe_dispatch_async_request' ] ); - } - - /** - * Unhook check for dispatching an async request. - */ - public function unhook_dispatch_async_request() { - remove_action( 'shutdown', [ $this, 'maybe_dispatch_async_request' ] ); - } - - /** - * Check if we should dispatch an async request to process actions. - * - * This method is attached to 'shutdown', so is called frequently. To avoid slowing down - * the site, it mitigates the work performed in each request by: - * 1. checking if it's in the admin context and then - * 2. haven't run on the 'shutdown' hook within the lock time (60 seconds by default) - * 3. haven't exceeded the number of allowed batches. - * - * The order of these checks is important, because they run from a check on a value: - * 1. in memory - is_admin() maps to $GLOBALS or the WP_ADMIN constant - * 2. in memory - transients use autoloaded options by default - * 3. from a database query - has_maximum_concurrent_batches() run the query - * $this->store->get_claim_count() to find the current number of claims in the DB. - * - * If all of these conditions are met, then we request an async runner check whether it - * should dispatch a request to process pending actions. - */ - public function maybe_dispatch_async_request() { - if ( is_admin() && ! \ActionScheduler::lock()->is_locked( 'async-request-runner' ) ) { - // Only start an async queue at most once every 60 seconds. - \ActionScheduler::lock()->set( 'async-request-runner' ); - $this->async_request->maybe_dispatch(); - } - } - - /** - * Process actions in the queue. Attached to self::WP_CRON_HOOK i.e. 'action_scheduler_run_queue' - * - * The $context param of this method defaults to 'WP Cron', because prior to Action Scheduler 3.0.0 - * that was the only context in which this method was run, and the self::WP_CRON_HOOK hook had no context - * passed along with it. New code calling this method directly, or by triggering the self::WP_CRON_HOOK, - * should set a context as the first parameter. For an example of this, refer to the code seen in - * - * @see ActionScheduler_AsyncRequest_QueueRunner::handle() + * Add the cron schedule. * - * @param string $context Optional identifier for the context in which this action is being processed, e.g. 'WP CLI' or 'WP Cron' - * Generally, this should be capitalised and not localised as it's a proper noun. + * @param array $schedules Array of current schedules. * - * @return int + * @return array */ - public function run( $context = 'WP Cron' ) { - \ActionScheduler_Compatibility::raise_memory_limit(); - \ActionScheduler_Compatibility::raise_time_limit( $this->get_time_limit() ); - do_action( 'action_scheduler_before_process_queue' );// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound - $this->run_cleanup(); - $processed_actions = 0; - if ( false === $this->has_maximum_concurrent_batches() ) { - $batch_size = apply_filters( 'action_scheduler_queue_runner_batch_size', 25 );// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound - do { - $processed_actions_in_batch = $this->do_batch( $batch_size, $context ); - $processed_actions += $processed_actions_in_batch; - } while ( $processed_actions_in_batch > 0 && ! $this->batch_limits_exceeded( $processed_actions ) ); // keep going until we run out of actions, time, or memory. + public function add_wp_cron_schedule( $schedules ) { + if ( isset( $schedules['every_minute'] ) ) { + return $schedules; } - do_action( 'action_scheduler_after_process_queue' );// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound + $schedules['every_minute'] = [ + 'interval' => 60, // in seconds. + 'display' => __( 'Every minute', 'rocket' ), + ]; - return $processed_actions; + return $schedules; } /** - * Process a batch of actions pending in the queue. + * Get the cron hook name. * - * Actions are processed by claiming a set of pending actions then processing each one until either the batch - * size is completed, or memory or time limits are reached, defined by @see $this->batch_limits_exceeded(). + * @since 3.20 * - * @param int $size The maximum number of actions to process in the batch. - * @param string $context Optional identifier for the context in which this action is being processed, e.g. 'WP CLI' or 'WP Cron' - * Generally, this should be capitalised and not localised as it's a proper noun. - * @return int The number of actions processed. + * @return string */ - protected function do_batch( $size = 100, $context = '' ) { - try { - $claim = $this->store->stake_claim( $size, null, [], $this->group ); - $this->monitor->attach( $claim ); - $processed_actions = 0; - - foreach ( $claim->get_actions() as $action_id ) { - // bail if we lost the claim. - if ( ! in_array( $action_id, $this->store->find_actions_by_claim_id( $claim->get_id() ), true ) ) { - break; - } - $this->process_action( $action_id, $context ); - ++$processed_actions; - - if ( $this->batch_limits_exceeded( $processed_actions ) ) { - break; - } - } - $this->store->release_claim( $claim ); - $this->monitor->detach(); - $this->clear_caches(); - $this->reset_group(); - return $processed_actions; - } catch ( \Exception $exception ) { - Logger::debug( $exception->getMessage() ); - $this->reset_group(); - return 0; - } + protected function get_wp_cron_hook(): string { + return self::WP_CRON_HOOK; } /** - * Reset group in store's claim filter. + * Get the cron schedule interval. * - * @return void - */ - private function reset_group() { - if ( ! method_exists( $this->store, 'set_claim_filter' ) ) { - return; - } - $this->store->set_claim_filter( 'group', '' ); - } - - /** - * Running large batches can eat up memory, as WP adds data to its object cache. + * @since 3.20 * - * If using a persistent object store, this has the side effect of flushing that - * as well, so this is disabled by default. To enable: - * - * add_filter( 'action_scheduler_queue_runner_flush_cache', '__return_true' ); + * @return string */ - protected function clear_caches() { - if ( ! wp_using_ext_object_cache() || apply_filters( 'action_scheduler_queue_runner_flush_cache', false ) ) {// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound - wp_cache_flush(); - } + protected function get_wp_cron_schedule(): string { + return self::WP_CRON_SCHEDULE; } /** - * Add the cron schedule. + * Get the queue group name. * - * @param array $schedules Array of current schedules. - * - * @return array - */ - public function add_wp_cron_schedule( $schedules ) { - if ( isset( $schedules['every_minute'] ) ) { - return $schedules; - } - - $schedules['every_minute'] = [ - 'interval' => 60, // in seconds. - 'display' => __( 'Every minute', 'rocket' ), - ]; - - return $schedules; - } - - /** - * Get the number of concurrent batches a runner allows. + * @since 3.20 * - * @return int + * @return string */ - public function get_allowed_concurrent_batches() { - return 2; + protected function get_group(): string { + return 'rocket-rucss'; } } diff --git a/inc/Engine/WPRocketUninstall.php b/inc/Engine/WPRocketUninstall.php index 1897336231..9022950d9f 100644 --- a/inc/Engine/WPRocketUninstall.php +++ b/inc/Engine/WPRocketUninstall.php @@ -116,6 +116,7 @@ class WPRocketUninstall { 'rocket_preload_clean_rows_time_event', 'rocket_preload_process_pending', 'rocket_preload_revert_old_failed_rows', + 'action_scheduler_run_queue_rocket_insights', ]; /**