J'utilise un script qui permet de mettre à jour plusieurs donnée de mon site. Cette mise à jour s'effectue toutes les 30 minutes et est déclenché par n'importe quel utilisateur de manière automatique. Il y a un compte à rebourd et dès qu'il atteint 0 le script se lance.
Le problème est que s'il y a 2 utilisateurs qui refresh en même temps le script s'exécute 2 fois.
J'ai alors tenté de protéger avec les Transactions sql et j'ai rajouté des "FOR UPDATE" dans tous les select du script mais rien ne va.
J'ai du mal à vraiment cerner le problème donc en trouver une solution complique tout.
Voici mon script (simplifie)
<?php
// require_once toutes les fonctions nécessaire
try
{
$pdo = PDO2::getInstance();
//We start our transaction.
$pdo->beginTransaction();
// Récupération tournoi actifs
$tournoi_actif_array = trans_info_bffa_tournoi_actif($pdo);
// Cloture les tournois fini
trans_bffa_tournoi_end($pdo, $tournoi_actif_array);
// Répartition des points pour chaque tournoi cloturé
foreach($tournoi_actif_array as $tournoi_id)
{
// Sélectionne que les tournois terminé parmi tous les tournois mis à jour
if ($tournoi_id['date_end'] <= date('Y-m-d H:i:s'))
{
// Info des players
$data_player = trans_info_bffa_player($pdo, $tournoi_id['id']);
// Calcul du score
$score_player = array();
$i = 0;
foreach($data_player as $key => $value)
{
$score_check = bffa_score($value, $tournoi_id['battlemin'], $tournoi_id['battlemax']);
// Si null on n'ajoute rien
if ($score_check !== null)
{
$score_player[$i]['user_id'] = $value['user_id'];
$score_player[$i]['score'] = $score_check;
$score_player[$i]['battle'] = $value['battles'];
$i++;
}
}
// Tri par score
$score_player = array_tri($score_player, 'score', SORT_DESC);
// Récupérations des points
$score = trans_bffa_points($score_player, $tournoi_id['potsize'], $tournoi_id['playermax']);
// Enregistrement des points
foreach($score as $value)
{
// Transaction points
trans_points($pdo, $value['user_id'], $value['prime'], $tournoi_id['id'], TYPE_BFFA, $value['score'], $value['battle'], $value['points']);
// Désactive le tournoi
bffa_stop_stat($tournoi_id['id'], $value['user_id']);
}
}
}
//We've got this far without an exception, so commit the changes.
$pdo->commit();
}
//Our catch block will handle any exceptions that are thrown.
catch(Exception $e)
{
//An exception has occured, which means that one of our database queries
//failed.
save_error_msg($e->getMessage());
//Rollback the transaction.
$pdo->rollBack();
}
?>
La fonction save_error_msg() enregistre la valeur de la variable dans un fichier txt (rien n'est enregistré pour info).
EDIT : le résultat est que trans_points(); est exécuté 2 fois et donc les joueurs sont récompensé 2 fois.
Je vais développé ici les fonctions
$pdo = PDO2::getInstance();
Vient de cette class :
<?php
/**
* Classe implémentant le singleton pour PDO
* @author Savageman
*/
class PDO2 extends PDO {
private static $_instance;
/* Constructeur : héritage public obligatoire par héritage de PDO */
public function __construct( ) {
}
// End of PDO2::__construct() */
/* Singleton */
public static function getInstance() {
if (!isset(self::$_instance)) {
try {
self::$_instance = new PDO(SQL_DSN, SQL_USERNAME, SQL_PASSWORD);
self::$_instance->exec("set names utf8");
} catch (PDOException $e) {
echo $e;
}
}
return self::$_instance;
}
// End of PDO2::getInstance() */
}
// end of file */
toutes les autres fonctions à part trans_bffa_points(), bffa_score() et array_tri sont des fonctions interagissant uniquement avec la DB SQL
J'utilise un script qui permet de mettre à jour plusieurs donnée de mon site. Cette mise à jour s'effectue toutes les 30 minutes et est déclenché par n'importe quel utilisateur de manière automatique. Il y a un compte a rebourd et dès qu'il atteint 0 le script se lance.
Tu pars donc du principe que tu as au moins un utilisateur sur ton site toutes les 30 minutes, c'est une mauvaise idée.
Si tu veux qu'un script se déclenche de cette manière, utilises plutôt CRON, qui ne nécessite pas l’interaction d'un utilisateur et qui par conséquent en plus d'être plus sûr, te permettra une intervalle respectée.
- Edité par Lartak 18 juillet 2019 à 15:38:47
Face a quelqu'un pour qui l'on n'éprouve que de l'aversion et du mépris, les yeux d'un homme deviennent extrêmement froids et cruels.
Il n'y a pas accès, c'est invisible pour lui. Et mon NAS plante parfois. L'utilisateur est donc la en backup. Mais comme je l'ai dis, si l'update ne s'effectue pas, ce n'est pas grave. Le script sert entre autre a actualiser le score de chaque joueur. Si le calcul est décalé, cela ne pose aucun problème car les tournois durent une journée entière (24h).
Bref, c'est la seule option que j'ai trouvé actuellement n'ayant pas de CRON.
Ce n'est pas de ce genre d'accès que je parlais, mais il ne faut pas que l'utilisateur en accédant à une page du site, que ça puisse lancer l'exécution du script.
Donc pour faire simple, il faut que le script ne puisse être exécuté que par un accès directement interne au serveur et non qu'il puisse être exécuté via une interaction de l'utilisateur, même si ce n'est qu'en se rendant à une url spécifique.
Inutile de reparler de CRON, étant donné que le NAS se charge lui même de la tâche planifiée, ce que je ne pouvais pas deviner, c'est pour ça que je t'avais parlé de CRON.
Tout portait à croire dans ce que tu as dit dans le contenu du sujet que le script n'était exécuté que par l'interaction d'un utilisateur et non via une tâche déjà planifiée.
Face a quelqu'un pour qui l'on n'éprouve que de l'aversion et du mépris, les yeux d'un homme deviennent extrêmement froids et cruels.
Il y a un compte à rebours (...) donc il doit y avoir sous une forme quelconque (un timer, une date, ..), une valeur qui indique quand le script a été lancé : il suffit donc de tester : valeur inférieure à 30 minutes je ne lance pas l'action, valeur supérieure à 30 minutes, je lance l'action. Que ce soit en automatique ou via un "truc" caché à l'utilisateur.
<?php
$filename = CHEMIN_CACHE.'bc_update.txt';
$update_actif = 1;
// Si le fichier existe on l'ouvre sinon on le créée
if (file_exists($filename))
{
$monfichier = fopen($filename, 'r+');
$date_maj = fgets($monfichier);
if ((time() - $date_maj) <= 60)
{
// Derniere maj il y a moins de 60 secondes, update inutile
$update_actif = 0;
}
}
else
{
$monfichier = fopen($filename, 'a+');
// Lit la date. format : timestamp
$date_maj = fgets($monfichier);
}
Je fait l'update uniquement si $update_actif = 1;
Le problème est qu'apparemment (c'est ce que je pense mais sans comprendre) ce script se lance parfois 2 fois exactement en même temps.
Donc comment protéger un script et éviter qu'ils ne s'exécute plusieurs fois en même temps ? (plusieurs personnes en même temps)
C'est également une question purement théorique sans forcément être associé à ce problème. Je pensais que les Transactions SQL résolverait mon problème mais ça n'a pas l'air d'être le cas.
Je ne vois pas avec le code que tu montres ce que vienent faire les transactions SQL ?????
Enfin ta variable $update_actif semble être un booléen alors pourquoi ne pas utiliser pour seules valeurs true et false qui me semblent plus parlantes que 0 et 1 .... même si tu penses que cela revient au même ou presque.
<?php
function trans_info_bffa_tournoi_actif($pdo)
{
$req = $pdo->prepare('SELECT id, tournoi_name, date_start, date_end, potsize, playermax, rating_min, rating_max, battlemin, battlemax
FROM '.PREFIXE.'bffa
WHERE tournoi_actif=1 AND date_start <= now() FOR UPDATE');
$req->execute();
return $req->fetchAll();
}
Pour $update_actif c'est vrai que je pourrais écrire true/false. J'ai pris l'habitude de garder la meme nomenclature que dans ma DB SQL qui correspond a 1/0.
Je suis d'accord que ce n'est pas exactement la même chose mais ça suffit dans ce cas.
Bloquer un script à une seule exécution simultanée
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Face a quelqu'un pour qui l'on n'éprouve que de l'aversion et du mépris, les yeux d'un homme deviennent extrêmement froids et cruels.
Face a quelqu'un pour qui l'on n'éprouve que de l'aversion et du mépris, les yeux d'un homme deviennent extrêmement froids et cruels.
Face a quelqu'un pour qui l'on n'éprouve que de l'aversion et du mépris, les yeux d'un homme deviennent extrêmement froids et cruels.