I have a PHP script which retrieves X records from a database, and loops through them at a rate of 1 every 10 seconds.
The same script must work with both a Cron Job, and an AJAX call via browser.
The cron part seems to work fine, I have a "master" cron job running every minute on my server, which then retrieves jobs defined in a database (with their own timing and configuration) and handles them with a curl_multi_init() logic.
The faulty part is that calling the script via AJAX freezes my entire web application until the script finishes its execution.
Here's the relevant code:
public function getAndHandleData() : array {
//instantiate db class and variables
$data = $database->getData();
$sleepTime = 60/$requestsPerMinute;
foreach ($data as $k => $v) {
// call function which does a curl request to a web service
// handle response
if ($k !== array_key_last($data)) {
// wait $sleepTime seconds before doing the next iteration, unless it's the last
sleep(ceil($sleepTime));
}
}
}
The JQuery AJAX call which freezes my app until it completes:
function apiRequestHandler(type, params) {
$.ajax({
type: 'POST',
url: '/route_calling_the_same_php_function',
data: {
'type' : type,
'params' : params,
},
cache: false,
timeout: 800000,
dataType: "json",
success: function (data)
{
const success = data['success'] ?? null
if (success) {...}
},
error: {...}
});
}
Solved:
The problem is caused by PHP session management, which locks the session file when a script is being executed, even if sleeping.
Using session_write_close()
before the loop+sleep solves the app freezing, while the task runs in the background, even if switching to another page.
Final working pseudo-code:
public function getAndHandleData() : array {
//instantiate db class and variables
$data = $database->getData();
$sleepTime = 60/$requestsPerMinute;
// locks session to prevent system freeze
session_write_close();
foreach ($data as $k => $v) {
// call function which does a curl request to a web service
// handle response
if ($k !== array_key_last($data)) {
// wait $sleepTime seconds before doing the next iteration, unless it's the last
sleep(ceil($sleepTime));
}
}
}