PluginProbe
WANotifier for Forms and Actions / 3.1.1
WANotifier for Forms and Actions v3.1.1
3.1.1 3.1.0 3.0.4 2.7.10 2.7.11 2.7.12 2.7.13 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 3.0.0 3.0.1 3.0.2 3.0.3 trunk 0.1.0 0.1.1 1.0.0 1.0.1 1.0.2 All 67 releases
← All changes | libraries/action-scheduler/classes/ActionScheduler_QueueRunner.php +86 -27 2.7.93.1.1 View file →
@@ -7,26 +7,48 @@
7 7 const WP_CRON_HOOK = 'action_scheduler_run_queue';
8 8
9 9 const WP_CRON_SCHEDULE = 'every_minute';
10 10
11 - /** @var ActionScheduler_AsyncRequest_QueueRunner */
11 + /**
12 + * ActionScheduler_AsyncRequest_QueueRunner instance.
13 + *
14 + * @var ActionScheduler_AsyncRequest_QueueRunner
15 + */
12 16 protected $async_request;
13 17
14 - /** @var ActionScheduler_QueueRunner */
18 + /**
19 + * ActionScheduler_QueueRunner instance.
20 + *
21 + * @var ActionScheduler_QueueRunner
22 + */
15 23 private static $runner = null;
16 24
17 - /** @var int */
25 + /**
26 + * Whether the cleaner instance is a non-default one.
27 + *
28 + * @var bool
29 + */
30 + private $is_custom_cleaner;
31 +
32 + /**
33 + * Number of processed actions.
34 + *
35 + * @var int
36 + */
18 37 private $processed_actions_count = 0;
19 38
20 39 /**
40 + * Get instance.
41 + *
21 42 * @return ActionScheduler_QueueRunner
22 43 * @codeCoverageIgnore
23 44 */
24 45 public static function instance() {
25 - if ( empty(self::$runner) ) {
26 - $class = apply_filters('action_scheduler_queue_runner_class', 'ActionScheduler_QueueRunner');
46 + if ( empty( self::$runner ) ) {
47 + $class = apply_filters( 'action_scheduler_queue_runner_class', 'ActionScheduler_QueueRunner' );
27 48 self::$runner = new $class();
28 49 }
50 +
29 51 return self::$runner;
30 52 }
31 53
32 54 /**
@@ -31,13 +53,14 @@
31 53
32 54 /**
33 55 * ActionScheduler_QueueRunner constructor.
34 56 *
35 - * @param ActionScheduler_Store $store
36 - * @param ActionScheduler_FatalErrorMonitor $monitor
37 - * @param ActionScheduler_QueueCleaner $cleaner
57 + * @param ActionScheduler_Store|null $store Store object.
58 + * @param ActionScheduler_FatalErrorMonitor|null $monitor Monitor object.
59 + * @param ActionScheduler_QueueCleaner|null $cleaner Cleaner object.
60 + * @param ActionScheduler_AsyncRequest_QueueRunner|null $async_request Async request runner object.
38 61 */
39 - public function __construct( ActionScheduler_Store $store = null, ActionScheduler_FatalErrorMonitor $monitor = null, ActionScheduler_QueueCleaner $cleaner = null, ActionScheduler_AsyncRequest_QueueRunner $async_request = null ) {
62 + public function __construct( ?ActionScheduler_Store $store = null, ?ActionScheduler_FatalErrorMonitor $monitor = null, ?ActionScheduler_QueueCleaner $cleaner = null, ?ActionScheduler_AsyncRequest_QueueRunner $async_request = null ) {
40 63 parent::__construct( $store, $monitor, $cleaner );
41 64
42 65 if ( is_null( $async_request ) ) {
43 66 $async_request = new ActionScheduler_AsyncRequest_QueueRunner( $this->store );
@@ -42,19 +65,22 @@
42 65 if ( is_null( $async_request ) ) {
43 66 $async_request = new ActionScheduler_AsyncRequest_QueueRunner( $this->store );
44 67 }
45 68
46 - $this->async_request = $async_request;
69 + $this->async_request = $async_request;
70 + $this->is_custom_cleaner = get_class( $this->cleaner ) !== ActionScheduler_QueueCleaner::class;
47 71 }
48 72
49 73 /**
74 + * Initialize.
75 + *
50 76 * @codeCoverageIgnore
51 77 */
52 78 public function init() {
53 79
54 - add_filter( 'cron_schedules', array( self::instance(), 'add_wp_cron_schedule' ) );
80 + add_filter( 'cron_schedules', array( self::instance(), 'add_wp_cron_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.CronSchedulesInterval
55 81
56 - // Check for and remove any WP Cron hook scheduled by Action Scheduler < 3.0.0, which didn't include the $context param
82 + // Check for and remove any WP Cron hook scheduled by Action Scheduler < 3.0.0, which didn't include the $context param.
57 83 $next_timestamp = wp_next_scheduled( self::WP_CRON_HOOK );
58 84 if ( $next_timestamp ) {
59 85 wp_unschedule_event( $next_timestamp, self::WP_CRON_HOOK );
60 86 }
@@ -67,8 +93,15 @@
67 93 }
68 94
69 95 add_action( self::WP_CRON_HOOK, array( self::instance(), 'run' ) );
70 96 $this->hook_dispatch_async_request();
97 +
98 + // Backward compatibility: If the action cleaner is standard, cleaning will be performed as an action to improve throughput
99 + // and enable daily runs. If not, cleaning will occur explicitly before processing actions to ensure backward compatibility.
100 + // The cleaner was initially designed as a QueueRunner dependency, which is why the hooks are registered here.
101 + if ( ! $this->is_custom_cleaner ) {
102 + $this->cleaner->register_cleaner_hooks();
103 + }
71 104 }
72 105
73 106 /**
74 107 * Hook check for dispatching an async request.
@@ -102,11 +135,14 @@
102 135 * If all of these conditions are met, then we request an async runner check whether it
103 136 * should dispatch a request to process pending actions.
104 137 */
105 138 public function maybe_dispatch_async_request() {
106 - if ( is_admin() && ! ActionScheduler::lock()->is_locked( 'async-request-runner' ) ) {
107 - // Only start an async queue at most once every 60 seconds
108 - ActionScheduler::lock()->set( 'async-request-runner' );
139 + // Only start an async queue at most once every 60 seconds.
140 + if (
141 + is_admin()
142 + && ! ActionScheduler::lock()->is_locked( 'async-request-runner' )
143 + && ActionScheduler::lock()->set( 'async-request-runner' )
144 + ) {
109 145 $this->async_request->maybe_dispatch();
110 146 }
111 147 }
112 148
@@ -116,11 +152,12 @@
116 152 * The $context param of this method defaults to 'WP Cron', because prior to Action Scheduler 3.0.0
117 153 * that was the only context in which this method was run, and the self::WP_CRON_HOOK hook had no context
118 154 * passed along with it. New code calling this method directly, or by triggering the self::WP_CRON_HOOK,
119 155 * should set a context as the first parameter. For an example of this, refer to the code seen in
156 + *
120 157 * @see ActionScheduler_AsyncRequest_QueueRunner::handle()
121 158 *
122 - * @param string $context Optional identifer for the context in which this action is being processed, e.g. 'WP CLI' or 'WP Cron'
159 + * @param string $context Optional identifier for the context in which this action is being processed, e.g. 'WP CLI' or 'WP Cron'
123 160 * Generally, this should be capitalised and not localised as it's a proper noun.
124 161 * @return int The number of actions processed.
125 162 */
126 163 public function run( $context = 'WP Cron' ) {
@@ -125,18 +162,32 @@
125 162 */
126 163 public function run( $context = 'WP Cron' ) {
127 164 ActionScheduler_Compatibility::raise_memory_limit();
128 165 ActionScheduler_Compatibility::raise_time_limit( $this->get_time_limit() );
166 +
129 167 do_action( 'action_scheduler_before_process_queue' );
130 - $this->run_cleanup();
131 168
169 + $cleanup_time_limit = 10 * $this->get_time_limit();
170 + // Backward compatibility: If the action cleaner is standard, cleaning will be performed as an action to improve throughput
171 + // and enable daily runs. If not, cleaning will occur explicitly before processing actions to ensure backward compatibility.
172 + if ( $this->is_custom_cleaner ) {
173 + // Execute complete cleanup cycle, as in this logical branch deletion IS NOT executed via a separate action.
174 + $this->cleaner->clean( $cleanup_time_limit );
175 + } else {
176 + // Execute partial cleanup cycle, as in this logical branch deletion IS executed via a separate action.
177 + $this->cleaner->reset_timeouts( $cleanup_time_limit );
178 + $this->cleaner->mark_failures( $cleanup_time_limit );
179 + }
180 +
132 181 $this->processed_actions_count = 0;
133 182 if ( false === $this->has_maximum_concurrent_batches() ) {
134 183 $batch_size = apply_filters( 'action_scheduler_queue_runner_batch_size', 25 );
184 + // Note: gc_collect_cycles() was considered here and in do_batch/clear_caches, but rejected:
185 + // upside is speculative, GC sweep cost scales with object count and can cause pauses.
135 186 do {
136 187 $processed_actions_in_batch = $this->do_batch( $batch_size, $context );
137 188 $this->processed_actions_count += $processed_actions_in_batch;
138 - } while ( $processed_actions_in_batch > 0 && ! $this->batch_limits_exceeded( $this->processed_actions_count ) ); // keep going until we run out of actions, time, or memory
189 + } while ( $processed_actions_in_batch > 0 && ! $this->batch_limits_exceeded( $this->processed_actions_count ) ); // keep going until we run out of actions, time, or memory.
139 190 }
140 191
141 192 do_action( 'action_scheduler_after_process_queue' );
142 193 return $this->processed_actions_count;
@@ -147,23 +198,25 @@
147 198 *
148 199 * Actions are processed by claiming a set of pending actions then processing each one until either the batch
149 200 * size is completed, or memory or time limits are reached, defined by @see $this->batch_limits_exceeded().
150 201 *
151 - * @param int $size The maximum number of actions to process in the batch.
152 - * @param string $context Optional identifer for the context in which this action is being processed, e.g. 'WP CLI' or 'WP Cron'
153 - * Generally, this should be capitalised and not localised as it's a proper noun.
202 + * @param int $size The maximum number of actions to process in the batch.
203 + * @param string $context Optional identifier for the context in which this action is being processed, e.g. 'WP CLI' or 'WP Cron'
204 + * Generally, this should be capitalised and not localised as it's a proper noun.
154 205 * @return int The number of actions processed.
155 206 */
156 207 protected function do_batch( $size = 100, $context = '' ) {
157 - $claim = $this->store->stake_claim($size);
158 - $this->monitor->attach($claim);
208 + $claim = $this->store->stake_claim( $size );
209 + $this->monitor->attach( $claim );
159 210 $processed_actions = 0;
160 211
212 + $claim_id = $claim->get_id();
161 213 foreach ( $claim->get_actions() as $action_id ) {
162 - // bail if we lost the claim
163 - if ( ! in_array( $action_id, $this->store->find_actions_by_claim_id( $claim->get_id() ) ) ) {
214 + // Bail if we lost the claim.
215 + if ( $claim_id !== $this->store->get_claim_id( $action_id ) ) {
164 216 break;
165 217 }
218 +
166 219 $this->process_action( $action_id, $context );
167 220 $processed_actions++;
168 221
169 222 if ( $this->batch_limits_exceeded( $processed_actions + $this->processed_actions_count ) ) {
@@ -169,9 +222,9 @@
169 222 if ( $this->batch_limits_exceeded( $processed_actions + $this->processed_actions_count ) ) {
170 223 break;
171 224 }
172 225 }
173 - $this->store->release_claim($claim);
226 + $this->store->release_claim( $claim );
174 227 $this->monitor->detach();
175 228 $this->clear_caches();
176 229 return $processed_actions;
177 230 }
@@ -214,11 +267,17 @@
214 267 wp_cache_flush();
215 268 }
216 269 }
217 270
271 + /**
272 + * Add schedule to WP cron.
273 + *
274 + * @param array<string, array<string, int|string>> $schedules Schedules.
275 + * @return array<string, array<string, int|string>>
276 + */
218 277 public function add_wp_cron_schedule( $schedules ) {
219 278 $schedules['every_minute'] = array(
220 - 'interval' => 60, // in seconds
279 + 'interval' => 60, // in seconds.
221 280 'display' => __( 'Every minute', 'action-scheduler' ),
222 281 );
223 282
224 283 return $schedules;