2012-10-12 1 views
0

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 

Répondre

1

Je peux penser à deux approches à ce genre de problème qui sont fiables.

contre colonne

Vous allez créer un « stock restant » colonne et mettre à jour atomiquement:

UPDATE sheet SET signups_remaining=signups_remaining-:count WHERE id=:id AND signups_remaining>=:count 

Vous devrez lier aux valeurs :count et :id en conséquence. Si cette requête s'exécute, cela signifie qu'il y a un nombre suffisant d'inscriptions restantes.

réservés Signups

Créez les dossiers d'inscription à l'avance et de les allouer:

UPDATE signups SET allocation_id=:allocation_id WHERE allocation_id IS NULL LIMIT :count 

Cela mettra à jour zéro ou plusieurs dossiers d'inscription, vous devrez vérifier que vous avez réservé le bon compte avant de valider votre transaction.

+0

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. –

+0

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