Comment éviter de bloquer indéfiniment lorsque vous utilisez un script PHP pour mettre à jour une table de firebase database dotée d’une transaction ouverte / en attente?

J’ai trouvé que si je modifie la table X via SQLplus et ne commets pas le changement, alors si mon application Web (qui s’exécute en tant que script PHP sous Apache en mode ModPHP) tente de valider une modification sur la table X Je commets mes changements via SQLplus.

Ce comportement est correct, mais ce que je veux, c’est un moyen propre / fiable / simple d’expiration de mon application Web après N secondes et d’émettre une erreur HTTP 409 au lieu de se bloquer indéfiniment.

Ma première pensée a été d’utiliser pcntl_alarm (N) avec un gestionnaire de signal pour intercepter le SIGALRM après N secondes. Mais j’ai trouvé que les fonctions pcntl_ * sont généralement éliminées du module Apache ModPHP au moment de la compilation, sans doute pour éviter de perturber les signaux que le processus Apache lui-même utilise pour contrôler ses enfants. Peut-être que les scripts PHP exécutés via ModPHP pour gérer leurs propres SIGARLM devraient être inefficaces, mais ce n’est pas un combat que je veux mener avec mon équipe Ops, alors je dois rejeter cette approche.

Ma deuxième idée est de lancer / lancer un processus enfant chaque fois que mon application a besoin de modifier la table X, et que le processus parent interroge l’enfant (peut-être via select () sur un tube) jusqu’à l’opération (succès) ou N secondes ont passé (timeout + échec).

La deuxième approche fonctionnera, mais elle me semble moche, compliquée et fragile.

Est-ce que quelqu’un connaît un meilleur moyen, étant donné les contraintes de PHP version 5.2.11 et Apache version 1.3.41 (sous Linux 2.6.9)?

Je pense que pour cette situation, je voudrais essayer de contrôler le délai d’attente de requête directement. La meilleure façon de faire serait d’utiliser le module MySQLi de PHP plutôt que le module MySQL, car vous avez alors access à la fonction mysqli :: options .

En utilisant mysqli :: options, vous pouvez définir la valeur du délai d’attente de la requête comme vous le souhaitez par connexion. Une fois le délai écoulé, vous pouvez contrôler l’erreur dans votre code dans le cadre du stream normal dès que cela se produit.

Si vous ne pouvez pas utiliser MySQLi (ou si vous n’utilisez pas MySQL 5), vous pouvez toujours définir cette valeur directement dans les options MySQL, mais cela aura évidemment un impact plus important sur votre application.

modifier

En réponse à votre commentaire, je peux voir que cela ne fonctionnera probablement pas. Voici quelque chose que je pensais juste qui est maladroit, mais pourrait vous faire passer.

La fonction set_time_limit () permet de définir une limite de temps globale pour l’exécution d’un script PHP et, si cette limite est atteinte, une erreur PHP fatale est déclenchée. Cependant, lorsque vous l’appelez, le minuteur se réinitialise à zéro … et les erreurs PHP fatales peuvent être traitées.

Vous pouvez écrire votre propre fonction de gestion des erreurs et, juste avant d’exécuter la requête, échangez-la via set_error_handler () . Appelez immédiatement set_time_limit() et lancez votre requête. Si votre limite de temps est dépassée, une erreur fatale se déclenche et passe à votre fonction de traitement des erreurs. Vous pouvez procéder à partir de là.

S’il n’est pas déclenché, vous pouvez réinitialiser le temporisateur avec un autre appel à set_time_limit() juste après la requête, puis utiliser restore_error_handler () pour rétablir la fonction de gestion des erreurs par défaut.

Comme je l’ai dit, maladroit, mais peut-être que ça pourrait marcher?

Malheureusement, il semble que PHP n’autorise pas l’utilisation des fonctions pcntl _ * () sous ModPHP + Apache. Et sous Linux, les fonctionnalités que PHP fournit lui-même pour définir des délais d’expiration sur un script s’appliquent uniquement au temps passé dans le script lui-même, et non au temps d’attente d’une requête de firebase database bloquée.

Par conséquent, vous devez recourir à la stratégie générique de lancement d’un nouveau processus (nous ne pouvons pas appeler pcntl_fork ()) pour “faire des choses qui pourraient bloquer”, puis demander au processus parent d’interroger la connexion et d’abandonner après N secondes. Proc_open () est un moyen d’y parvenir, avec des canaux PHP non bloquants (via stream_set_blocking ()) et une boucle d’interrogation stream_select () qui s’interrompt au bout d’un certain temps.

C’est agaçant à plusieurs niveaux. Premièrement, vous devez vous assurer de lancer le processus fils correctement et de transmettre en toute sécurité les informations sur l’opération requirejse de parent à enfant, puis vous devez vous assurer que la logique d’interrogation traite correctement les différentes conditions de défaillance des enfants. informations de retour d’enfant à parent, et enfin vous devez tout nettoyer correctement par la suite (ce qui devient important si le processus parent persiste sur de nombreuses itérations). La complexité est telle que je parie que quiconque le fera pour la première fois devra passer des semaines à corriger les bugs avant que le comportement ne soit vraiment robuste. Et bien que vous ayez un contrôle explicite sur la fréquence d’interrogation et la gestion des erreurs, vous avez également les coûts associés au lancement d’un nouveau processus.

Je concède qu’il n’y a pas de solution “taille unique” à tous les problèmes de ce type, mais tous ceux qui ont écrit une application Web en PHP qui doit communiquer avec une firebase database Oracle ont déjà voulu exécuter la requête X et lancer une exception si nous ne recevons pas de réponse dans les secondes qui suivent “. Il me semble donc ridicule que tous ceux qui veulent ce comportement doivent mettre en œuvre tout le système décrit ci-dessus (ou trouver quelqu’un d’autre qui l’a déjà fait).