PERF: Defer setting of distributed cache in performance critical paths.

PERF: Defer setting of distributed cache in performance critical paths.

Setting a key/value pair in DistributedCache involves waiting on the write to Redis to finish. In most cases, we don’t need to wait on the setting of the cache to finish. We just need to take our return value and move on.

diff --git a/app/models/theme.rb b/app/models/theme.rb
index 97d26ed..5d44c53 100644
--- a/app/models/theme.rb
+++ b/app/models/theme.rb
@@ -155,10 +155,10 @@ class Theme < ActiveRecord::Base
   end
 
   def self.get_set_cache(key, &blk)
-    if @cache.hash.key? key.to_s
-      return @cache[key]
-    end
-    @cache[key] = blk.call
+    return @cache[key] if @cache[key]
+    value = blk.call
+    @cache.defer_set(key, value)
+    value
   end
 
   def self.theme_ids
diff --git a/lib/distributed_cache.rb b/lib/distributed_cache.rb
index 31cb69f..833a9ae 100644
--- a/lib/distributed_cache.rb
+++ b/lib/distributed_cache.rb
@@ -11,4 +11,12 @@ class DistributedCache < MessageBus::DistributedCache
       app_version: Discourse.git_version
     )
   end
+
+  # Defer setting of the key in the cache for performance critical path to avoid
+  # waiting on MessageBus to publish the message which involves writing to Redis.
+  def defer_set(k, v)
+    Scheduler::Defer.later("#{@key}_set") do
+      self[k] = v
+    end
+  end
 end
diff --git a/lib/site_icon_manager.rb b/lib/site_icon_manager.rb
index ea87a8a..cd7d95d 100644
--- a/lib/site_icon_manager.rb
+++ b/lib/site_icon_manager.rb
@@ -62,7 +62,10 @@ module SiteIconManager
   private
 
   def self.get_set_cache(key)
-    @cache[key] ||= yield
+    return @cache[key] if @cache[key]
+    value = yield
+    @cache.defer_set(key, value)
+    value
   end
 
   def self.resolve_original(info)
diff --git a/lib/stylesheet/manager.rb b/lib/stylesheet/manager.rb
index 6b916e9..d630b39 100644
--- a/lib/stylesheet/manager.rb
+++ b/lib/stylesheet/manager.rb
@@ -88,14 +88,16 @@ class Stylesheet::Manager
             builder.compile unless File.exists?(builder.stylesheet_fullpath)
             href = builder.stylesheet_path(current_hostname)
           end
-          cache[cache_key] = href
+
+          cache.defer_set(cache_key, href)
         end
 
         data[:theme_id] = theme_id if theme_id.present? && data[:theme_id].blank?
         data[:new_href] = href
         stylesheets << data
       end
-      cache[array_cache_key] = stylesheets.freeze
+
+      cache.defer_set(array_cache_key, stylesheets.freeze)
       stylesheets
     end
   end
@@ -128,7 +130,7 @@ class Stylesheet::Manager
 
     href = builder.stylesheet_path(current_hostname)
     stylesheet[:new_href] = href
-    cache[cache_key] = stylesheet.freeze
+    cache.defer_set(cache_key, stylesheet.freeze)
     stylesheet
   end
 
diff --git a/lib/svg_sprite/svg_sprite.rb b/lib/svg_sprite/svg_sprite.rb
index a7ba577..34c6510 100644
--- a/lib/svg_sprite/svg_sprite.rb
+++ b/lib/svg_sprite/svg_sprite.rb
@@ -478,7 +478,10 @@ License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL
   end
 
   def self.get_set_cache(key)
-    cache[key] ||= yield
+    return cache[key] if cache[key]
+    value = yield
+    cache.defer_set(key, value)
+    value
   end
 
   def self.cache

GitHub sha: 8cfe2033

This commit appears in #13248 which was approved by eviltrout. It was merged by tgxworld.