2012-10-12 128 views
0

我有一个应用程序,其中人们注册的项目。每个项目的插槽数量有限。我该如何处理并发?我试过这样的项目类:并发关联在ActiveRecord中

def sign_up(signup) 
    ActiveRecord::Base.transaction do 
    return 'Sorry, that item is full.' if full? 
    signups << signup 
    sheet.save! 
    nil 
    end 
end 

def full? 
    locked_signups = signups.lock(true).all 
    locked_signups.size >= max_signups 
end 

是我试图通过AR甚至可能做什么?我是否需要通过列实现自己的锁定?欢迎任何建议。

更新:我得到了这个工作每塔德曼的答案。以下是可用的代码:

rows_updated = ActiveRecord::Base.transaction do 
    Item.connection.update "update items set signup_count=signup_count+1 where id=#{ActiveRecord::Base.sanitize(self.id)} and signup_count<quantity" 
end 
return 'Sorry, that item is full. Refresh the page to see what\'s still open.' if rows_updated < 1 

回答

1

我可以想到两种解决这类问题的方法。

计数器列

您将创建一个“剩余库存”列并自动更新:

UPDATE sheet SET signups_remaining=signups_remaining-:count WHERE id=:id AND signups_remaining>=:count 

你就不得不相应地绑定到:count:id值。如果此查询运行,则意味着有足够的注册数量。

保留注册等

创建提前注册记录,并对其进行分配:

UPDATE signups SET allocation_id=:allocation_id WHERE allocation_id IS NULL LIMIT :count 

这将更新零个或多个注册记录,所以你必须要检查是否保留正确的在提交您的交易之前进行计数。

+0

使用任一选项,我们不会有同样的并发问题吗?第二个(第三,第四等)用户在先前用户声明一个插槽(通过插入一个新注册行)之后并且在提交signups_remaining更新之前读取signups_remaining值。 –

+0

这两者都是可靠的,因为在第一种情况下,如果库存不足,查询将不会返回执行结果,而在第二种情况下,您只会声明与当前未分配一样多的查询。 'WHERE allocation_id IS NULL'确保它不会覆盖以前的任何工作。这是可能的,因为这些都是单原子语句,既可以运行也可以不运行。两个单独的查询更难同步。 – tadman