Which cleanup mechanisms apply when a Drupal Ajax request is aborted?
0 reputation · 28 Jul 2023, 09:02 UTC
0 reputation · 28 Jul 2023, 09:02 UTC
When a browser cancels a Drupal Ajax request, the client-side XMLHttpRequest is aborted and the network connection closes. The PHP process handling that request continues running until it reaches PHP's max_execution_time or an explicit connection_aborted() check. For Ajax callbacks that acquire locks or write temporary data, this gap creates uncertainty about when server-side resources are released.
Drupal core does not expose a dedicated hook or event that fires specifically when an Ajax request is aborted. Although the Batch API and Queue workers include connection_aborted() checks in their loops, standard Ajax callbacks do not. The Symfony HttpKernel does dispatch kernel.terminate after the response is sent, including when the connection is aborted, but it remains unclear whether that event is a safe place for cleanup logic in all environments, particularly behind proxies or load balancers.
Is kernel.terminate a reliable cleanup point after an aborted Ajax request, or should developers rely on periodic connection_aborted() checks inside the callback? How do intermediary network devices affect the accuracy of abort detection in either approach?
34275 reputation · 28 Jul 2023, 11:41 UTC
When a browser aborts a Drupal Ajax request, the client-side connection closes, but the server-side PHP process continues executing until it naturally completes, reaches the max_execution_time, or explicitly detects the disconnection. Drupal core does not provide a dedicated hook for aborted Ajax requests.
kernel.terminate event typically fires after the response is sent, including cases where the connection was aborted, provided the PHP process is allowed to finish.connection_aborted() to stop processing during long loops.Because no single event is guaranteed across all server configurations, a dual-layered approach is necessary for releasing locks or deleting temporary files.
For long-running processes, manually check the connection status. This prevents the server from wasting resources on a request the user has already abandoned.
if (connection_aborted()) {
// Perform immediate cleanup and exit
return [];
}
Use a kernel.terminate listener or a PHP shutdown function to ensure cleanup occurs regardless of how the script ended (unless the process was killed by the OS/SIGKILL).
Example: Symfony Event Subscriber
public function onKernelTerminate(PostResponseEvent $event) {
if (connection_aborted()) {
// Release locks or remove temporary data
}
}
The reliability of kernel.terminate and connection_aborted() depends heavily on the infrastructure:
fastcgi_finish_request() can alter when the connection is considered "closed" relative to the script's execution.Verification: To verify behavior in your specific environment, add error_log() entries to both a kernel.terminate listener and a register_shutdown_function(), then abort a request via Browser DevTools to see which logs persist.
Diagnostic Detail Required: Does your environment use a buffering reverse proxy or fastcgi_finish_request()? This determines if you can rely on post-response events or must prioritize active polling.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.