[Résolu] mysql_fetch_aray plante aléatoirement



  • Bonjour,

    Alors voilà, je viens à vous avec un problème assez pénible, parce qu'il n'est pas reproductible.

    Le problème, c'est qu'une fois de temps en temps (environ 5 fois par jour sur une requête en particulier), l'une ou l'autre requête affiche "… is not a valid MySQL resource in welcome.php on line X". (sur un mysql_fetch_array).

    J'en ai parlé au support, et ils voient 3 possibilités :

    • Le moteur MyISAM a du mal (mais j'utilise InnoDB sur toutes mes tables)
    • Le max_connect est atteint (mais alors ce serait une erreur de type "too many connections")
    • J'essaie d'écrire dans une table alors que l'opération précédente n'est pas finie (peu probable, parce que même avec des requêtes de type "SELECT * FROM member", ça me le fait, et, en plus, je protège les accès concurrents avec des LOCK TABLE (ce qui n'est pas le plus performant, je l'accorde)).

    Bref, je voudrais savoir si, d'une part, ça vous est déjà arrivé (et, si oui, comment vous avez résolu le problème).

    Et sinon, comment pourrais-je gérer ça, sachant que j'ai un code du genre :

    $result = mysql_query("SELECT ...");
    while($row = mysql_fetch_array($result))
    {
    //do something
    }

    Voilà, j'espère que vous pourrez m'aider,

    Bonne journée ;)



  • Tout dépend de ce qu'il sélectionne, si on a pas le code précit.. sa va être dur..



  • Bon, pour donner un exemple précis, voici :

    $query = "SELECT COUNT(*) FROM pubs WHERE HTML LIKE '%idpub=" . $pubs[$i][$IdPubIndex][1] . "&pseudo%'";
    $result = mysql_query($query); //<- Ligne 109
    $row = mysql_fetch_array($result); // <- Ligne 110

    (on sent la requête complexe ;) )

    Dans $pubs[$i][$IdPubIndex][1], il y a une valeur numérique.

    Et là, boum :

    Warning: mysql_query() [function.mysql-query]: Unable to save result set in /home/hiakabe/www/updateCampaignList.php on line 109

    Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in /home/hiakabe/www/updateCampaignList.php on line 110

    Mais pas tout le temps, évidemment, juste une fois de temps en temps (et pas tout le temps cette requête-là, ça peut être un truc aussi simple que "SELECT COUNT(*) FROM member WHERE Etat = 1"), bref, n'importe quelle requête est susceptible de me faire un coup comme ça à n'importe quel moment, et il suffit que je recharge la page pour que la requête passe comme une fleur.

    Toutes mes requêtes sont encapsulées dans un LOCK TABLE machin WRITE, truc WRITE …UNLOCK TABLES (je ne sais pas si ça a de l'importance ou pas)



  • Vérifie tu que ta requête retourne bien quelques choses ?

    En informatique, il n'y a rien qui "Plante aléatoirement", il y a toujours une cause ou un bug. Ici, je commencerais a stocker les résultat et les erreurs pour voir sur quel type de requête ça bug.



  • Normalement oui. Même si la table est vide, le COUNT(*) doit renvoyer 0, non?

    Et sinon, comment je peux savoir que mysql_query ne peut pas sauver le result set? Ca renvoie false? (désolé, j'ai pas l'habitude de devoir gérer le résultat d'un mysql_query mis à part pour le passer dans un mysql_fetch_array).



  • $result = mysql_query($query) or die('Erreur MYSQL <br />'. mysql_error()); //<- Ligne 109
    


  • Une query (quelque soit la requête SQL) qui n'aboutit pas ou n'envoie aucun résultat, renvoie FALSE (ou un équivalent (null ou autre)), sauf si elle est suivie d'un die() (cf. l'exemple de game-war).



  • Ok, merci, je vais tester ça et je vous dis quoi. (pardon, réponse tardive, mais 15 août festif ;) )



  • Donc, ça avance, j'ai une info cruciale. A chaque mysql_query, si elle renvoie false, je stocke la query, l'errno et l'erreur.

    Et à chaque fois, j'ai : Deadlock found when trying to get lock; try restarting transaction (Errno 1213).

    Et sur plus ou moins n'importe quelle requête (select, update, et même lock).

    Donc voilà, ça m'avance, mais pas tant que ça. Au moins, je sais que ça bugge à cause d'un deadlock. Ceci dit, je ne vois pas comment c'est possible, vu que je fais un lock (sur toutes les tables que j'utilise) et un unlock après coup. Peut-être que, ce qui se passe, c'est que parfois les gens viennent, le lock se fait, mais ils rechargent (ou coupent) la page avant le unlock et du coup, ben j'ai un deadlock.

    Je vois plus ou moins comment résoudre le problème (du genre, si mysql_query renvoie false, relancer la requête), mais c'est peut-être un peu bourrin et ce serait mieux de savoir d'où vient le deadlock.

    Ma proposition vous semble-t-elle plausible? Comment rêgleriez-vous le problème?

    Merci


  • Administrateur

    Te voilà mieux avancé.
    Je t'invite à lire cet article provenant de la doc officielle :
    http://dev.mysql.com/doc/refman/5.0/en/ … locks.html

    Le deadlock est un problème courant sur le moteur InnoDB. Comme quoi, je n'étais pas fou en t'indiquant que la table devait être probablement protégé contre les écritures / lectures au moment des requêtes en erreur.

    Tu trouveras sur la doc un certain nombre d'infos pouvant t'aider à résoudre le problème a l'aide de vérification préliminaire.



  • Ok, merci, je vais potasser ça, mais j'ai déjà vu un truc qui pourrait expliquer.

    Je fais UNLOCK TABLES puis COMMIT, alors que je devrais faire l'inverse. Enfin, je vais tester et je vous dirai si j'ai encore des problèmes après.

    Merci ;)



  • Bon, ça ne marche toujours pas. J'ai bien suivi l'exemple donné dans la doc mysql

    SET autocommit=0;
    LOCK TABLES t1 WRITE, t2 READ, …;
    ... do something with tables t1 and t2 here ...
    COMMIT;
    UNLOCK TABLES;

    mais j'ai toujours autant de deadlocks. Je n'ai donc plus qu'à me rabattre sur :

    Always be prepared to re-issue a transaction if it fails due to deadlock. Deadlocks are not dangerous. Just try again.

    Le problème, c'est que mes transactions contiennent parfois beaucoup de requêtes, et qu'à la limite, je préfèrerais carrément annuler la transaction et demander à l'utilisateur de relancer plutôt que la relancer automatiquement par script.

    Est-ce qu'un truc du genre :

    $result = mysql_query($query);
    if($result == false)
    {
    mysql_query("ROLLBACK");
    die("Erreur SQL. Veuillez recharger la page, svp");
    }

    pourrait fonctionner, sachant donc que j'utilise le modèle ci-dessus (je ne commence jamais explicitement une transaction, puisque LOCK n'est pas compatible avec).

    D'ailleurs, et je ne l'ai pas vu dans la doc, qu'est-ce qui se passe en case d'erreur deadlock au niveau de la transaction? Bon, a priori, il libère un des lock (sinon, par définition du deadlock, on reste coincé pour toujours), mais il va continuer après les autres requêtes normalement? Si oui, c'est assez catastrophique, vu que parfois il coince sur un LOCK (qui protège justement des incohérences lors d'accès concurrents), et s'il continue ses requêtes sans plus se tracasser du LOCK, ça va être assez problématique...



  • Mais en faite, tu essaie de faire quoi exactement ?



  • Tu t'embête pas un petit peu avec tous tes lock, unlock…etc ? sa me parait vraiment tiré par les cheveux tout sa.



  • Ben c'est juste que j'ai besoin de vraiment m'assurer que les accès concurrents sont gérés.

    Si j'ai 2 utilisateurs qui lisent l'état de la BD, qu'ils voient qu'il faut faire une mise à jour, et que les 2 lancent la mise à jour en même temps, et bien je vais finir par avoir une mise à jour de trop (parfois sans importance, parfois catastrophique (c'est comme le cas du "select … update +1" qui donne +2 s'il n'y a pas de lock)).

    Bref, j'ai besoin de lock. Ça, il n'y a même pas à discuter. Si c'était un petit site qui peut se permettre d'avoir quelques erreurs parfois, ça irait, sauf que, même si c'est pas google ou amazon, c'est quand même un site relativement complexe en programmation (j'en suis à plus d'un million de caractères (on peut dire des dizaines de milliers de lignes de code)), sans vouloir me faire mousser (je reste quand même un noob sur certains trucs), et je ne peux pas me permettre d'avoir 2 ajouts au lieu d'un.

    Bref, pour en revenir au sujet, pensez-vous qu'un rollback en cas d'erreur règlerait le problème?



  • Le rollback fonctionne, ce qui m'a permis de voir un autre problème que j'avais (et je le mets, ça peut servir à d'autres (quoi que, il semble que je sois un des rares à utiliser les lock ;) )

    Quand je faisais mes transactions avec start, il fallait que je précise, dans le lock, toutes les tables que la query utilise, mais si on croise une table avec elle-même, il fallait le mettre 2 fois.

    Puisque j'ai retiré cette instruction (les lock n'étant pas compatibles avec les transactions, en fait, je l'ai appris récemment), il n'y avait plus de transactions (au sens premier du terme, car mon code reste quand même exécuté entièrement ou pas du tout, grâce au rollback), et dans ce cas, on ne lock une table qu'une fois, même en cas de croisement avec elle-même.

    Bref, ça fonctionne maintenant, et merci pour le coup de main ;)


Se connecter pour répondre
 

Il semble que votre connexion ait été perdue, veuillez patienter pendant que nous vous re-connectons.