/ / Comment éviter les situations de concurrence lorsque vous utilisez la méthode find_or_create de DBIx :: Class :: ResultSet? - perl, postgresql, base de données, dbix-class

Comment éviter les conditions de concurrence lors de l'utilisation de la méthode find_or_create de DBIx :: Class :: ResultSet? - perl, postgresql, base de données, classe dbix

De la documentation pour find_or_create:

Remarque: Parce que find_or_create () lit dans la base de données puis éventuellement des insertions basées sur le résultat, cette méthode est soumise à une course état. Un autre processus pourrait créer un enregistrement dans la table après la recherche est terminée et avant le début de la création. Éviter Pour résoudre ce problème, utilisez find_or_create () dans une transaction.

Est-ce suffisant d’utiliser find_or_create() dans une transaction dans PostgreSQL?

Réponses:

6 pour la réponse № 1

Non, la documentation est incorrecte. Utiliser une transaction seule ne ne pas éviter ce problème. Cela garantit uniquement que toute la transaction est annulée en cas d'exception, afin qu'aucun état incohérent ne soit conservé dans la base de données.

À éviter ce problème, vous devez verrouiller la table - dans une transaction, car tous les verrous sont libérés à la fin d'une transaction. Quelque chose comme:

BEGIN;
LOCK TABLE mytbl IN SHARE MODE;

-- do your find_or_create here

COMMIT;

Mais ce n’est pas un remède magique pour tout. Cela peut devenir un problème de performance et impasses (transactions simultanées essayant mutuellement de verrouillerressources que l’autre a déjà bloquées). PostgreSQL ™ détectera une telle condition et annulera toutes les transactions concurrentes sauf une. Vous devez être prêt à relancer l'opération en cas d'échec.

Le manuel de PostgreSQL sur les verrous.

Si vous n’avez pas beaucoup de concurrence, vous pourriezaussi simplement ignorer le problème. Le créneau horaire étant très réduit, il ne se produit que très rarement. Si vous interceptez l'erreur de violation de clé en double, qui ne causera aucun préjudice, vous avez également couvert le problème.


0 pour la réponse № 2

Cette mise en œuvre de find_or_create devrait empêcher la condition de concurrence, décrite dans le PO:

eval {
$row = $self->model->create( { ... } );
}
if($@ && $@ =~ /duplicate/i) {
$row = $self->model->find( { ... } );
}

Il réduit également find_or_create() à une seule requête dans le meilleur des cas.