如何处理删除和创建相同行的并发请求?

时间:2015-04-23 00:51:57

标签: ruby-on-rails-3 database-concurrency

我的表格如下所示:

game_stats表:

id | game_id | player_id | stats | (many other cols...)
----------------------
1  | 'game_abc' | 8 | 'R R A B S' | ...
2  | 'game_abc' | 9 | 'S B A S' | ...

用户批量上传特定游戏的数据,同时提交两个玩家'数据一下子。例如:

"game": {
  id: 'game_abc',
  player_stats: {
    8: {
      stats: 'R R A B S'
    },
    9: {
      stats: 'S B A S'
    }
  }
}

将此提交到我的服务器应该会产生第一个表。

当我再次提交相同的数据时(例如,使用修订版),我在控制器中执行的操作首先删除game_stats表中具有给定game_id的所有现有行,而不是更新现有行: / p>

class GameStatController
    def update
      GameStat.where("game_id = ?", game_id).destroy_all
      params[:game][:player_stats].each do |stats|
        game_stat.save
      end
    end
end

这适用于单线程或单进程服务器。问题是我正在运行Unicorn,这是一个多进程服务器。如果有两个请求同时进入,我会遇到竞争条件:

Request 1: GameStat.where(...).destroy_all
Request 2: GameStat.where(...).destroy_all
Request 1: Save new game_stats
Request 2: Save new game_stats

结果:多个具有相同数据的game_stat行。

我相信以某种方式锁定行或表是同时防止多个更新的方法 - 但我无法弄清楚如何做到这一点。结合交易似乎是正确的做法,但我并不理解为什么。

编辑

澄清为什么我无法弄清楚如何使用锁定:我无法一次锁定一行,因为该行只是删除而不是修改。

1 个答案:

答案 0 :(得分:0)

默认情况下,AR不支持表级锁定。您必须执行特定于db的SQL或使用像Monogamy

这样的gem

如果没有别的话,在事务中包装保存语句会加快速度。

另一种方法是使用Redis实现锁定。像redis-lock这样的宝石也可以使用。这可能风险较小,因为它不会触及数据库,您可以将Redis密钥设置为过期。