/ rails Public
Disable ActionView::Template finalizers in test environment #32418
Add this suggestion to a batch that can be applied as a single commit. This suggestion is invalid because no changes were made to the code. Suggestions cannot be applied while the pull request is closed. Suggestions cannot be applied while viewing a subset of changes. Only one suggestion per line can be applied in a batch. Add this suggestion to a batch that can be applied as a single commit. Applying suggestions on deleted lines is not supported. You must change the existing code in this line in order to create a valid suggestion. Outdated suggestions cannot be applied. This suggestion has been applied or marked resolved. Suggestions cannot be applied from pending reviews. Suggestions cannot be applied on multi-line comments. Suggestions cannot be applied while the pull request is queued to merge.
To avoid expensive finalization of
ActionView::Templateinstances at the end of large test suites, this adds a configuration option to disable the finalizers, and uses it to disable them in the test environment.
ActionView::Template instances compile their source to methods on the ActionView::CompiledTemplates module. To prevent leaks in development mode, where templates can frequently change, a finalizer is added that undefines these methods when the templates are garbage-collected.
This is undesirable in the test environment, however, as templates don't change during the life of the test, so there's no leak to be avoided. Moreover, the cost of undefining a method is proportional to the number of descendants a class or module has, since the method cache must be cleared for all descendant classes.
ActionView::CompiledTemplatesis mixed into
ActionView::TestCase(or in RSpec suites, every view example group), it can end up with a very large number of descendants, and undefining its methods can become very expensive. In large test suites, this results in a long delay at the end of the test suite as all template finalizers are run, only for the process to then exit.
To avoid this unnecessary cost, this change adds a config option,
action_view.finalize_compiled_template_methods, defaulting to true, and sets it to false in the test environment only.
I've built a small reproduction of this issue using RSpec, illustrating a test suite that has 1,000 templates. For this suite the finalization cost at the end of the run is approximately 30 seconds, which is comparable to the delay seen in the app I'm currently working on (~45s).
I've confirmed that this patch addresses the performance issue. However, I'm not entirely sure that exposing a configuration option is the best thing in this instance, since it's inevitably a rather obscure flag that the vast majority of users probably won't want to touch.
Any and all feedback is obviously welcome, but in particular if anyone has a better idea for making this environment-specific change, I'd be very open to suggestions.