J'ai une application où les gens s'inscrivent pour les articles. Chaque article a un nombre limité d'emplacements. Comment puis-je gérer la concurrence? J'ai essayé comme ceci dans la classe d'article:Concurrence sur l'association Dans ActiveRecord
def sign_up(signup)
ActiveRecord::Base.transaction do
return 'Sorry, that item is full.' if full?
signups << signup
sheet.save!
nil
end
end
def full?
locked_signups = signups.lock(true).all
locked_signups.size >= max_signups
end
Est-ce que j'essaye de faire même possible par l'AR? Dois-je implémenter mon propre verrouillage via une colonne? Toutes les suggestions sont les bienvenues.
MISE À JOUR: J'ai eu ce travail par réponse de tadman. Voici le code qui fonctionne:
rows_updated = ActiveRecord::Base.transaction do
Item.connection.update "update items set signup_count=signup_count+1 where id=#{ActiveRecord::Base.sanitize(self.id)} and signup_count<quantity"
end
return 'Sorry, that item is full. Refresh the page to see what\'s still open.' if rows_updated < 1
Avec l'une ou l'autre option, n'avons-nous pas le même problème de concurrence? Un deuxième utilisateur (troisième, quatrième, etc.) lit la valeur signups_remaining après que le ou les utilisateurs précédents revendiquent un emplacement (en insérant une nouvelle ligne d'inscription) et avant que la mise à jour de signups_remaining soit validée. –
Ces deux méthodes sont fiables car, dans le premier cas, la requête ne retournera pas si le stock est insuffisant et, dans le second cas, vous n'en réclamez pas autant que celles qui ne le sont pas actuellement. Le 'WHERE allocation_id IS NULL' s'assure qu'il ne va pas écraser n'importe quel travail précédent. Ceci est possible car il s'agit d'instructions atomiques uniques qui s'exécutent ou ne s'exécutent pas. Deux requêtes distinctes sont beaucoup plus difficiles à synchroniser. – tadman