Rails (ERB):如果条件不满足,我可以取消片段缓存吗?

Rails (ERB): Can I cancel a fragment cache if a condition fails?

我正在寻找一种简单有效的方法来在条件失败时取消对 ERB 视图中命名片段的缓存写入。

我目前正在这样做:

<% cache("header_#{$I18n.locale}", expires_in: 1.day) do %>
  <% begin %>
      <%= raw open("https://mywebsite.org/remote/fragment", :read_timeout => 10).read %>
  <% rescue OpenURI::HTTPError => e %>
    Error Loading Remote File: <%= e.message %>
  <% end %>
<% end %>

显然其中一些只是为了演示我的问题,而不是展示生产代码的最佳实践。

问题是:上面的代码会缓存错误消息并显示 1 天,而不是在下次加载时重试服务器。

(通常这不会发生在视图中,但在这种情况下,我正在为更大的 rails 应用程序编写一个插件,不能修改控制器,只能修改视图。)

这就是我想要做的:

<% cache("header_#{$I18n.locale}", expires_in: 1.day) do %>
  <% begin %>
      <%= raw open("https://mywebsite.org/remote/fragment", :read_timeout => 10).read %>
  <% rescue OpenURI::HTTPError => e %>
    --> Some command to cancel the cache action started above
    --> Show a backup something to the user (I'll provide)
  <% end %>
<% end %>

有人对如何做到这一点有建议吗?

There are two hard problems in computer science: naming things, cache invalidation, and off-by-one errors.

欢迎来到第二个难题。

尝试使用 Rails.cache 而不是 cache - 我不是 100% 知道有什么区别,或者是否有区别,但是 Rails.cache 为您提供了一些更明显的方法用于缓存操作。

Rails.cache.fetch 采用三个选项:缓存键、选项哈希和块。如果指定的键在缓存中存在且未过期,则return缓存的内容;否则,它将执行块并将其结果存储在缓存中。

<% Rails.cache.fetch "header_#{$I18n.locale}", :expires_in => 1.day do %>
  <% begin %>
    <%= raw open("https://mywebsite.org/remote/fragment", :read_timeout => 10).read %>
  <% rescue OpenURI::HTTPError => e %>
    <% Rails.cache.delete "header_#{$I18n.locale}" # removes the cache entry %>
    <!-- Show your backup here -->
  <% end %>
<% end %>

如果这不起作用,则可能取决于缓存操作的完成顺序 - 可能是条目在您的 rescue 块中被删除,但随后在整个过程中重新添加块已完成执行。在那种情况下,试试这个:

<% remove_cache_entry = false %>

<% Rails.cache.fetch "header_#{$I18n.locale}", :expires_in => 1.day do %>
  <% begin %>
    <%= raw open("https://mywebsite.org/remote/fragment", :read_timeout => 10).read %>
  <% rescue OpenURI::HTTPError => e %>
    <% remove_cache_entry = true %>
    <!-- Show your backup here -->
  <% end %>
<% end %>

<% Rails.cache.delete "header_#{$I18n.locale}" if remove_cache_entry %>

由于您不想在出现异常时缓存结果,因此您应该将异常处理代码放在 cache 块之外:

<% begin %>
  <% cache("header_#{$I18n.locale}", expires_in: 1.day) do %>
    <%= raw open("https://mywebsite.org/remote/fragment", :read_timeout => 10).read %>
  <% end %>
<% rescue OpenURI::HTTPError => e %>
  Error Loading Remote File: <%= e.message %>
<% end %>

这样,当缓存块内出现异常时,该块将在完成之前退出,并且不会发生缓存。