我在Rails 4.2.8上。
我们说我的模型Contact
为contact = Contact.new
。
调用contact.cache_key
会返回带时间戳的密钥,如下所示:
"触点/ 2615608-20180109154442000000000"
其中2615608
是ID,20180109154442000000000
是时间戳。
我在控制器中看到了一种奇怪的行为。
在contact.update(contact_params)
之后,如果我拨打contact.cache_key
我有不同的时间戳,那么我打电话给contact.reload.cache_key
。
奇怪的是,我100%确定没有其他更新发生在模型updated_at
上(通过检查控制台的SQL更新语句,只有一个正在改变updated_at
)
如果我在控制器中执行类似的操作,那就太奇怪了:
contact.udpate(contact_params)
Rails.logger.info "Updated_at before reload is #{@contact.updated_at} and it's cache_key is #{@contact.cache_key}"
Rails.logger.info "Updated_at after reload is #{@contact.reload.updated_at} and it's cache_key is #{@contact.reload.cache_key}"
输出显示IDENTICAL updated_at值,但不同的cache_key值:
Updated_at before reload is 2018-01-09 14:01:58 -0200 and it's cache_key is contacts/2615608-20180109160158143423000
Updated_at after reload is 2018-01-09 14:01:58 -0200 and it's cache_key is contacts/2615608-20180109160158000000000
如您所见,相同的updated_at,但不同的时间戳。
这让我疯了。我讨厌必须手动。重新加载模型。为什么会这样?尝试查看cache_key的源代码,但无法在那里找到答案,因为它显然只取决于updated_at值(相同)。
答案 0 :(得分:0)
发现了这个问题。 这是一个Rails错误,已在Rails 5.0中修复。
可以找到Rails 4.2 .cache_key
的源代码here。如您所见,它使用cache_timestamp_format = :nsec
,这太精确了。
我能理解的原因是,在重新加载之前,模型的updated_at仍然在内存中,因此它具有足够的分辨率(在内存中)以返回非常精确的纳秒键。但是在model.reload
之后,updated_at
来自数据库,它没有那么高的分辨率,所以这就是为什么cache_key时间戳产生不同的数字,最后有很多零(在我的例子,20180109160158 000000000 )。
This issue使用过于精确(纳秒)的时间戳详细说明问题,合并this pull request以解决问题,将精度从:nsec更改为:usec。
在the issue discussion上方(上面的相同链接)@tarzan通过使用以下代码创建ActiveSupport::Concern
来提出修复建议:
included do
self.cache_timestamp_format = :usec
end
并将:usec定义为初始化程序中的格式化程序:
Time::DATE_FORMATS[:usec] = "%Y%m%d%H%M%S%6N"
然后手动将您的关注点插入到您想要手动使用的模型中.cache_key
(顺便说一句,我使用的是根据oficial Rails指南推荐的用于低级缓存的方法,使用{{1} },在主题1.6低级缓存中检查http://guides.rubyonrails.org/caching_with_rails.html。
问题是,至少在我的测试中(MySQL on Mac Os High Sierra),联系一个脚手架模型,其精度:datetime列(如updated_at)仅在秒级;这就是为什么他的修复不能解决它的原因,因为从纳秒变为微秒精度仍然会导致两个不同的cache_keys,并且添加的零更少。举个例子:
Rails.cache.fetch(self.cache_key)
所以,至少现在,我很遗憾地坚持.reload。
最后,但至少,我只是意识到了这个错误,因为我们有一个20个这样的联系人页面,可以通过ajax进行操作。每个ajax调用都会调用模型上的[4] pry(#<ContactsController>)> ::Time::DATE_FORMATS[:usec] = "%Y%m%d%H%M%S%6N"
=> "%Y%m%d%H%M%S%6N"
[5] pry(#<ContactsController>)> @contact.updated_at.utc.to_s(:usec)
=> "20180109234014062142"
[6] pry(#<ContactsController>)> @contact.reload.updated_at.utc.to_s(:usec)
Contact Load (2.2ms) SELECT `contacts`.* FROM `contacts` WHERE `contacts`.`id` = 2615608 LIMIT 1 /*application:Temporadalivre,controller:contacts,action:toggle_status*/
=> "20180109234014000000"
,并将模型.update
返回给视图。自调用.as_cached_json
以来,我们希望通过重新加载页面,所有内容都已经被缓存,但事实并非如此,只有通过检查缓存键才能发现这个错误,这给我们带来了非常好的性能提升,失去了。