首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在RoR中锁定记录

如何在RoR中锁定记录
EN

Stack Overflow用户
提问于 2020-06-24 02:29:40
回答 1查看 1.2K关注 0票数 1

我正在为你可以租的东西开发一个预订系统。我希望限制多个用户保留相同的项目。

我显示一个列表,用户可以单击该列表来检查详细信息。如果任何用户已经打开了详细视图,那么其他用户就不能同时打开它。我正在维护一个标志调用is_lock,以检查记录是否已被锁定,但当多个用户同时单击同一项时,我正面临问题。

所以我采用了悲观锁,这降低了这个问题的发生率,但是多个用户打开同一个项目,但它没有完全解决这个问题。我仍然面临着同样的问题。

代码语言:javascript
运行
复制
begin
Item.transaction do
  item = Item.lock.where(id: item_id, is_lock: false)
  item.is_lock = true;
  item.save!
end
rescue Exception => e
  # Something went wrong.
end

上面是我实现的代码。

如果我做错了什么,请告诉我。

编辑:

我尝试了@rmlockerd提供的解决方案,方法如下:

  1. 在两个不同的控制台中运行rails。
  2. 使用id:100的锁从控制台-1获取记录。
  3. 从控制台-2获取相同的记录。

但是上面的测试失败了,因为我能够从两个控制台获取相同的记录,即使记录是从控制台1锁定的。

在两个不同的控制台中运行rails。

EN

回答 1

Stack Overflow用户

发布于 2020-06-24 06:08:53

仅仅看一下您提供的代码片段可能会产生误导,但是由于您的.where谓词,似乎存在可能的争用条件。

如果User2试图在User1之后但在事务提交之前获得同一项的锁,则.where仍将返回带有is_lock false的原始记录。.lock的默认行为是简单地等待锁的出现。因此,User2将阻塞直到原始事务提交,然后获得一个锁并继续将is_lock设置为true。

好消息是,当您获得一个锁,Rails重新加载记录,以便您得到最新的数据。在获得锁后检查is_lock应消除该争用条件,如下所示:

代码语言:javascript
运行
复制
Item.transaction do
  item = Item.lock.find_by(id: item_id, is_lock: false) # only 1, so where is unnecessary
  return if item.blank? || !item.is_lock

  item.update!(is_lock: true)
end

# I have the lock stuff...

.lock方法还接受一个可选的'locking子句‘(根据您使用的数据库而有所不同),它可以用于配置锁定行为。例如,如果您使用Postgres,您可以:

代码语言:javascript
运行
复制
Item.transaction do
  item = Item.lock('FOR UPDATE SKIP LOCKED').find_by(id: item_id, is_lock: false)
  return if item.blank?

  item.update!(is_lock: true)
end

SKIP LOCKED子句指示Postgres自动跳过任何已经锁定的记录。在上述竞赛条件下,第二次呼叫.lock将立即保释并返回零,因此只需检查物品是否存在就足够了。如果您对数据库特定的锁定子句感兴趣,请查看波斯特格斯MySQL文档。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62546667

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档