由于某些原因,如果不同的用户在同一时间运行该代码,则此代码可以创建重复的游戏:
game = Game.find_or_create_by(
status: Game::STATUS[:waiting],
category_id: params[:category_id],
private: 0
) do |g|
is_new = true
g.user = current_user
end
我无法清楚地知道是什么问题,但可能是关于使用不同数据库连接的不同Unicorn进程,因此事务可以并行运行。
如果是这样,我需要正确的方法来避免它,也许我应该使用Rails事务或Postgres锁,但我真的需要一个使用的例子。
谢谢。
答案 0 :(得分:1)
可以以高并发级别发生。
根据rails docs,这些查询将运行:
SELECT * FROM games WHERE status = 'waiting' AND ... LIMIT 1;
INSERT INTO games (status, ...) VALUES ('waiting', ...);
当第一个避难所返回时,第二个只运行。
如果两个(或更多)连接在几微秒内启动第一个查询,则多个进程可能会创建多个条目。为防止这种情况发生,您可以在该表格上使用ACCESS EXCLUSIVE
lock,或使用自定义advisory lock。
您也可以使用一些唯一索引,以防止将多个条目插入到您的数据库中,但如果它自己使用,则会在这种情况下导致SQL异常。
修改强>:
可以通过LOCK
command获取 ACCESS EXCLUSIVE
锁。
可以使用pg_advisory_lock(id)
function获取咨询锁定。
两者都要求您运行任意SQL命令。
另一种方法是使用自定义查询:
东西,比如:
INSERT INTO games (status, category_id, private)
SELECT 'waiting', 2, 0
WHERE NOT EXISTS (
SELECT 1
FROM games
WHERE status = 'waiting'
AND category_id = 2
AND private = 0
)
RETURNING *;
-- only select, when this not inserted anything
SELECT *
FROM games
WHERE status = 'waiting'
AND category_id = 2
AND private = 0