Éviter une double insertion en base de donnée

WRInaute impliqué
J'ai quelques anomalies sur un projet : une personne inscrite deux fois à un même évènement.

En principe, lorsqu'une demande d'inscription est faite, je vérifie d'abord dans le script PHP que la personne n'est pas déjà inscrite sur cet évènement, en interrogeant la BDD et seulement si la requête ne me retourne rien, j'insère l'inscription en BDD.

Mais là j'ai des inscriptions très rapprochées (de l'ordre du quart de seconde), et je suppose que lorsque la seconde demande est examinée, la première n'a pas encore été traitée, et donc la première comme la deuxième interrogation en BDD laissent penser que la personne n'est pas déjà inscrite.

Je ne sais pas trop comment apparaît cette double requête : un double clic éclair, un problème réseau, un navigateur qui bégaye ? sachant qu'elle est envoyée par le navigateur lui-même, pas par un script JS. Quoiqu'il en soit, ce n'est pas tout à fait isolé, j'ai détecté 3 doublons, sur environ 2000 entrées. Ça arrive rarement, mais ça arrivé suffisamment régulièrement pour que je ne puisse ignorer le problème.

Comment gérer ça ?

Créer un index unique sur le coupe id du participant / id de l'évènement ? Ça devrait bloquer toute insertion d'un doublon, et gérer côté php l'erreur générée, ou, mieux, avec INSERT IGNORE.

Utiliser une requête du style INSERT INTO … SELECT … WHERE NOT EXISTS ( SELECT … ) ?

Utiliser des verrous de table ? A priori c'est fait pour ça, mais je n'y suis guère habitué… comment sont gérés les demandes faites en parallèle, sont-elles rejetées en raison du verrou apposé par ailleurs ou bien mises en attente le temps que le verrou soit levé ?
 
WRInaute impliqué
Bin là je vois même pas ce que je pourrais désactiver en JS, puisque c'est un "vrai" POST fait par le navigateur lui-même.

La plupart des choses qu'ils proposent ne me semblent pas vraiment transposables dans mon cas : beaucoup côté client, et d'autres qui ne permettent en rien de régler le problème de concurrence (AJAX, PRG, CSRF), et les propositions côté BDD vont poser le même problème que celles que j'ai déjà.

Pour le moment j'ai changé la requête d'insertion parINSERT INTO … SELECT … WHERE NOT EXISTS ( SELECT … ) et en comptant ensuite le nombre de lignes avec mysqli_affected_rows(). Ça remplit l'objectif, nécessite peu de modification côté code, et aucune côté base de donnée.
 
WRInaute impliqué
Créer un index unique sur le coupe id du participant / id de l'évènement ? Ça devrait bloquer toute insertion d'un doublon, et gérer côté php l'erreur générée, ou, mieux, avec INSERT IGNORE.
L'indexe unique est la meilleure solution. Il permet d'utiliser le mécanisme de vérification de la base de données et garantit que tu ne peux pas avoir de doublon.
Tout le reste, c'est de l'à peu près, même ton INSERT INTO … SELECT … WHERE NOT EXISTS ( SELECT … ) est une requête qui tente d'éviter de créer des doublons, mais le stockage lui-même ne l'interdit pas : tu peux toujours créer des doublons via un phpMyAdmin, par exemple.

De plus avec un indexe unique, quelqu'un qui ne sait pas vraiment comment fonctionne ton système peut voir qu'il ne peut pas y avoir d'inscription multiple à un évènement rien qu'en voyant la table, sans avoir à regarder en plus ton code PHP.
 

➡️ Offre MyRankingMetrics ⬅️

pré-audit SEO gratuit avec RM Tech (+ avis d'expert)
coaching offert aux clients (avec Olivier Duffez ou Fabien Faceries)

Voir les détails ici

coaching SEO
Discussions similaires
Haut