PluginProbe
Image Optimizer – Compress Images and Convert to WebP or AVIF / 1.7.4
Image Optimizer – Compress Images and Convert to WebP or AVIF v1.7.4
1.7.7 1.7.6 1.7.5 1.7.4 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.2.1 1.3.0 1.4.0 1.4.1 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 All 33 releases
image-optimization / modules / optimization / components / actions-cleanup.php

actions-cleanup.php in Image Optimizer – Compress Images and Convert to WebP or AVIF 1.7.4, at modules/optimization/components/actions-cleanup.php

154 lines 4.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace ImageOptimization\Modules\Optimization\Components;
4
5 use ImageOptimization\Classes\Async_Operation\{
6 Async_Operation,
7 Async_Operation_Hook,
8 Async_Operation_Queue,
9 Exceptions\Async_Operation_Exception,
10 Queries\Image_Optimization_Operation_Query,
11 Queries\Operation_Query
12 };
13
14 use ImageOptimization\Classes\Image\{
15 Image_Meta,
16 Image_Optimization_Error_Type,
17 Image_Query_Builder,
18 Image_Status,
19 };
20
21 use ImageOptimization\Classes\Logger;
22 use Throwable;
23
24 if ( ! defined( 'ABSPATH' ) ) {
25 exit; // Exit if accessed directly.
26 }
27
28 class Actions_Cleanup {
29 const FIVE_MINUTES_IN_SECONDS = 300;
30
31 /**
32 * @async
33 * @return void
34 */
35 public function cleanup_stuck_operations() {
36 global $wpdb;
37
38 $table_name = $wpdb->prefix . 'actionscheduler_actions';
39 $now = time();
40 $threshold = $now - self::FIVE_MINUTES_IN_SECONDS;
41
42 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
43 $results = $wpdb->get_col(
44 $wpdb->prepare(
45 "
46 SELECT action_id
47 FROM {$table_name}
48 WHERE last_attempt_gmt IS NOT NULL
49 AND UNIX_TIMESTAMP(last_attempt_gmt) < %d
50 ",
51 $threshold
52 )
53 );
54 // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
55
56 if ( empty( $results ) ) {
57 Logger::debug( 'No stuck optimization operations found for cleanup.' );
58 return;
59 }
60
61 foreach ( $results as $action_id ) {
62 $action = Async_Operation::get_by_id( (int) $action_id );
63
64 if (
65 ! $action ||
66 Async_Operation_Queue::OPTIMIZE !== $action->get_queue() ||
67 Async_Operation::OPERATION_STATUS_RUNNING !== $action->get_status()
68 ) {
69 continue;
70 }
71
72 try {
73 do_action(
74 'action_scheduler_failed_action',
75 $action_id,
76 self::FIVE_MINUTES_IN_SECONDS
77 );
78
79 Logger::debug( "Triggered retry for stuck action ID {$action_id}." );
80 } catch ( Throwable $t ) {
81 Logger::warn( "Failed to handle stuck operation for action ID {$action_id}: " . $t->getMessage() );
82 }
83 }
84
85 try {
86 $this->cleanup_stuck_statuses();
87 } catch ( Throwable $t ) {
88 Logger::warn( 'Failed to run stuck statuses clearing job: ' . $t->getMessage() );
89 }
90 }
91
92 /**
93 * The handler checks if there are any attachments that have the in-progress status, but no jobs are currently
94 * run or pending. Those attachment statuses will be updated to a generic error.
95 *
96 * @throws Async_Operation_Exception
97 */
98 public function cleanup_stuck_statuses() {
99 $operations_query = ( new Image_Optimization_Operation_Query() )
100 ->set_status( [ Async_Operation::OPERATION_STATUS_PENDING, Async_Operation::OPERATION_STATUS_RUNNING ] )
101 ->set_limit( 1 );
102
103 $operations = Async_Operation::get( $operations_query );
104
105 if ( ! empty( $operations ) ) {
106 return;
107 }
108
109 $image_query = ( new Image_Query_Builder() )
110 ->return_optimization_in_progress_images()
111 ->set_paging_size( -1 )
112 ->execute();
113
114 foreach ( $image_query->posts as $attachment_id ) {
115 ( new Image_Meta( $attachment_id ) )
116 ->set_status( Image_Status::OPTIMIZATION_FAILED )
117 ->set_error_type( Image_Optimization_Error_Type::GENERIC )
118 ->save();
119 }
120 }
121
122 public function schedule_cleanup() {
123 try {
124 $cleanup_job_query = ( new Operation_Query() )
125 ->set_queue( Async_Operation_Queue::CLEANUP )
126 ->set_hook( Async_Operation_Hook::STUCK_OPERATION_CLEANUP )
127 ->set_status( [ Async_Operation::OPERATION_STATUS_PENDING, Async_Operation::OPERATION_STATUS_RUNNING ] )
128 ->set_limit( 1 );
129
130 // Prevents job duplication. For some reason unique=true is not enough
131 if ( ! empty( Async_Operation::get( $cleanup_job_query ) ) ) {
132 return;
133 }
134
135 Async_Operation::create_recurring(
136 time(),
137 self::FIVE_MINUTES_IN_SECONDS,
138 Async_Operation_Hook::STUCK_OPERATION_CLEANUP,
139 [],
140 Async_Operation_Queue::CLEANUP,
141 10,
142 true
143 );
144 } catch ( Async_Operation_Exception $aoe ) {
145 Logger::warn( 'Failed to schedule recurring stuck operation cleanup: ' . $aoe->getMessage() );
146 }
147 }
148
149 public function __construct() {
150 add_action( 'action_scheduler_init', [ $this, 'schedule_cleanup' ] );
151 add_action( Async_Operation_Hook::STUCK_OPERATION_CLEANUP, [ $this, 'cleanup_stuck_operations' ] );
152 }
153 }
154