php, sémaphores et atomicité

SuperCed

Membre expert
Club iGen
20 Juin 2001
1 346
72
45
superced.rb38.eu
J'ai un ensemble de webservices utilisés avec la librairie NuSoap.
Ces webservices doivent être appelés dans un certain ordre pour un utilisateur distant.
C'est à dire qu'il y a un séquencement d'actions (d'appels de méthodes) et que l'utilisateur ne doit en aucun cas pouvoir appeler 2 webservices en même temps.

J'aimerais, pour éviter ces appels simultanés et concurrents, mettre un place un système d'attente afin que le premier appel soit bien terminé avant que l'autre se lance.

Pour ce faire, j'ai pensé à un système de sémaphore ou de mutex, comme on fait en langage C. Et là, a mon grand désarroi, je m'aperçois que php est bien mal armé pour ce genre de choses.
D'une part, les sémaphore ne sont disponible que sous les Système V, d'autre part, il faut recompiler le tout, ce qui dans mon cas n'est pas concevable.

J'ai besoin donc d'une restriction par session php, pour ne pas qu'il y ait des appels concurrents aux webservices. J'ai pensé pour cela, utiliser une variable de session "monSemaphore" que je mettrai à 0 ou à 1 si un appel est en cours. Dans le cas ou il y a déjà un appel en cours, je pourrai faire boucler sur un sleep() ou wait() pour tester à intervalle régulier la disponibilité de mon sémaphore. Bien sur, je mettrai aussi un timeout pour éviter tout deadlock.

Mon problème est le suivant : je ne suis pas certain que la lecture ou l'écriture dans une variable de session soit atomique ou protégée (thread safe).

Donc ma question est :
est-ce que la lecture d'une variable de session php est atomique ou "thread safe"? Ou est-ce que cela n'est pas du tout prévu dans php?

Merci beaucoup!
 
Pourquoi y a-t-il plusieurs services ?
Est-ce que chacun de ces services peut être appelé de manière totalement indépendante ?
Je me trompe peut-être, vu que je n'ai pas la connaissance complète de ton architecture, mais ton problème ne viendrait-il pas d'un problème de design ?
Chacun de tes webservices doit fournir un service à part entière. Exemple : si tu es une banque, tu ne vas pas faire un webservice pour débiter d'un compte X et un autre pour créditer sur un compte Y ; tu fais un seul service pour débiter X et créditer Y.
 
L'appli est un ensemble de services.
En fait, il y a une fonction pour avoir une liste de produits, une fonction pour ajouter au panier, une fonction pour payer, etc.

Donc si le gars essaye d'appeler en même temps la fonction qui ajoute dans le panier et le paiement, ça pose des problèmes.

Par contre, je veux pas que le gars ajoute dans son panier et paye dans la même fonction.

Donc je ne pense pas qu'il s'agisse d'un problème de design.
 
D'accord. Je comprends mieux le problème et effectivement ce n'est pas un problème de design.
Il me semble que le plus simple dans ton cas serait que chaque appel à tes webservices envoie un identifiant qui identifie l'utilisateur et son processus d'achat. Ensuite, j'imagine que tes informations sont stockées en base de données ? Alors pourquoi ne pas avoir un état de ta commande en bdd ? Par exemple: tant que tu n'as pas appeler le service "jeVeuxPayer()", tu peux appeler "ajouterProduit()" tant que tu veux. Et si quelqu'un appelle ajouterProduit() après avoir appelé jeVeuxPayer(), alors le webService lève une fault.
 
En fait, pour ça, je peux mettre des états, mais mon problème est un peu plus fin.
Mon problème vient du fait qu'il peut y avoir 2 écriture quasi simultanées en base ou alors une écriture et une lecture en même temps.
Or, si je teste que mon état est bon dans un webservice, et que pendant ce temps, je met à jour la même valeur pour dire que l'etat change, je risque d'avoir un conflit de concurrence.
Je sais que ça se joue à quelques millisecondes près, cependant, j'ai parfois beaucoup de connexions simultanées, et je dois essayer de prévoir pour les pires cas.

Donc mon problème est le suivant, j'ai 2 threads (2 appels webservice, ce sont d'ailleurs peut-être plutôt des process):
T1 : mise dans le panier
T2 : achat

T1 est appelé, il check l'état, l'état est ok (lecture du sémaphore : disponible)
T2 est appelé quasi simultanément, il check l'état, il est ok (lecture du sémaphore : dispo aussi)
T1 inscrit qu'il prend le sémaphore (état non disponible)
T2 inscrit aussi qu'il prend le sémaphore vu que le test plus haut était bon (état non dispo à nouveau)
T1 et T2 s'éxécutent en même temps -> conflit!

C'est donc le risque qu'il y a.
Si j'emplois une même requête de mise à jour mysql, ça peut peut être fonctionner :
Bloc de code:
update  sem where session = idsession and sem=0 #(libre)
Je pourrai ensuite tester le nombre d'enregistrements changés.
C'est un peu lourd mais bon...

Merci, je crois que j'ai une piste finalement.
 
Tu as raison de te soucier de ce problème.
Ton problème de concurrence n'es pas lié à la couche des webservices, elle vient de la base de données. Tu devrais chercher du côté des transactions ou des locks sur ta base de données ;)
 
A vrai dire, ça ne résout même pas mon problème, car la partie sql est bien atomique elle.
Le problème est plus au niveau du code php qui va effectuer une requête d'une part, et qui va tester le retour en deuxième.
La combinaison de ces 2 actions peut éventuellement poser problème.

Mais en fait, en y réfléchissant, je crois qu'il n'y a plus de soucis et que je peux faire comme ça sans problème normalement.
En effet, l'analyse des résultats de la requête sera forcément cohérente.

Donc je pense qu'on a trouvé la solution. Le seul impératif ait qu'il n'y ait bien qu'une seule requête appelée et qui fait office à la fois de mise à jour et de lecture.

Merci pour ton aide!