FIX: Invalidate database theme cache when hostname changes (#9908)
Hostname can vary per-site on a multisite cluster, so this change requires converting the compiler_version from a constant into a class method which is evaluated at runtime. The value is stored in the theme DistributedCache, so performance impact should be negligible.
This commit is contained in:
parent
7635c18a14
commit
d29d69e10d
|
@ -122,6 +122,19 @@ class Theme < ActiveRecord::Base
|
||||||
SvgSprite.expire_cache
|
SvgSprite.expire_cache
|
||||||
end
|
end
|
||||||
|
|
||||||
|
BASE_COMPILER_VERSION = 16
|
||||||
|
def self.compiler_version
|
||||||
|
get_set_cache "compiler_version" do
|
||||||
|
dependencies = [
|
||||||
|
BASE_COMPILER_VERSION,
|
||||||
|
Ember::VERSION,
|
||||||
|
GlobalSetting.cdn_url,
|
||||||
|
Discourse.current_hostname
|
||||||
|
]
|
||||||
|
Digest::SHA1.hexdigest(dependencies.join)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.get_set_cache(key, &blk)
|
def self.get_set_cache(key, &blk)
|
||||||
if @cache.hash.key? key.to_s
|
if @cache.hash.key? key.to_s
|
||||||
return @cache[key]
|
return @cache[key]
|
||||||
|
@ -248,7 +261,7 @@ class Theme < ActiveRecord::Base
|
||||||
theme_ids = [theme_ids] unless Array === theme_ids
|
theme_ids = [theme_ids] unless Array === theme_ids
|
||||||
|
|
||||||
theme_ids = transform_ids(theme_ids)
|
theme_ids = transform_ids(theme_ids)
|
||||||
cache_key = "#{theme_ids.join(",")}:#{target}:#{field}:#{ThemeField::COMPILER_VERSION}"
|
cache_key = "#{theme_ids.join(",")}:#{target}:#{field}:#{Theme.compiler_version}"
|
||||||
lookup = @cache[cache_key]
|
lookup = @cache[cache_key]
|
||||||
return lookup.html_safe if lookup
|
return lookup.html_safe if lookup
|
||||||
|
|
||||||
|
@ -262,7 +275,7 @@ class Theme < ActiveRecord::Base
|
||||||
theme_ids = [theme_ids] unless Array === theme_ids
|
theme_ids = [theme_ids] unless Array === theme_ids
|
||||||
|
|
||||||
theme_ids = transform_ids(theme_ids)
|
theme_ids = transform_ids(theme_ids)
|
||||||
get_set_cache("#{theme_ids.join(",")}:modifier:#{modifier_name}:#{ThemeField::COMPILER_VERSION}") do
|
get_set_cache("#{theme_ids.join(",")}:modifier:#{modifier_name}:#{Theme.compiler_version}") do
|
||||||
ThemeModifierSet.resolve_modifier_for_themes(theme_ids, modifier_name)
|
ThemeModifierSet.resolve_modifier_for_themes(theme_ids, modifier_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -321,7 +334,7 @@ class Theme < ActiveRecord::Base
|
||||||
def self.resolve_baked_field(theme_ids, target, name)
|
def self.resolve_baked_field(theme_ids, target, name)
|
||||||
if target == :extra_js
|
if target == :extra_js
|
||||||
require_rebake = ThemeField.where(theme_id: theme_ids, target_id: Theme.targets[:extra_js]).
|
require_rebake = ThemeField.where(theme_id: theme_ids, target_id: Theme.targets[:extra_js]).
|
||||||
where("compiler_version <> ?", ThemeField::COMPILER_VERSION)
|
where("compiler_version <> ?", Theme.compiler_version)
|
||||||
require_rebake.each { |tf| tf.ensure_baked! }
|
require_rebake.each { |tf| tf.ensure_baked! }
|
||||||
require_rebake.map(&:theme_id).uniq.each do |theme_id|
|
require_rebake.map(&:theme_id).uniq.each do |theme_id|
|
||||||
Theme.find(theme_id).update_javascript_cache!
|
Theme.find(theme_id).update_javascript_cache!
|
||||||
|
|
|
@ -60,14 +60,6 @@ class ThemeField < ActiveRecord::Base
|
||||||
validates :name, format: { with: /\A[a-z_][a-z0-9_-]*\z/i },
|
validates :name, format: { with: /\A[a-z_][a-z0-9_-]*\z/i },
|
||||||
if: Proc.new { |field| ThemeField.theme_var_type_ids.include?(field.type_id) }
|
if: Proc.new { |field| ThemeField.theme_var_type_ids.include?(field.type_id) }
|
||||||
|
|
||||||
BASE_COMPILER_VERSION = 16
|
|
||||||
DEPENDENT_CONSTANTS = [
|
|
||||||
BASE_COMPILER_VERSION,
|
|
||||||
Ember::VERSION,
|
|
||||||
GlobalSetting.cdn_url
|
|
||||||
]
|
|
||||||
COMPILER_VERSION = Digest::SHA1.hexdigest(DEPENDENT_CONSTANTS.join)
|
|
||||||
|
|
||||||
belongs_to :theme
|
belongs_to :theme
|
||||||
|
|
||||||
def process_html(html)
|
def process_html(html)
|
||||||
|
@ -311,18 +303,18 @@ class ThemeField < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def ensure_baked!
|
def ensure_baked!
|
||||||
needs_baking = !self.value_baked || compiler_version != COMPILER_VERSION
|
needs_baking = !self.value_baked || compiler_version != Theme.compiler_version
|
||||||
return unless needs_baking
|
return unless needs_baking
|
||||||
|
|
||||||
if basic_html_field? || translation_field?
|
if basic_html_field? || translation_field?
|
||||||
self.value_baked, self.error = translation_field? ? process_translation : process_html(self.value)
|
self.value_baked, self.error = translation_field? ? process_translation : process_html(self.value)
|
||||||
self.error = nil unless self.error.present?
|
self.error = nil unless self.error.present?
|
||||||
self.compiler_version = COMPILER_VERSION
|
self.compiler_version = Theme.compiler_version
|
||||||
DB.after_commit { CSP::Extension.clear_theme_extensions_cache! }
|
DB.after_commit { CSP::Extension.clear_theme_extensions_cache! }
|
||||||
elsif extra_js_field?
|
elsif extra_js_field?
|
||||||
self.value_baked, self.error = process_extra_js(self.value)
|
self.value_baked, self.error = process_extra_js(self.value)
|
||||||
self.error = nil unless self.error.present?
|
self.error = nil unless self.error.present?
|
||||||
self.compiler_version = COMPILER_VERSION
|
self.compiler_version = Theme.compiler_version
|
||||||
elsif basic_scss_field?
|
elsif basic_scss_field?
|
||||||
ensure_scss_compiles!
|
ensure_scss_compiles!
|
||||||
DB.after_commit { Stylesheet::Manager.clear_theme_cache! }
|
DB.after_commit { Stylesheet::Manager.clear_theme_cache! }
|
||||||
|
@ -332,12 +324,12 @@ class ThemeField < ActiveRecord::Base
|
||||||
DB.after_commit { CSP::Extension.clear_theme_extensions_cache! }
|
DB.after_commit { CSP::Extension.clear_theme_extensions_cache! }
|
||||||
DB.after_commit { SvgSprite.expire_cache }
|
DB.after_commit { SvgSprite.expire_cache }
|
||||||
self.value_baked = "baked"
|
self.value_baked = "baked"
|
||||||
self.compiler_version = COMPILER_VERSION
|
self.compiler_version = Theme.compiler_version
|
||||||
elsif svg_sprite_field?
|
elsif svg_sprite_field?
|
||||||
DB.after_commit { SvgSprite.expire_cache }
|
DB.after_commit { SvgSprite.expire_cache }
|
||||||
self.error = nil
|
self.error = nil
|
||||||
self.value_baked = "baked"
|
self.value_baked = "baked"
|
||||||
self.compiler_version = COMPILER_VERSION
|
self.compiler_version = Theme.compiler_version
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.will_save_change_to_value_baked? ||
|
if self.will_save_change_to_value_baked? ||
|
||||||
|
@ -370,7 +362,7 @@ class ThemeField < ActiveRecord::Base
|
||||||
rescue SassC::SyntaxError => e
|
rescue SassC::SyntaxError => e
|
||||||
self.error = e.message unless self.destroyed?
|
self.error = e.message unless self.destroyed?
|
||||||
end
|
end
|
||||||
self.compiler_version = COMPILER_VERSION
|
self.compiler_version = Theme.compiler_version
|
||||||
self.value_baked = Digest::SHA1.hexdigest(result.join(",")) # We don't use the compiled CSS here, we just use it to invalidate the stylesheet cache
|
self.value_baked = Digest::SHA1.hexdigest(result.join(",")) # We don't use the compiled CSS here, we just use it to invalidate the stylesheet cache
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -716,7 +716,7 @@ HTML
|
||||||
first_common_value = Theme.lookup_field(child.id, :desktop, "header")
|
first_common_value = Theme.lookup_field(child.id, :desktop, "header")
|
||||||
first_extra_js_value = Theme.lookup_field(child.id, :extra_js, nil)
|
first_extra_js_value = Theme.lookup_field(child.id, :extra_js, nil)
|
||||||
|
|
||||||
stub_const(ThemeField, :COMPILER_VERSION, "SOME_NEW_HASH") do
|
Theme.stubs(:compiler_version).returns("SOME_NEW_HASH") do
|
||||||
second_common_value = Theme.lookup_field(child.id, :desktop, "header")
|
second_common_value = Theme.lookup_field(child.id, :desktop, "header")
|
||||||
second_extra_js_value = Theme.lookup_field(child.id, :extra_js, nil)
|
second_extra_js_value = Theme.lookup_field(child.id, :extra_js, nil)
|
||||||
|
|
||||||
|
@ -730,5 +730,18 @@ HTML
|
||||||
expect(new_extra_js_compiler_version).to eq("SOME_NEW_HASH")
|
expect(new_extra_js_compiler_version).to eq("SOME_NEW_HASH")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'recompiles when the hostname changes' do
|
||||||
|
theme.set_field(target: :settings, name: :yaml, value: "name: bob")
|
||||||
|
theme_field = theme.set_field(target: :common, name: :after_header, value: '<script>console.log("hello world");</script>')
|
||||||
|
theme.save!
|
||||||
|
|
||||||
|
expect(Theme.lookup_field(theme.id, :common, :after_header)).to include("_ws=#{Discourse.current_hostname}")
|
||||||
|
|
||||||
|
SiteSetting.force_hostname = "someotherhostname.com"
|
||||||
|
Theme.clear_cache!
|
||||||
|
|
||||||
|
expect(Theme.lookup_field(theme.id, :common, :after_header)).to include("_ws=someotherhostname.com")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue