偏执狂增量!计数器可能无法正常工作

时间:2014-06-13 17:41:26

标签: ruby postgresql activerecord ruby-on-rails-3.2 rails-activerecord

上周,我在作业模型上实施了:view_count 整数列。今天我在比较我的网站和Google分析之间的统计数据。浏览量的数字并不一致(有时会有所不同),我现在变得偏执,也许我已经完成了这个简单的任务。

另一个复杂因素是此操作中的一些条件逻辑。如果有人想提出任何改变,我会愿意更好地编写整个行动。

  def show
    unless signed_in?
      redirect_to jobs_path, status: 301, if @job.end_date  < Time.zone.now.to_date
      @job.increment! :view_count unless @job.end_date  < Time.zone.now.to_date
    end
  end

在审核了API文档后,我考虑将以下行重写为:

Job.increment_counter(:view_count, @job) unless @job.end_date  < Time.zone.now.to_date

那么。
1)有什么不对劲跳出来了吗? 2)哪个更好用:增量!或increment_counter。

我正在使用Postgres作为数据库。任何有用的建议或想法都表示赞赏。

注意:使用 before_filter :show for @job = Job.find(params [:id])

1 个答案:

答案 0 :(得分:2)

出错有什么不对吗?是的,increment!都是错的,不应该使用恕我直言。我为什么这么说?和Rails一样,你必须阅读源代码才能看到正在发生的事情。这就是increment!的作用:

def increment!(attribute, by = 1)
  increment(attribute, by).update_attribute(attribute, self[attribute])
end

increment做了什么? increment这样做:

def increment(attribute, by = 1)
  self[attribute] ||= 0
  self[attribute] += by
  self
end

update_attribute执行此操作:

def update_attribute(name, value)
  name = name.to_s
  raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
  send("#{name}=", value)
  save(:validate => false)
end

展开,我们发现您的@job.increment! :view_count与此相同:

@job.view_count = (@job.view_count || 0) + 1
@job.save(:validate => false)

这里的竞争条件应该是明确的:

  1. 处理1加载@job
  2. Process-2加载相同的@job
  3. Process-1递增@job.view_count并将其写入数据库。
  4. Process-2递增@job.view_count并将其写入数据库。
  5. 但是Process-2并不知道Process-1后面的view_count递增,所以Process-1的增量消失了。所以是的,increment!非常愚蠢,容易丢失数据,我不会将它用于任何事情。

    如果您追踪increment_counter,您会发现它只是update_counters的包装。如果您阅读update_counters,您将看到它正确地让数据库完成工作,只需告诉数据库使用大致如此的SQL增加列值:

    update t set c = c + 1 where id = x
    

    那应该是可靠的。

    摘要:忘记increment!decrement!incrementdecrement!存在并改为使用increment_counter