PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.10.19
ووسلام – همگام سازی ووکامرس و باسلام v1.10.19
1.10.19 1.10.20 1.10.18 1.10.17 1.10.15 1.10.14 1.10.13 1.10.12 1.10.10 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.2 1.9.1 1.9.0 1.8.8 1.8.5 1.8.6 All 53 releases
← All changes | JobsRunner.php +39 -19 1.10.121.10.19 View file →
@@ -10,9 +10,12 @@
10 10 class JobsRunner
11 11 {
12 12 private const ASYNC_ACTION = 'sync_basalam_run_jobs_async';
13 13 private const ASYNC_DISPATCH_LOCK_TRANSIENT = 'sync_basalam_jobs_runner_async_dispatch_lock';
14 - private const ASYNC_DISPATCH_LOCK_SECONDS = 1;
14 + // Keep the dispatch lease longer than a normal async batch. Without this,
15 + // every frontend request can boot another full WordPress AJAX worker while
16 + // a large product queue is active.
17 + private const ASYNC_DISPATCH_LOCK_SECONDS = 25;
15 18 private const ASYNC_TIME_LIMIT_SECONDS = 20;
16 19 private const GLOBAL_RUNNER_LAST_RUN_OPTION = 'sync_basalam_jobs_runner_last_run';
17 20 private const STALE_PROCESSING_TIMEOUT_SECONDS = 120;
18 21
@@ -28,10 +31,12 @@
28 31 $CheckHttpBlockService
29 32 ) {
30 33 add_action('wp_ajax_' . self::ASYNC_ACTION, [$this, 'handleAsyncRequest']);
31 34 add_action('wp_ajax_nopriv_' . self::ASYNC_ACTION, [$this, 'handleAsyncRequest']);
35 + // Probe and dispatch after the response path so normal storefront
36 + // requests never pay for the queue query or loopback HTTP request.
32 37 add_action('shutdown', [$this, 'maybeDispatchAsyncRequest'], PHP_INT_MAX);
33 - add_action('sync_basalam_job_created', [$this, 'maybeDispatchAsyncRequest']);
38 + add_action('sync_basalam_job_created', [$this, 'maybeDispatchAsyncRequest'], 10, 0);
34 39
35 40 $this->jobManager = $jobManager;
36 41 $this->jobExecutor = $jobExecutor;
37 42 $this->discountScheduler = $discountScheduler;
@@ -43,8 +48,11 @@
43 48 if ($this->isCurrentAsyncRequest()) return;
44 49 if ($this->CheckHttpBlockService->SyncBasalamHttpBlock()) return;
45 50 if (get_transient(self::ASYNC_DISPATCH_LOCK_TRANSIENT)) return;
46 51
52 + // Reserve the dispatch lease before probing the queue. This keeps
53 + // concurrent shutdown callbacks from all running the queue query and
54 + // dispatching duplicate async workers.
47 55 set_transient(
48 56 self::ASYNC_DISPATCH_LOCK_TRANSIENT,
49 57 1,
50 58 self::ASYNC_DISPATCH_LOCK_SECONDS
@@ -89,8 +97,16 @@
89 97 }
90 98
91 99 private function runAsyncBatch(): int
92 100 {
101 + if ($this->CheckHttpBlockService->SyncBasalamHttpBlock()) return 0;
102 +
103 + // Hold the advisory lock for the whole batch, including rate-limit
104 + // waits. Previously it was released after every job, so duplicate
105 + // async requests could pile up and sleep in parallel until the next
106 + // job became eligible, exhausting the site's PHP workers.
107 + if (!$this->jobExecutor->acquireGlobalJobsLock(0)) return 0;
108 +
93 109 $processed = 0;
94 110 $deadline = microtime(true) + (float) apply_filters(
95 111 'sync_basalam_jobs_runner_async_time_limit',
96 112 self::ASYNC_TIME_LIMIT_SECONDS
@@ -95,30 +111,34 @@
95 111 'sync_basalam_jobs_runner_async_time_limit',
96 112 self::ASYNC_TIME_LIMIT_SECONDS
97 113 );
98 114
99 - while (microtime(true) < $deadline) {
100 - if (!$this->jobManager->hasPendingOrStaleProcessingJobs(self::STALE_PROCESSING_TIMEOUT_SECONDS)) {
101 - break;
102 - }
115 + try {
116 + while (microtime(true) < $deadline) {
117 + if (!$this->jobManager->hasPendingOrStaleProcessingJobs(self::STALE_PROCESSING_TIMEOUT_SECONDS)) {
118 + break;
119 + }
103 120
104 - $ranJob = $this->checkAndRunJobs();
121 + $ranJob = $this->runEligibleJobs();
105 122
106 - if ($ranJob) {
107 - $processed++;
108 - }
123 + if ($ranJob) {
124 + $processed++;
125 + }
109 126
110 - $delay = $this->secondsUntilNextAllowedRun();
111 - if ($delay <= 0.0) {
112 - if (!$ranJob) break;
113 - continue;
114 - }
127 + $delay = $this->secondsUntilNextAllowedRun();
128 + if ($delay <= 0.0) {
129 + if (!$ranJob) break;
130 + continue;
131 + }
115 132
116 - if ((microtime(true) + $delay) >= $deadline) {
117 - break;
133 + if ((microtime(true) + $delay) >= $deadline) {
134 + break;
135 + }
136 +
137 + usleep((int) ($delay * 1000000));
118 138 }
119 -
120 - usleep((int) ($delay * 1000000));
139 + } finally {
140 + $this->jobExecutor->releaseGlobalJobsLock();
121 141 }
122 142
123 143 return $processed;
124 144 }