From c3ab762738d19915d6176147889a144a970cd187 Mon Sep 17 00:00:00 2001 From: Martin Meyerhoff Date: Tue, 7 May 2024 16:57:54 +0200 Subject: [PATCH] Use page version's `updated_at` timestamp as cache key Currently, it's hard for us to invalidate caches for Alchemy pages when content that's referenced through ingredients with related objects changes. For example, in a situation where a user combines Alchemy and Solidus using the `alchemy_solidus` gem, a page's cache key does not update when a product that's referenced through a SpreeProduct ingredient changes. There's a PR up on the alchemy_solidus gem that touches ingredients in these situations, and with this change, that touching can be used for breaking caches. [1] In the unlikely event the requested version is not available, we use the page's updated_at timestamp as a fallback. [1](https://github.com/AlchemyCMS/alchemy-solidus/pull/108) --- app/models/alchemy/page/page_natures.rb | 15 +++++------ spec/models/alchemy/page_spec.rb | 34 ++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/app/models/alchemy/page/page_natures.rb b/app/models/alchemy/page/page_natures.rb index 728d2c54af..90417f3c16 100644 --- a/app/models/alchemy/page/page_natures.rb +++ b/app/models/alchemy/page/page_natures.rb @@ -101,18 +101,15 @@ def layout_partial_name page_layout.parameterize.underscore end - # Returns the version that's taken for Rails' recycable cache key. + # Returns the string version that's used as Rails' recycable cache key. # - # Uses the +published_at+ value that's updated when the user publishes the page. - # - # If the page is the current preview it uses the +updated_at+ value as cache key. + # In preview mode, it will take the draft version's updated_at timestamp. + # In public mode, it will take the public version's updated_at timestamp. + # If no version is available, it will take the page's updated_at timestamp. # def cache_version - if Current.preview_page == self - updated_at.to_s - else - published_at.to_s - end + relevant_page_version = (Current.preview_page == self) ? draft_version : public_version + (relevant_page_version&.updated_at || updated_at).to_s end # Returns true if the page cache control headers should be set. diff --git a/spec/models/alchemy/page_spec.rb b/spec/models/alchemy/page_spec.rb index 30e50c6c3f..f6089700e2 100644 --- a/spec/models/alchemy/page_spec.rb +++ b/spec/models/alchemy/page_spec.rb @@ -705,12 +705,16 @@ module Alchemy describe "#cache_version" do let(:now) { Time.current } + let(:yesterday) { Time.current - 1.day } let(:last_week) { Time.current - 1.week } let(:page) do - build_stubbed(:alchemy_page, updated_at: now, published_at: last_week) + build_stubbed(:alchemy_page, public_version: public_version, draft_version: draft_version, updated_at: yesterday) end + let(:public_version) { build_stubbed(:alchemy_page_version, updated_at: last_week) } + let(:draft_version) { build_stubbed(:alchemy_page_version, updated_at: now) } + subject { page.cache_version } before do @@ -732,6 +736,34 @@ module Alchemy is_expected.to eq(last_week.to_s) end end + + context "if page has no public version" do + let(:public_version) { nil } + + context "in preview mode" do + let(:preview) { page } + + it "uses updated_at" do + is_expected.to eq(now.to_s) + end + + context "if page has no draft version" do + let(:draft_version) { nil } + + it "returns nil" do + is_expected.to eq(yesterday.to_s) + end + end + end + + context "not in preview mode" do + let(:preview) { nil } + + it "uses updated_at" do + is_expected.to eq(yesterday.to_s) + end + end + end end describe "#public_version" do