3

在我的 Rails 应用程序中,我想记录时间 a userwas last_seen

现在,我在我的SessionsHelper

def sign_in(user)
  .....
  user.update_column(:last_seen, Time.zone.now)
  self.current_user = user
end

但这不是很精确,因为用户可能在上午 8 点登录,而在晚上,last_seen数据库列仍将包含该时间。

所以我想last_seen在用户采取行动时更新:

class ApplicationController
  before_filter :update_last_seen

  private

  def update_last_seen
    current_user.last_seen = Time.zone.now
    current_user.save
  end
end

但我也不喜欢这种方法,因为数据库会受到用户执行的每一个操作的影响。

那么有什么比这更好的选择呢?

4

1 回答 1

9

Rails 实际上内置了这种行为touch

User.last.touch
#=> User's updated_at is updated to the current time

在任何配置良好的数据库中处理像这样更新单个列所需的时间应该远低于 5 毫秒,而且很可能低于 1 毫秒。如果您已经准备建立该数据库连接(或者,在 Rails 的情况下,使用以前从池中建立的连接),则开销可以忽略不计。


要回答您的代码是否较慢的问题,那么您认为这一切都是错误的。您可以优化已经非常快速的操作以提高性能,但我反而更担心“正确性”。下面是 ActiveRecordtouch方法的实现:

def touch(name = nil)
  attributes = timestamp_attributes_for_update_in_model
  attributes << name if name

  unless attributes.empty?
    current_time = current_time_from_proper_timezone
    changes = {}

    attributes.each do |column|
      changes[column.to_s] = write_attribute(column.to_s, current_time)
    end

    changes[self.class.locking_column] = increment_lock if locking_enabled?

    @changed_attributes.except!(*changes.keys)
    primary_key = self.class.primary_key
    self.class.unscoped.update_all(changes, { primary_key => self[primary_key] }) == 1
  end
end

现在你告诉我,哪个更快?哪个更正确

在这里,我给你一个提示:成千上万的人已经使用了这个实现,touch并且这个代码可能已经运行了数百万次。您的代码已被您单独使用,可能甚至没有编写测试,也没有任何同行评审。

“但仅仅因为其他人使用它并不能使它在经验上变得更好,”你争辩道。当然,你是对的,但它再次忽略了重点:虽然你可以继续构建你的应用程序并制作其他人(你的用户)可以使用并从中受益的东西,但你在这里转动你的轮子,想知道什么对你更好即使其他人已经找到了一个好的解决方案。

把钉子钉在棺材上,是的,你的代码比较慢。它执行回调,进行脏跟踪,并将所有更改的属性保存到数据库中。touch绕过了大部分,专注于完成将时间戳更新持久化到模型所需的工作。

于 2013-08-18T18:51:41.050 回答