PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

PostgreSQL et ActiveRecord sous-sélectionnent pour la condition de concurrence

Vos options sont :

  • Exécuter en SERIALIZABLE isolation. Les transactions interdépendantes seront abandonnées lors de la validation en raison d'un échec de sérialisation. Vous recevrez beaucoup de spam dans le journal des erreurs et vous ferez de nombreuses tentatives, mais cela fonctionnera de manière fiable.

  • Définir un UNIQUE contrainte et réessayer en cas d'échec, comme vous l'avez noté. Mêmes problèmes que ci-dessus.

  • S'il existe un objet parent, vous pouvez SELECT ... FOR UPDATE l'objet parent avant de faire votre max requête. Dans ce cas, vous devez SELECT 1 FROM bar WHERE bar_id = $1 FOR UPDATE . Vous utilisez bar comme un verrou pour tous les foo s avec ce bar_id . Vous pouvez alors savoir que vous pouvez continuer en toute sécurité, tant que chaque requête qui effectue l'incrémentation de votre compteur le fait de manière fiable. Cela peut très bien fonctionner.

    Cela fait toujours une requête agrégée pour chaque appel, ce qui (selon l'option suivante) n'est pas nécessaire, mais au moins cela ne spamme pas le journal des erreurs comme les options ci-dessus.

  • Utilisez une table de comptoir. C'est ce que je ferais. Soit en bar , ou dans une table d'appoint comme bar_foo_counter , obtenez un ID de ligne à l'aide de

    UPDATE bar_foo_counter SET counter = counter + 1
    WHERE bar_id = $1 RETURNING counter
    

    ou l'option la moins efficace si votre framework ne peut pas gérer RETURNING :

    SELECT counter FROM bar_foo_counter
    WHERE bar_id = $1 FOR UPDATE;
    
    UPDATE bar_foo_counter SET counter = $1;
    

    Ensuite, dans la même transaction , utilisez la ligne de compteur générée pour le number . Lorsque vous validez, la ligne de la table de compteur pour ce bar_id est déverrouillé pour la prochaine requête à utiliser. Si vous annulez, la modification est annulée.

Je recommande l'approche du compteur, en utilisant une table latérale dédiée pour le compteur au lieu d'ajouter une colonne à bar . C'est plus propre à modéliser, et cela signifie que vous créez moins de ballonnement de mise à jour dans bar , ce qui peut ralentir les requêtes à bar .