Mise à jour le 13/11/2021
PHP RFC: Wall-Clock Time Based Execution Timeout

PHP RFC: Wall-Clock Time Based Execution Timeout

1. Introduction de la RFC

Lien de la RFC : https://wiki.php.net/rfc/max_execution_wall_time
Date d'introduction : 2020-12-12

Cette RFC propose d'introduire le paramètre INI max_execution_wall_time.
Ce paramètre définit un temps de time-out auquel le script PHP doit s'arreter.

Ce qui me semble existe déjà puisque ça porte d'ailleurs le nom de max_execution_time.
Sauf que l'auteur indique le max_execution_time utilise le temps CPU et non pas le "wall-clock time" (alias du real-time).

2. Wall-clock time VS CPU time

Tout se joue dans la définition de ces deux termes.
* Dans le cas du CPU Time, il faut que le script PHP fasse un vrai traitement (càd qu'à chaque ligne de code interprété, on compte à nouveau le temps qu'il reste).
* Dans le cas du Real Time, peu importe ce que le script PHP fasse, si le temps est écoulé, il s'arrête, même s'il faisait quelque chose d'important.

Autrement dit, on pourrait penser intuitivement que le max_execution_time correspond au cas de figure numéro 2, alors qu'il correspond au cas numéro 1.

L'auteur pointe donc un problème : si le script PHP est en attente de quelque chose, il continu d'être executé.
Et donc, il risque d'y avoir une accumulation d'execution de script PHP au niveau du serveur web, ce qui peut faire tomber un site web.

3. Démonstration

J'ai souhaité vérifier qu'il était possible de dépasser le temps de max_execution_time avec un appel HTTP.
Pour ce faire, j'ai créer un fichier index.php que j'interroge via mon navigateur, rien de très sorcier.

3.1 Cas où le script PHP s'arrète après un time out

index.php
<?php
$i = 0;
do {
    $i++;
} while ($i < 100000000000);

Si on considère que le paramètre max_execution_time est à la valeur 30, ce genre de code part en time out (le nombre d'itérations dépend de la puissance de votre CPU.
Ici le script fait un traitement, on peut se mettre à la place d'un outil comme xdebug et faire des boucles visuellement.
Pendant ce traitement les secondes qui défilent sont prises en compte.

Le script s'arrete au bout de 30 secondes, on peut en voir la trace dans le log du serveur web (ici Apache/2.4.41) :
[Tue Dec 15 15:04:05.217052 2020] [php7:error] [pid 1609] [client 127.0.0.1:43918] PHP Fatal error: Maximum execution time of 30 seconds exceeded in /var/www/html/index.php on line 8

3.2 Cas où le script PHP ne s'arrète pas

index.php
<?php
sleep(50);

Si on considère que le paramètre max_execution_time est à la valeur 30, ce genre de code continue de tourner après 30 secondes. La page s'affiche au bout de 50 secondes.
Ici c'est un sleep, on peut le changer, sauf que ce problème peut tout à fait arriver lorsque notre code PHP fait appel à un service externe (mysql/memcached/url). Et là c'est effectivement le début d'une belle pagaille car les appels vont se cumuler et le service d'en face va avoir deux options : soit il est très performant au démarrage et il traite tout ce petit monde, soit il décide de dire stop et tous les scripts PHP en cours d'execution font finalement s'arreter.

4. Pertinente de la RFC

4.1 Le plutôt pour

Alors est ce que cette RFC est pertinente ? Et bien, la réponse me semble oui car on aurait préféré que ce comportement fusse celui du max_execution_time.
Ce qui est ennuyeux, comme le soulève d'auteur, c'est qu'il serait extrèmement dangereux de modifier le comportement du max_execution_time car cela aurait des effets de bords potentiellement très graves chez l'ensemble des utilisateurs du PHP (80% des sites web).
Le plus prudent étant en effet de créer un nouveau paramètre appelé max_execution_wall_time.

4.2 Le plutôt contre

Le risque : cependant, il faut aussi se dire que si l'on force le script PHP à réellement s'arreter, cela veut dire qu'il ne pourra plus pallier à la faiblesse des services qu'il utilisera. Autrement dit, si le serveur MySql d'en face met 2 minutes à exécuter une requête non optimisée (cas classique : il manquerait un index), et bien le script PHP plantera alors que ce n'était pas en soi de sa faute.
Donc, avant d'introduire ce paramètre max_execution_wall_time, il faudrait modifier son code pour introduire des temps limites aux appels externes, et donc être capable de determiner pour tous les chemins de code possibles le temps maximum que l'on accorderait aux services externes, chose qui me semble en pratique infaisable.

En bref, si le max_execution_time a ce comportement là, ce n'est peut être pas pour rien. On verra ce qu'il en est dans les mois à venir de cette proposition de Máté Kocsis.