rails - implémentation d'un verrou simple pour empêcher l'utilisateur de modifier simultanément les mêmes données

StackOverflow https://stackoverflow.com/questions/1803574

Question

J'ai une application où je dois empêcher les utilisateurs de modifier des données alors qu'elles sont modifiées par un autre utilisateur. J'essaie de trouver le meilleur moyen de le faire et je voulais demander des idées. Jusqu'à présent, j'ai créé un modèle de paramètres qui stocke la configuration à l'échelle de l'application sur la base de données sous forme de paires clé / valeur. Donc, pour le verrou, j’ai une instance de paramètres appelée LOCKED_TABLE_UID, qui stocke l’id utilisateur de l’utilisateur modifiant la table ou null (nil) si la table est libre.

>> lock = Setting.find_by_key('LOCKED_TABLE_UID')

Ensuite, j'ai implémenté deux méthodes dans mon contrôleur d'application pour acquérir et libérer le verrou:

# current_user returns the user currently logged in
def acquire_lock
  lock = Setting.find_by_key("LOCKED_TABLE_UID")
  if lock.value
    # if lock taken, see if it's the current_user or someone else
    if lock.value.to_i == current_user.id.to_i
      return true
    else
      return false
    end
  else
    # lock is free, assign it to this user
    lock.value = current_user.id
    return true if lock.save
  end
end

def release_lock
  lock = Setting.find_by_key("LOCKED_TABLE_UID")
  if lock.value
    # the lock belongs to current_user, so he can release it
    if lock.value.to_i == current_user.id.to_i
      lock.value = nil
      return true if lock.save
    else
      # not your lock, go away
      return false
    end
  else
    # lock is free, quit bugging
    return true
  end
end

Ce que je veux, c'est créer un code bloc contenant le mécanisme de verrouillage, comme ceci:

def some_crud_action
  requires_locking do |lock|
    if lock
      # do some CRUD stuff here
    else
      # decline CRUD and give some error
    end
  end
end

J'apprécierais de l'aide à ce sujet - mais je suis également ouvert à d'autres suggestions sur la façon de réaliser tout cela, ou certaines choses que j'ai peut-être oubliées. Ce verrou ne doit pas nécessairement être atomique, mais assez basique et le plus important - que cela fonctionne :) merci.

Était-ce utile?

La solution

Vous êtes presque là. Créez votre require_locking? action comme bon vous semble. Ensuite, traitez-le avec un before_filter.

 before_filter :requires_locking?, :only => [:update, :destroy]
 after_filter :release_lock, :only => [:update, :destroy]

 def requires_locking do |lock|
   unless acquire_lock
      lock = Setting.find_by_key("LOCKED_TABLE_UID")
      user_with_lock = User.find(lock.value)
      flash[:message] = "Action denied: Table locked by: #{user_with_lock.name}"
      redirect_to :back
   end
 end

Autres conseils

Avez-vous vu la fonctionnalité de verrouillage intégrée à ActiveRecord?

J'aime l’idée, mais vous voyez un gros problème avec votre solution, c’est que vous obtenez et libérez des verrous de tables entières.

Pour une très petite application, cela pourrait bien fonctionner, mais imaginons que des milliers d'utilisateurs tentent d'accéder, par exemple, au tableau "PRODUITS" et d'attendre parce que quelqu'un édite une entrée totalement indépendante de leurs propres produits.

Vous pourriez peut-être utiliser une approche plus fine et verrouiller des lignes particulières au lieu de tableaux. Le verrou inclurait alors le nom de la table, l’ID de ligne et l’ID d’utilisateur.

Je pense que la acts_as_lockable_by gem est exactement ce que vous avez demandé en termes plus simples et avec moins de code. Il est facilement intégrable avec des rails ou même un projet Ruby nu.

Avec cette gemme, vous obtenez les méthodes lock , unlock et renew_lock atomiques. De plus, vous obtenez un verrou ttl à expiration automatique. Par conséquent, si la merde frappe le ventilateur et que vous ne pouvez pas déverrouiller la ressource, elle sera automatiquement déverrouillée pour vous!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top