Bonjour,
Je n'ai pas testé votre cas, mais vu que nous sommes sur du mutualisé, ce que je vois qu'il se passe :
- Accès au script via un host (disons : webm210), pose de votre "semaphore". Une semaphore est posée par le kernel en mémoire.
- Accès à nouveau au script via un autre host (disons: webm0300), pas de semaphore en mémoire sur ce host.
Une solution serait de jouer avec la présence ou non du fichier.
- Le fichier n'existe pas, le créer, et continuer. Ne pas oublier de supprimer le fichier une fois l'operation faite.
- Le fichier existe, on est lock, on attend ou on quitte.
Je n'ai pas testé votre cas, mais vu que nous sommes sur du mutualisé, ce que je vois qu'il se passe :
Accès au script via un host (disons : webm210), pose de votre "semaphore". Une semaphore est posée par le kernel en mémoire.
Accès à nouveau au script via un autre host (disons: webm0300), pas de semaphore en mémoire sur ce host.
C'est donc ce que je pressentais, mais que j'ai mal exprimé.
De plus, en mutualisé, je ne suis pas sûr que deux lancements de scripts travaillent sur le même espace.
Bonjour,
merci pour votre réponse. (Rah je ne pouvais pas répondre car j'avais trop posté pour mon premier jour !)
Notez que pour la sémaphore, au début ça marchait bien, et à un moment donnée, ça m'a sorti l'erreur comme quoi il n'y a plus de mémoire. J'ai essayé de trouver un autre ID qui soit libre et ça a marché à nouveau avec cet ID pour un certain temps, puis même problème.
Je veux bien mais pour votre solution, comment implémentez-vous le fait qu'on "attend" si le fichier existe ? Le fait que "flock" soit bloquant est la propriété que je recherche.
@ LouisD, comme la précisé Ludovic d'OVH, le système d'hébergement mutualisé OVH est différent du cas d'un serveur dédié qui a un environnement unique.
D'après ce que cru comprendre, il y a plusieurs serveurs "Apache" webm… qui gèrent les requêtes entrantes.
Le "flock" ne serait pas "accroché" au fichier sur le cluster xxx, mais conservé en mémoire de ce webm… .
* Si tes DEUX requêtes arrivent sur le même webm… le système de "flock" fonctionnera bien.
* Si tes DEUX requêtes arrivent sur des webm… différents le système de "flock" ne fonctionnera plus du tout, car les informations "flock" se trouvera sur l'autre webm… .
Mmh d'accord, c'est probablement ça.. Donc pas moyen de faire fonctionner flock en mutualisé de manière fiable j'imagine ?
Je vais probablement implémenter qqch moi-même qui va boucler tant que la ressource n'est pas libérée mais bon.. ![]()
J'aurais bien aimé savoir comment @Ludo.H aurait fait pour "attendre" ?
Le fichier existe, on est lock, on attend ou on quitte.
Bon, j'ai donc implémenté ma propre sémaphore avec un fichier comme ceci.
(Notez qu'elle ne correspond pas à la définition classique d'une sémaphore en interne car ici 0 signifie "libre", 1 signifie "locked", et 2 signifie "invalid", permettant un petit système de récupération d'erreur "à l'arrache".)
Version de base toute simple pour le principe : (il faut un fichier semaphore.txt initialisé à 0)
function semAcquire() {
for($i = 0; intval(file_get_contents("semaphore.txt")); $i++) {
usleep(8000+rand(0,2000)); // Pour éviter deux lectures simultanées de semaphore.txt
}
file_put_contents("semaphore.txt", 1);
}
function semRelease() {
file_put_contents("semaphore.txt", 0);
}
Version avec TimeOut et error recovery (dans le cas où, par erreur ou je ne sais quelle raison, la sémaphore ne serait pas relâchée).
// Mes propres sémaphores puisque les sémaphores php ne marchent pas très bien en mutualisé…
define("SEM_MAX_WAIT", 600);
function semAcquire() {
global $db;
if(intval(file_get_contents("semaphore.txt"))==2) { // Recovery
file_put_contents("semaphore.txt", 0);
$db->log("Semaphore recovery.\n", 0);
}
for($i = 0; intval(file_get_contents("semaphore.txt")) && $i usleep(8000+rand(0,2000)); // Pour éviter deux lectures simultanées de semaphore.txt
}
if($i==SEM_MAX_WAIT) {
file_put_contents("semaphore.txt", 2);
$db->log("semAcquire timeout!\n", 0);
die("Couldn't acquire semaphore.");
}
file_put_contents("semaphore.txt", 1);
}
function semRelease() {
file_put_contents("semaphore.txt", 0);
}
L'utilisation recommandée est la suivante :
semAcquire();
try {
// Partie critique
} catch(Exception $e) {
// Log le message qq part peut être utile
}
semRelease();
Ainsi, théoriquement, même si le script plante, on relâche la sémaphore, mais j'ai quand même un petit système de recovery.
Le principe de semAcquire est le suivant :
En temps normal, semaphore.txt contient 0.
- Un process vient, lit la semaphore, elle est à 0 donc il l'a met à 1 et entre la partie critique.
- Un deuxième process vient, lit la sémaphore qui est à 1 et donc boucle jusqu'à ce que : soit elle soit relâchée (remise à 0) soit jusqu'à ce que SEM_MAX_WAIT itérations soit atteint.
=> Si on timeout, on met la sémaphore à 2 et on quitte.
L'idée est que si on a timeout à cause d'un burst de requêtes simultanées, ça ne mette pas en péril la partie critique car la sémaphore étant à 2 != 0, les process en attente continuent d'attendre, et le prochain à entrer la partie critique va la remettre à 1.
Par contre si la sémaphore est "cassée" parce-qu'elle n'a jamais été relâchée :
Un premier process entre et attend jusqu'à timeout, et met la sémaphore à 2.
La prochaine requête remettra la sémaphore à 0 et tout rentre dans l'ordre.
Alors évidemment, c'est POSSIBLE de mettre à mal le système mais pour ça il faut :
Un process en cours (sem=1) et au moins un autre qui attend.
Un process qui attend timeout
Une nouvelle requête arrive AVANT que le process en cours ne termine.
=> Dans ce cas, la partie critique sera entrée alors que le premier process y est encore.
M'enfin, dans mon cas, c'est assez improbable il me semble.. (Je sens la loi de Murphy qui va me tomber dessus maintenant hihi)
Bon, voilà, en tous cas ça semble très bien fonctionner, le truc qui me dérangeait par rapport à flock ou à sem_acquire de php, est que là, les process en attente ne sont pas mis en pause, ils travaillent quand même ce qui potentiellement ralentit le process dans la partie critique et donc augmente l'effet de bottleneck dû à ma partie critique.. Mais en fait, puisque j'utilise usleep (j'y avais pas pensé au début, et en fait c'est essentiel sinon plusieurs process en attente risquent de lire en même temps que la sémaphore est relâchée et peuvent ainsi entrer simultanément la partie critique !), ça revient à les mettre en pause pour la plupart du temps au final !
Merci pour l'aide !
Astucieux l'état 2 et le time-out. ![]()
Cette solution ne fonctionne pas dans tous les cas.
Elle n'évite pas que un fil ouvre le fichier et lit la valeur 0 puis que un autre fil fasse la même chose avant que la valeur 1 ne soit écrite.
Voir une solution avec mkdir() ici: https://stackoverflow.com/questions/51155848/php-flock-for-read-modify-write-does-not-work