使用索引作为ActiveRecord属性

时间:2014-06-29 17:35:26

标签: mysql sql ruby-on-rails ruby activerecord

我试图建模一个错误跟踪器,每个用户都有连续票号的门票:

用户1:

  • 门票#1
  • 门票#2
  • 门票#3

用户2:

  • 门票#1
  • 门票#2

默认情况下,Rails为每条记录提供一个唯一的ID,我需要另一个属性,ticket_number,填充在create并且取决于用户的票数。

我尝试使用Rails,使用before_create挂钩执行user.tickets.count + 1,但这似乎并不安全,因为异步工作人员可以同时创建多个票证例如。

我是否可以利用数据库索引在数据库级别处理这个ticket_number,就像它为id发生一样,并将此ticket_number作为属性检索?

2 个答案:

答案 0 :(得分:1)

我认为最安全的方法是

  • 为您的故障单ticket_number
  • 添加额外的属性
  • 在数据库层中为复合索引user_id, ticket_number
  • 添加唯一约束
  • 为您的故障单上的ticket_number添加user_id ticket_number验证程序,以匹配数据库层
  • 添加处理程序以构建ticket_number并解决模型中的竞争条件。

通过此设置,您可以确定

  • rails 尝试不允许无效数据
  • 如果db越过rails
  • ,则db将不接受两个不一致的user_id,ticket_number组合
  • rails将正常处理数据库异常并重新分配before_create :set_ticket_number

你需要一个

recalculate_number将计算正确的票号(将分配给在比赛条件下获胜的第一张记录)

和{{1}}方法来处理由于db唯一性约束(竞争条件的输家)而无法持久化的记录。

答案 1 :(得分:0)

所以,你可能想要签出Rails' belongs_to上的counter_cache选项会自动执行该行为:

class Ticket
  belongs_to :user, counter_cache: true
end

# you need to add `tickets_count` to your `users` table (or `name_of_counter_count`)
class User
  has_many :tickets
end

然后,您可以从#tickets_count的任何实例访问User。 Rails确保在创建故障单时增加该数量,并在销毁故障时减少该数量。

但是要小心,它使用ActiveRecord回调,因此如果您因某些原因跳过它们,计数器将会失去同步(您可以使用#reset_counters(:counter_name)重置它们

还有counter_culture宝石将提供更灵活的解决方案。

至于竞争条件,我不是肯定的,但我认为这是在桌子被锁定时在同一交易中完成的。待验证。

注意:根据您的各种验证和业务逻辑,仅在数据库级别处理此问题可能会变得非常棘手。

IMO:如果这个数量对您的应用程序至关重要,那么在您需要它时可能值得查询(SELECT COUNT(...)