5 votes

Comment puis-je verrouiller un ensemble d'objets pour une opération?

Dans mon application rails, j'ai du code comme ceci :

def foo
  if object_bar_exists
    raise "can't create bar twice!"
  end

  Bar.create
end

Quel pourrait être invoqué par deux requêtes différentes envoyées au serveur d'application. Si ce code est exécuté par deux requêtes simultanément, et qu'elles exécutent toutes les deux la vérification du if en même temps, aucune ne trouvera le bar de l'autre, et 2 bar seront créés.

Quel est le meilleur moyen de créer un "mutex" pour "la collection de bars" ? Une table de mutex spécifique dans la base de données ?

mise à jour

Je tiens à souligner que je ne peux pas utiliser un mutex en mémoire ici, car la concurrence est entre des requêtes/processus et non des threads.

6voto

Pan Thomakos Points 19946

La meilleure chose à faire est d'effectuer vos opérations dans une transaction de base de données. Parce que vous aurez probablement finalement plusieurs applications s'exécutant et qu'elles ne partageront très probablement pas la mémoire, vous ne pourrez pas créer de verrou Mutex au niveau de l'application, surtout si ces deux services d'application s'exécutent sur des boîtiers physiques totalement différents. Voici comment accomplir la transaction de base de données :

ActiveRecord::Base.transaction do
  # Le code de la transaction va ici.
end

Si vous voulez vous assurer un rollback sur la transaction de base de données, vous devrez activer la validation sur la classe Bar afin qu'une demande de sauvegarde invalide provoque un rollback :

ActiveRecord::Base.transaction do
  bar = Bar.new(params[:bar])
  bar.save!
end

Si vous avez déjà un objet bar dans la base de données, vous pouvez verrouiller cet objet de manière pessimiste ainsi :

ActiveRecord::Base.transaction do
  bar = Bar.find(1, :lock => true)
  # effectuer des opérations sur bar
end

1voto

Alex Moore Points 1959

Si toutes les requêtes arrivent sur la même machine et la même machine virtuelle ruby, vous pourriez utiliser la classe Mutex intégrée de Ruby : Docs Mutex.

S'il y a plusieurs machines ou rvms, vous devrez utiliser une transaction de base de données pour créer / obtenir l'objet Bar, en supposant qu'il est stocké dans la base de données d'une certaine manière.

0voto

BeepDog Points 1891

Je créerais probablement un objet Singleton dans le répertoire lib, afin que vous n'ayez qu'une seule instance de la chose, et utiliser Mutexes pour verrouiller dessus.

De cette façon, vous pouvez vous assurer qu'il n'y a qu'un accès à la chose à un moment donné. Bien sûr, toutes les autres demandes seront bloquées dessus, donc c'est quelque chose à garder à l'esprit.

Pour plusieurs machines, vous devriez stocker un jeton dans une base de données, et synchroniser un certain type d'accès au jeton. Par exemple, le jeton doit être interrogé et retiré, et suivre un certain numéro ou quelque chose pour garantir que les gens ne puissent pas retirer le jeton en même temps. Ou utiliser un service web de verrouillage centralisé afin que la gestion de vos jetons ne se fasse qu'en un seul endroit.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X