From 10d93a04d3a5dac0d1ba0ffe7f92d90a8fc6e5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 17 May 2024 22:41:15 +0200 Subject: [PATCH] Clear Rails cache when upgrading Consul Democracy We use caching in our application in two different ways: 1. Rails.cache.fetch in models/controllers/libraries 2. Fragment caching in the views When using Rails.cache.fetch, we usually set an expiration date or provide a precise way to invalidate it. If the code changes and the information stored in the cache is different from what the new code would return, it's usually not a big deal because the cache will expire in an hour or a day. Until commit a4461a1a5, the statistics were an exception to this rule, but that's no longer the case. The actual exception to this rule are the i18n translations, but the code caching them is very simple and hasn't changed for more than three years (when it was written for the first time). When using fragment caching, on the other hand, Rails automatically invalidates the cache when the associated _view code_ changes. That is, if a view contains cache, the view renders a partial, and the partial changes, the cache is correctly invalidated. The cache isn't invalidated when the code in helpers, models or controllers change, though, which the Rails team considers a compromise solution. However, we've been moving view partials (and even views) to components, and the cache isn't invalidated when a component changes (it doesn't matter whether the component Ruby file or the component ERB file changes). That means that the cache will keep rendering the HTML generated by the old code. So, now, we're clearing the cache when upgrading to a new version of Consul Democracy, as part of the release tasks. That way, institutions upgrading to a new version don't have to worry about possible issues with the cache due to the new code not being executed. I was thinking of adding it to a Capistrano task, but that would mean that every time people deploy new code, even if it's a hotfix that doesn't affect the cache at all, the cache would be cleared, which could be inconvenient. Doing it in a release, that usually changes thousands of lines of code, sounds like a good compromise. --- lib/tasks/cache.rake | 7 +++++++ lib/tasks/consul.rake | 1 + spec/lib/tasks/cache_spec.rb | 15 +++++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 lib/tasks/cache.rake create mode 100644 spec/lib/tasks/cache_spec.rb diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake new file mode 100644 index 000000000..30832696a --- /dev/null +++ b/lib/tasks/cache.rake @@ -0,0 +1,7 @@ +namespace :cache do + desc "Clears the Rails cache" + task clear: :environment do + ApplicationLogger.new.info "Clearing Rails cache" + Rails.cache.clear + end +end diff --git a/lib/tasks/consul.rake b/lib/tasks/consul.rake index fcc23ff26..73296660a 100644 --- a/lib/tasks/consul.rake +++ b/lib/tasks/consul.rake @@ -2,6 +2,7 @@ namespace :consul do desc "Runs tasks needed to upgrade to the latest version" task execute_release_tasks: ["settings:rename_setting_keys", "settings:add_new_settings", + "cache:clear", "execute_release_2.2.0_tasks"] desc "Runs tasks needed to upgrade from 2.1.1 to 2.2.0" diff --git a/spec/lib/tasks/cache_spec.rb b/spec/lib/tasks/cache_spec.rb new file mode 100644 index 000000000..3aef942d2 --- /dev/null +++ b/spec/lib/tasks/cache_spec.rb @@ -0,0 +1,15 @@ +require "rails_helper" + +describe "rake cache:clear" do + before { Rake::Task["cache:clear"].reenable } + + it "clears the cache", :with_cache do + Rails.cache.write("tests/cache_exists", true) + + expect(Rails.cache.fetch("tests/cache_exists") { false }).to be true + + Rake.application.invoke_task("cache:clear") + + expect(Rails.cache.fetch("tests/cache_exists") { false }).to be false + end +end