Attenton validate_uniqueness n’est pas garantie absolue d’unicité car
ton opération n’est pas atomique
En effet le pattern:
Requête 1: Test uniqueness => OK
Requête 2: Test uniqueness => OK
Requête 1: Insert user
Requête 2: Insert user => duplication du user
Est tout à fait possible, le seul moyen fiable de garantir l’unicité
c’est de gérer la contrainte au niveau de la base de donnée.
Ce genre de pattern d’erreur est plus probable d’arriver si ta
création de user se fait en ajax sans invalidé le bouton de submit et
que cette requête est particulièrement lente (FULL SCAN ?) car dans ce
cas là tes utilisateurs seront plus prompt à cliquer 2 fois sur submit
et comme tes requêtes sont lente le cas décrit est plus succeptible
d’arriver.
Un cas qui pourrait avoir tendance à empirer le nombre d’occurrence de
ce pattern est l’utilisation de table innodb avec des transactions. Le
commit ne se faisant qu’à la fin et si je ne m’abuse comme il n’y a
pas de table locking mais du row locking, si une 2ème requete arrive
en demandant la création du user tu es marron car comme ce n’est pas
commit le test d’unicité passe. A vérifier, j’utilise surtout du
myisam donc je dis peut-être des innepties.
J’ai pu voir qu’il n’y avait pas la gem mysql d’installée, est ce qu’il
est possible que le problème vienne de là ?
il me semble que si la gem mysql n’est pas installée , tu n’as pas de
connexion à ta base donc l’application ne fonctionne pas.
donc a priori non.
Faux, jusqu’à Rails 2.1.x tu as un connecteur bundle avec Rails. Il
est moins performant mais n’est pas buggé à ce point
là.A partir de Rails 2.2, sans gem mysql ca plantera par contre.
Est tout à fait possible, le seul moyen fiable de garantir l’unicité
c’est de gérer la contrainte au niveau de la base de donnée.
Es-tu sur que cela se produit avec l’utilisation que rails fait des
transactions SQL ?
Tu as raison de douter, mais il n’y a rien de plus simple à
vérifier
Myisam ne supporte pas les transaction, donc à moins d’utiliser les
hooks mysql (procédure, intégrité, ON DUPLICATE …) ou de locker la
table tu n’as pas de garantie que 2 requetes distinctes soient
executées de facon atomiques
Innodb supporte les transactions mais ne garantie absoluement pas
le coté atomique de plusieurs transaction distinces exemple:
Si on prend l’exemple d’une table innodb users avec un champ email
client mysql1:
$ mysql -u root database
mysql> START TRANSACTION;
Query OK, 0 rows affected (0,00 sec)
mysql> select * from users where email=‘[email protected]’;
Empty set (0,00 sec)
mysql> INSERT INTO users set email= ‘[email protected]’;
Query OK, 1 row affected (0,08 sec)
select * from users where email=‘[email protected]’;
±-------------------+
| email |
±-------------------+
| [email protected] |
±-------------------+
1 row in set (0,00 sec)
client mysql2:
mysql> START TRANSACTION;
Query OK, 0 rows affected (0,00 sec)
mysql> select * from users where email=‘[email protected]’;
Empty set (0,00 sec)
mysql> INSERT INTO users set email= ‘[email protected]’;
Query OK, 1 row affected (0,08 sec)
select * from users where email=‘[email protected]’;
±-------------------+
| email |
±-------------------+
| [email protected] |
±-------------------+
COMMIT;
Query OK, 0 rows affected (0,00 sec)
client mysql1:
COMMIT;
Query OK, 0 rows affected (0,00 sec)
select * from users where email=‘[email protected]’;
±-------------------+
| email |
±-------------------+
| [email protected] |
±-------------------+
| [email protected] |
±-------------------+
2 rows in set (0,00 sec)
Boom …
Les transactions ici empire le problème car allonge le temps où
la
base de donnée ne possède pas le users avec l’email toto alors que
pourtant il va être committé
Ce n’est pas un problème de RAILS c’est juste une impossibilité de
gérer coté soft des contraintes niveau DB, ceci dit c’est “good enough
la plupart du temps” sauf si tu as des requêtes qui prennent des
plombes à commiter.
En effet, j’ai mal fait le test et théoriquement toute base sql aura
ce comportement.
Ca existe et ca s’appelle “predicate locking” mais dans ce genre de
cas trivial pour éviter d’utiliser le LOCK, les procédures ou les
contraintes d’intégrité c’est jeter l’argent par les fenetres
Globalement c’est ni sur postgres, ni sur mysql, mais ca doit se
trouver sur DB2 ou Oracle.
Bref il faut avoir en tête les limites des validations cross records a
la Rails, en particulier en mode full transactionnel avec des after
_create gourmant.
En effet, j’ai mal fait le test et th�oriquement toute base sql aura
ce comportement.
Ca existe et ca s’appelle “predicate locking” mais dans ce genre de
cas trivial pour �viter d’utiliser le LOCK, les proc�dures ou les
contraintes d’int�grit� c’est jeter l’argent par les fenetres
Globalement c’est ni sur postgres, ni sur mysql, mais ca doit se
trouver sur DB2 ou Oracle.
Bref il faut avoir en t�te les limites des validations cross records a
la Rails, en particulier en mode full transactionnel avec des after
_create gourmant.