1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Merge pull request #35201 from rails/no-leaks2

Fix memory leaks in development
This commit is contained in:
Aaron Patterson 2019-02-09 11:54:02 -08:00 committed by GitHub
commit ea0eaced55
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 60 additions and 20 deletions

View file

@ -58,22 +58,32 @@ module ActionView
alias :eql? :equal?
@details_keys = Concurrent::Map.new
@digest_cache = Concurrent::Map.new
def self.get(details)
def self.digest_cache(details)
@digest_cache[details_cache_key(details)] ||= Concurrent::Map.new
end
def self.details_cache_key(details)
if details[:formats]
details = details.dup
details[:formats] &= Template::Types.symbols
end
@details_keys[details] ||= Concurrent::Map.new
@details_keys[details] ||= Object.new
end
def self.clear
ActionView::ViewPaths.all_view_paths.each do |path_set|
path_set.each(&:clear_cache)
end
ActionView::LookupContext.fallbacks.each(&:clear_cache)
@view_context_class = nil
@details_keys.clear
@digest_cache.clear
end
def self.digest_caches
@details_keys.values
@digest_cache.values
end
def self.view_context_class(klass)
@ -88,7 +98,7 @@ module ActionView
# Calculate the details key. Remove the handlers from calculation to improve performance
# since the user cannot modify it explicitly.
def details_key #:nodoc:
@details_key ||= DetailsKey.get(@details) if @cache
@details_key ||= DetailsKey.details_cache_key(@details) if @cache
end
# Temporary skip passing the details_key forward.
@ -102,7 +112,8 @@ module ActionView
private
def _set_detail(key, value) # :doc:
@details = @details.dup if @details_key
@details = @details.dup if @digest_cache || @details_key
@digest_cache = nil
@details_key = nil
@details[key] = value
end
@ -178,7 +189,7 @@ module ActionView
user_details = @details.merge(options)
if @cache
details_key = DetailsKey.get(user_details)
details_key = DetailsKey.details_cache_key(user_details)
else
details_key = nil
end
@ -205,7 +216,7 @@ module ActionView
end
if @cache
[details, DetailsKey.get(details)]
[details, DetailsKey.details_cache_key(details)]
else
[details, nil]
end
@ -236,6 +247,7 @@ module ActionView
def initialize(view_paths, details = {}, prefixes = [])
@details_key = nil
@digest_cache = nil
@cache = true
@prefixes = prefixes
@rendered_format = nil
@ -245,7 +257,7 @@ module ActionView
end
def digest_cache
details_key
@digest_cache ||= DetailsKey.digest_cache(@details)
end
def initialize_details(target, details)

View file

@ -87,6 +87,7 @@ module ActionView
# Returns an object that is able to render templates.
def view_renderer # :nodoc:
# Lifespan: Per controller
@_view_renderer ||= ActionView::Renderer.new(lookup_context)
end

View file

@ -5,13 +5,21 @@ module ActionView
extend ActiveSupport::Concern
included do
class_attribute :_view_paths, default: ActionView::PathSet.new.freeze
ViewPaths.set_view_paths(self, ActionView::PathSet.new.freeze)
end
delegate :template_exists?, :any_templates?, :view_paths, :formats, :formats=,
:locale, :locale=, to: :lookup_context
module ClassMethods
def _view_paths
ViewPaths.get_view_paths(self)
end
def _view_paths=(paths)
ViewPaths.set_view_paths(self, paths)
end
def _prefixes # :nodoc:
@_prefixes ||= begin
return local_prefixes if superclass.abstract?
@ -29,6 +37,22 @@ module ActionView
end
end
# :stopdoc:
@all_view_paths = {}
def self.get_view_paths(klass)
@all_view_paths[klass] || get_view_paths(klass.superclass)
end
def self.set_view_paths(klass, paths)
@all_view_paths[klass] = paths
end
def self.all_view_paths
@all_view_paths.values.uniq
end
# :startdoc:
# The prefixes used in render "foo" shortcuts.
def _prefixes # :nodoc:
self.class._prefixes

View file

@ -11,9 +11,9 @@ class AVLogSubscriberTest < ActiveSupport::TestCase
def setup
super
ActionView::LookupContext::DetailsKey.clear
view_paths = ActionController::Base.view_paths
view_paths.each(&:clear_cache)
ActionView::LookupContext.fallbacks.each(&:clear_cache)
lookup_context = ActionView::LookupContext.new(view_paths, {}, ["test"])
renderer = ActionView::Renderer.new(lookup_context)

View file

@ -631,9 +631,8 @@ class CachedViewRenderTest < ActiveSupport::TestCase
# Ensure view path cache is primed
def setup
ActionView::LookupContext::DetailsKey.clear
view_paths = ActionController::Base.view_paths
view_paths.each(&:clear_cache)
ActionView::LookupContext.fallbacks.each(&:clear_cache)
assert_equal ActionView::OptimizedFileSystemResolver, view_paths.first.class
setup_view(view_paths)
end
@ -650,9 +649,7 @@ class LazyViewRenderTest < ActiveSupport::TestCase
# Test the same thing as above, but make sure the view path
# is not eager loaded
def setup
view_paths = ActionController::Base.view_paths
view_paths.each(&:clear_cache)
ActionView::LookupContext.fallbacks.each(&:clear_cache)
ActionView::LookupContext::DetailsKey.clear
path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH)
view_paths = ActionView::PathSet.new([path])
assert_equal ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH), view_paths.first
@ -710,10 +707,10 @@ class CachedCollectionViewRenderTest < ActiveSupport::TestCase
# Ensure view path cache is primed
setup do
ActionView::LookupContext::DetailsKey.clear
view_paths = ActionController::Base.view_paths
assert_equal ActionView::OptimizedFileSystemResolver, view_paths.first.class
view_paths.each(&:clear_cache)
ActionView::LookupContext.fallbacks.each(&:clear_cache)
ActionView::PartialRenderer.collection_cache = ActiveSupport::Cache::MemoryStore.new

View file

@ -4,6 +4,7 @@ require "abstract_unit"
class ResolverCacheTest < ActiveSupport::TestCase
def test_inspect_shields_cache_internals
ActionView::LookupContext::DetailsKey.clear
assert_match %r(#<ActionView::Resolver:0x[0-9a-f]+ @cache=#<ActionView::Resolver::Cache:0x[0-9a-f]+ keys=0 queries=0>>), ActionView::Resolver.new.inspect
end
end

View file

@ -7,9 +7,9 @@ end
class SetupFiberedBase < ActiveSupport::TestCase
def setup
ActionView::LookupContext::DetailsKey.clear
view_paths = ActionController::Base.view_paths
view_paths.each(&:clear_cache)
ActionView::LookupContext.fallbacks.each(&:clear_cache)
@assigns = { secret: "in the sauce", name: nil }
@view = ActionView::Base.with_empty_template_cache.with_view_paths(view_paths, @assigns)

View file

@ -24,6 +24,11 @@ module ActionView
DeveloperStruct = Struct.new(:name)
module SharedTests
def setup
ActionView::LookupContext::DetailsKey.clear
super
end
def self.included(test_case)
test_case.class_eval do
test "helpers defined on ActionView::TestCase are available" do