Partage
  • Partager sur Facebook
  • Partager sur Twitter

Requêtes SQL double

    1 avril 2018 à 16:34:29

    Bonjour,

    Je me prends la tête depuis plusieurs mois, sur un bug aléatoire. De temps à autres j'ai des requêtes qui s'effectuent deux fois. La raison j'en vois pas, à par que deux utilisateur actualise la page en même temps...

    J'ai simplifié le code, mais ce que j'ai détecté s'est que la boucle while est lancé plusieurs fois simultanément.

    Ce qui est incompréhensible pour moi s'est que dans la fonction MissionAttaque et MissionTransport je supprime la flotte et que je vois que dans mes logs que la boucle lancé en parallèle la trouve toujours... Alors que j'ai une transaction...

    <?php
    
    try {
    	$options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
    	$serveur = "localhost";
    	$bdd = new PDO("mysql:host=".$serveur.";dbname=dbtest", 'root', '', $options);
    	$prefixe = "test";
    }
    
    catch (Exception $e){
    	die('Erreur : ' . $e->getMessage());
    }
    
    
    if(!empty($_GET['page'])){
    	$page = $_GET['page'];
    } else{
    	$page = 'planete';
    }
    
    
    try{
    
    		if ($user['idjoueur'] != ""){ // Si joueur est connecté
    
    			$bdd->beginTransaction();
    			$messageAction = "DEBUG106-2051 fleetsactu";
    			$bdd->query("INSERT INTO {$prefixe}__actions SET `idjoueur1` = '".$user['idjoueur']."', `idjoueur2` = '0', `categorie` = '9', `description` = '".$messageAction."', `date` = '".time()."' ;" );
    
    			$messageAction = "DEBUG15-2045 FlyingFleetHandler";
    			$bdd->query("INSERT INTO {$prefixe}__actions SET `idjoueur1` = '".$user['idjoueur']."', `idjoueur2` = '0', `categorie` = '9', `description` = '".$messageAction."', `date` = '".time()."' ;" );
    	
    			$fleetquery = $bdd->query("SELECT * FROM {$prefixe}__fleets WHERE ((`fleet_start_time` <= '".time()."') OR (`fleet_end_time` <= '".time()."'));");
    
    
    			while ($CurrentFleet = $fleetquery->fetch(PDO::FETCH_ASSOC)) {
    		
    				$calculDesPoints = false;
    			
    				$messageAction = "DEBUG25-2044 Mission".$CurrentFleet['fleet_mission']." flotte/troupe:".$CurrentFleet['fleet_id']." .. ".$CurrentFleet['fleet_mess']." WHILE";
    				$bdd->query("INSERT INTO {$prefixe}__actions SET `idjoueur1` = '".$CurrentFleet['fleet_owner']."', `idjoueur2` = '".$user['idjoueur']."', `categorie` = '9', `description` = '".$messageAction."', `date` = '".time()."' ;" );
    
    				switch ($CurrentFleet["fleet_mission"]) {
    					case 1:
    						MissionAttaque ( $CurrentFleet );
    						$calculDesPoints = true;
    						break;
    
    					case 2:
    						MissionTransport ( $CurrentFleet );
    						$calculDesPoints = true;
    						break;
    				}
    
    				$messageAction = "DEBUG145-1812 END FlyingFleetHandler";
    				$bdd->query("INSERT INTO {$prefixe}__actions SET `idjoueur1` = '".$user['idjoueur']."', `idjoueur2` = '0', `categorie` = '9', `description` = '".$messageAction."', `date` = '".time()."' ;" );
    
    
    				$bdd->commit();
    
    				$bdd->query("UPDATE {$prefixe}__joueurs SET `connexion` = '". time()."', `inactifemailnb` = '0' WHERE `idjoueur` = '{$user['idjoueur']}';");
    				$planete = $bdd->prepare("SELECT * FROM {$prefixe}__planetes WHERE `idplanetes` = ?;");
    				$planete->execute(array($user['planetecourante']));
    				$planete = $planete->fetch(PDO::FETCH_ASSOC);
    
    				if (!empty($_GET['page']) && is_file('controleurs/'.$page.'.php')){
    					include 'controleurs/'.$page.'.php';
    				} else{
    					include 'controleurs/planete.php';
    				}
    
    			} 
    		}
    
    } catch (PDOException $erreur) {
    	//	$bdd->rollback();
    	echo "<br><br><font color=red>Code de l'erreur : " .$erreur. "</font>";
    }
    
    
    
    $bdd = null;
    
    ?>



    • Partager sur Facebook
    • Partager sur Twitter
      1 avril 2018 à 18:02:13

      Salut,

      Tu termines la transaction avant le dernier UPDATE => moyen ta base n'est plus dans un état cohérent.

      Les transactions sont utilisées sur TOUS les UPDATE/INSERT/DELETE ou sur aucun.

      Corrige déjà cela et vois ce qu'il se passe.

      -
      Edité par christouphe 1 avril 2018 à 18:29:24

      • Partager sur Facebook
      • Partager sur Twitter
        1 avril 2018 à 18:18:31

        @

        christouphe a écrit:

        Salut,

        déjà tu as deux fois la même requête UPDATE:

                    $bdd->query("INSERT INTO {$prefixe}__actions 
        

        Update tu es sûr :)
        T.
        • Partager sur Facebook
        • Partager sur Twitter
          1 avril 2018 à 18:28:58

          erff ^^

          fatigué, merci ;) je corrige

          • Partager sur Facebook
          • Partager sur Twitter
            2 avril 2018 à 0:28:12

            christouphe a écrit:

            Les transactions sont utilisées sur TOUS les UPDATE/INSERT/DELETE ou sur aucun.


            Une transaction applique ou annule l'ensemble des ordres sql/dml qu'elle "embarque" .. la règle est donc d'ouvrir une transaction pour un lot d'ordres liés dans une logique de tout ou rien.

            Dans le code plus haut c'est donc étrange d'ouvrir une transaction avant un while et faire un commit à l'intérieur de la boucle.

            T.

            -
            Edité par Tracker 2 avril 2018 à 0:29:02

            • Partager sur Facebook
            • Partager sur Twitter
              22 mai 2018 à 20:43:23

              Bonjour,

              veuillez m'excusez du temps de réponse. J'ai testé ceci :

              <?php
               
              try {
                  $options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
                  $serveur = "localhost";
                  $bdd = new PDO("mysql:host=".$serveur.";dbname=dbtest", 'root', '', $options);
                  $prefixe = "test";
              }
               
              catch (Exception $e){
                  die('Erreur : ' . $e->getMessage());
              }
               
               
              if(!empty($_GET['page'])){
                  $page = $_GET['page'];
              } else{
                  $page = 'planete';
              }
               
               
              try{
              	$bdd->beginTransaction();
                      if ($user['idjoueur'] != ""){ // Si joueur est connecté
               
                          $bdd->beginTransaction();
                          $messageAction = "DEBUG106-2051";
                          $bdd->query("INSERT INTO {$prefixe}__actions SET `idjoueur1` = '".$user['idjoueur']."', `idjoueur2` = '0', `categorie` = '9', `description` = '".$messageAction."', `date` = '".time()."' ;" );
               
                          $fleetquery = $bdd->query("SELECT * FROM {$prefixe}__fleets WHERE ((`fleet_start_time` <= '".time()."') OR (`fleet_end_time` <= '".time()."'));");
               
               
                          while ($CurrentFleet = $fleetquery->fetch(PDO::FETCH_ASSOC)) {
                       
                              $calculDesPoints = false;
                           
                              $messageAction = "DEBUG25-2044 Mission".$CurrentFleet['fleet_mission']." flotte/troupe:".$CurrentFleet['fleet_id']." .. ".$CurrentFleet['fleet_mess']." WHILE";
                              $bdd->query("INSERT INTO {$prefixe}__actions SET `idjoueur1` = '".$CurrentFleet['fleet_owner']."', `idjoueur2` = '".$user['idjoueur']."', `categorie` = '9', `description` = '".$messageAction."', `date` = '".time()."' ;" );
               
                              switch ($CurrentFleet["fleet_mission"]) {
                                  case 1:
                                      MissionAttaque ( $CurrentFleet );
                                      $calculDesPoints = true;
                                      break;
               
                                  case 2:
                                      MissionTransport ( $CurrentFleet );
                                      $calculDesPoints = true;
                                      break;
                              }
               
                              $messageAction = "DEBUG145-1812";
                              $bdd->query("INSERT INTO {$prefixe}__actions SET `idjoueur1` = '".$user['idjoueur']."', `idjoueur2` = '0', `categorie` = '9', `description` = '".$messageAction."', `date` = '".time()."' ;" );
               
               
                              $bdd->query("UPDATE {$prefixe}__joueurs SET `connexion` = '". time()."', `inactifemailnb` = '0' WHERE `idjoueur` = '{$user['idjoueur']}';");
                              $planete = $bdd->prepare("SELECT * FROM {$prefixe}__planetes WHERE `idplanetes` = ?;");
                              $planete->execute(array($user['planetecourante']));
                              $planete = $planete->fetch(PDO::FETCH_ASSOC);
               
                              if (!empty($_GET['page']) && is_file('controleurs/'.$page.'.php')){
                                  include 'controleurs/'.$page.'.php';
                              } else{
                                  include 'controleurs/planete.php';
                              }
               
                          }
                      }
              	$bdd->commit();
              } catch (PDOException $erreur) {
                    $bdd->rollback();
                  echo "<br><br><font color=red>Code de l'erreur : " .$erreur. "</font>";
              }
              
               
              $bdd = null;
               
              ?>

               Je me répète mais ce qui est incompréhensible pour moi s'est que dans la fonction MissionAttaque et MissionTransport je supprime la flotte (la ligne de la table) et que je vois que dans mes logs que la boucle lancé en parallèle la trouve toujours... Alors que j'ai une transaction...

              J'ai souvent régulièrement l'erreur :

              PDOException: SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction in miseajour.php:287
              Stack trace:
              #0 /home/miseajour.php(287): PDOStatement->execute()

              La duplication est aléatoire, j'ai l'impression que cela arrive quand il y a un peu de charge sur le serveur, possible ? je suis en mutualisé sur OVH pour infos en php 7.0.

              Merci

              • Partager sur Facebook
              • Partager sur Twitter
                23 mai 2018 à 7:52:12

                Re tu es allé voir ce que veut dire deadlock ? car c'est assez explicite comme anglais ;)

                EDIT: quelle ligne est le 287 sur le code ci-dessus ?

                -
                Edité par christouphe 23 mai 2018 à 7:59:59

                • Partager sur Facebook
                • Partager sur Twitter
                  23 mai 2018 à 12:37:31

                  Bonjour, d'après ce que j'ai compris s'est que des requêtes qui essaye d'accéder à une table deja verrouillé, mais ce problème me gène pas. Le problème qui me gêne sont les doublons. Comme j'ai un rollback , sa ne devrait pas avoir d'influence si j'ai bien compris.
                  • Partager sur Facebook
                  • Partager sur Twitter
                    23 mai 2018 à 13:04:41

                    lol un deadlock qui te gène pas mais qui gènerai 99% des pros

                    Bon je reprends sur quelle ligne le deadlock ?

                    • Partager sur Facebook
                    • Partager sur Twitter
                      23 mai 2018 à 18:21:13

                      Oui quand je dis que le deadlock  ne me gêne pas, s'est que je veux pas vous importuné avec celui-ci, je peu regardé par moi même. Le dealock vient d'une page qui est inclut sur un update ou insert statjoueurs.

                      • Partager sur Facebook
                      • Partager sur Twitter
                        23 mai 2018 à 19:28:42

                        normal tu ne dois pas faire d'insert/update sur la même table dans la même transaction, de tête mysql locke la table mais tu peux descendre à l'enregistrement en changeant pour Posgre (de tête)
                        • Partager sur Facebook
                        • Partager sur Twitter
                          23 mai 2018 à 20:37:14

                          Je vais séparé en deux transactions, ne pas l'inclure dans la transaction... Mais ceci peut avoir un rapport avec mais doublons ?
                          • Partager sur Facebook
                          • Partager sur Twitter
                            23 mai 2018 à 20:39:24

                            Tu dis que les requêtes s'effectuent 2 fois aléatoirement ??

                            mais la(les)quelle(s)

                            • Partager sur Facebook
                            • Partager sur Twitter
                              23 mai 2018 à 21:11:46

                              christouphe a écrit:

                              normal tu ne dois pas faire d'insert/update sur la même table dans la même transaction, de tête mysql locke la table mais tu peux descendre à l'enregistrement en changeant pour Posgre (de tête)

                              Comment tu fais dans ce cas une transaction entre deux compte par exemple ?

                              Début transaction
                              
                              Ajoute a Monsieur X 10€ dans la table compte
                              
                              Retire a Monsieur Y 10€ dans la table compte
                              
                              Fin de transaction

                              Cela fait bien deux requête dans la même table.

                              En fait mais doubons ce font aléatoirement et un peu dans tout. S'est un jeu de statégie spatial, le problème peut venir à l'envois de la flotte, au retour, à la création d'une planète...

                              Pour empêcher le doublon à l'envois j'ai mis une clé unique sur un timestamp avec l'ID du joueur. J'ai les remonté d'erreur mais sa permet d'éviter les flottes en double.

                              J'ai mis des requêtes pour insérer dans actions les différents évènement, et pour moi j'ai l'impression que la page est exécuté deux fois et le timestamp peut être identique ou varié à 3 secondes.




                              • Partager sur Facebook
                              • Partager sur Twitter
                                23 mai 2018 à 21:16:14

                                Dans le cas d'une transaction bancaire, ce sont 2 UPDATE successifs sur la même table, normalement il n'y a pas de problème je l'ai déjà fait avec l'API mysql_* et les transactions à l'époque. Mais là tu fais un UPDATE et un INSERT donc un peu différent ;)

                                Fait un test dans une table InnoDB pour voir (les deux cas UU e UI) ;)

                                EDIT:

                                Avec ce code pas de problème:

                                    try {
                                        $options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
                                        $bdd = new PDO("mysql:host=localhost;dbname=test_transac", 'root', 'root', $options);
                                    
                                        echo 'TEST 1 U / U<br>';
                                        $req0 = "SELECT * FROM transac";
                                        $stmt0 = $bdd->query($req0);
                                        echo '<table>';
                                        while ($data = $stmt0->fetch()) {
                                            echo '<tr>';
                                            foreach ($data AS $field) {
                                                echo '<td>'.$field.'</td>';
                                            }
                                            echo '</tr>';
                                        }
                                        echo '</table>';
                                        $bdd->beginTransaction();
                                        $req1 = "UPDATE transac SET solde = solde - 30 where id = 1";
                                        $stmt1 = $bdd->exec($req1);
                                        $req2 = "UPDATE transac SET solde = solde + 30 where id = 2";
                                        $stmt2 = $bdd->exec($req2);
                                        $bdd->commit();
                                    } catch (PDOException $e) {
                                        if (!is_null($bdd)) {
                                            $bdd->rollBack();
                                        }
                                        echo '<br/>'.$e->getMessage();
                                    } catch (Exception $e) {
                                        if (!is_null($bdd)) {
                                            $bdd->rollBack();
                                        }
                                        echo '<br/>'.$e->getMessage();
                                    }
                                    $req3 = "SELECT * FROM transac";
                                    $stmt3 = $bdd->query($req3);
                                    echo '<table>';
                                    while ($data = $stmt3->fetch()) {
                                        echo '<tr>';
                                        foreach ($data AS $field) {
                                            echo '<td>'.$field.'</td>';
                                        }
                                        echo '</tr>';
                                    }
                                    echo '</table>';
                                    
                                    echo '<hr>';
                                    
                                    try {        
                                        echo 'TEST 2 U / I<br>';
                                        $req0 = "SELECT * FROM transac";
                                        $stmt0 = $bdd->query($req0);
                                        echo '<table>';
                                        while ($data = $stmt0->fetch()) {
                                            echo '<tr>';
                                            foreach ($data AS $field) {
                                                echo '<td>'.$field.'</td>';
                                            }
                                            echo '</tr>';
                                        }
                                        echo '</table>';
                                        $bdd->beginTransaction();
                                        $req1 = "UPDATE transac SET solde = solde - 30 where id = 1";
                                        $stmt1 = $bdd->exec($req1);
                                        $req2 = "INSERT INTO transac (nom,prenom,solde) VALUES ('C','C',100)";
                                        $stmt2 = $bdd->exec($req2);
                                        $bdd->commit();
                                    } catch (PDOException $e) {
                                        if (!is_null($bdd)) {
                                            $bdd->rollBack();
                                        }
                                        echo '<br/>'.$e->getMessage();
                                    } catch (Exception $e) {
                                        if (!is_null($bdd)) {
                                            $bdd->rollBack();
                                        }
                                        echo '<br/>'.$e->getMessage();
                                    }
                                    $req3 = "SELECT * FROM transac";
                                    $stmt3 = $bdd->query($req3);
                                    echo '<table>';
                                    while ($data = $stmt3->fetch()) {
                                        echo '<tr>';
                                        foreach ($data AS $field) {
                                            echo '<td>'.$field.'</td>';
                                        }
                                        echo '</tr>';
                                    }
                                    echo '</table>';
                                ?>



                                la table:

                                --
                                -- Base de données :  `test_transac`
                                --
                                
                                -- --------------------------------------------------------
                                
                                --
                                -- Structure de la table `transac`
                                --
                                
                                CREATE TABLE `transac` (
                                  `id` int(11) UNSIGNED NOT NULL,
                                  `nom` varchar(50) NOT NULL,
                                  `prenom` varchar(50) NOT NULL,
                                  `solde` int(11) NOT NULL
                                ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
                                
                                --
                                -- Contenu de la table `transac`
                                --
                                
                                INSERT INTO `transac` (`id`, `nom`, `prenom`, `solde`) VALUES
                                (1, 'A', 'A', 40),
                                (2, 'B', 'B', 130);
                                
                                --
                                -- Index pour les tables exportées
                                --
                                
                                --
                                -- Index pour la table `transac`
                                --
                                ALTER TABLE `transac`
                                  ADD PRIMARY KEY (`id`);
                                
                                --
                                -- AUTO_INCREMENT pour les tables exportées
                                --
                                
                                --
                                -- AUTO_INCREMENT pour la table `transac`
                                --
                                ALTER TABLE `transac`
                                  MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;



                                -
                                Edité par christouphe 23 mai 2018 à 21:35:43

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  23 mai 2018 à 21:24:47

                                  Oui, insert ou update pas les deux, je sais pas car s'est une requête concaténé.

                                  Les tables était en MyISAM je les ai passé en InnoDB, il y a quelques temps pour ce problème, mais sans résultat.

                                  Edit :

                                  bdd

                                  Je viens de trouver ceci dans mes logs, 9412207 et une clé primaire AI. Et avec sa je m'arrache les cheveux, si sa continue comme ça je vais avoir la même calvitie que mon père

                                  -
                                  Edité par JérômeBernard3 23 mai 2018 à 21:36:37

                                  • Partager sur Facebook
                                  • Partager sur Twitter

                                  Requêtes SQL double

                                  × 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.
                                  • Editeur
                                  • Markdown