rieles: implementa un bloqueo simple para evitar que los usuarios editen los mismos datos simultáneamente
-
05-07-2019 - |
Pregunta
Tengo una aplicación donde necesito evitar que los usuarios editen datos mientras un usuario diferente los está editando. Estoy tratando de pensar en la mejor manera de hacerlo y quería pedir ideas. Hasta ahora, he creado un modelo de configuración que almacena la configuración de toda la aplicación en la base de datos en pares clave / valor. Entonces, para el bloqueo, tengo una instancia de configuración que se llama LOCKED_TABLE_UID, y almacenó el user_id del usuario que edita la tabla o nulo (nulo) si la tabla está libre.
>> lock = Setting.find_by_key('LOCKED_TABLE_UID')
Luego, implementé 2 métodos en mi controlador de aplicación para adquirir y liberar el bloqueo:
# 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
Lo que quiero es crear algún tipo de código de bloque que contenga el mecanismo de bloqueo, algo como esto:
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
Apreciaría la ayuda en esto, pero también estoy abierto a otras sugerencias sobre cómo lograr todo eso, o algunas cosas que puedo haber pasado por alto. Este bloqueo no tiene que ser atómico, sino bastante básico y lo más importante: que funcione :) gracias.
Solución
Ya casi estás allí. ¿Crear su require_locking? acción como mejor te parezca. Luego procesalo con 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
Otros consejos
¿Has visto la función de bloqueo integrada ActiveRecord?
Me gusta la idea, pero veo un gran problema con su solución, y es que está obteniendo y liberando bloqueos de tablas enteras.
Para una aplicación muy pequeña que podría estar bien, pero imagínese si tiene miles de usuarios intentando acceder a, digamos, la tabla 'PRODUCTOS' y tener que esperar porque alguien está editando una entrada que no está relacionada con sus propios productos.
Tal vez podría tener un enfoque de grano más fino y bloquear filas particulares en lugar de tablas. Luego, el bloqueo incluiría el nombre de la tabla, el ID de fila y el ID de usuario.
Creo que Act_as_lockable_by gema está haciendo exactamente lo que pediste en términos más simples y con menos código. Es fácilmente integrable con rieles o incluso un proyecto de rubí desnudo.
Con esta gema, obtienes métodos atómicos de bloqueo
, desbloqueo
y renew_lock
. Además, obtienes bloqueos ttl
de caducidad automática, por lo que si la mierda golpea al ventilador y no puedes desbloquear el recurso, ¡se desbloqueará automáticamente para ti!