Repository navigation
Only register the compile cache executor hook without template caching - #2728
Merged
joelhawksley merged 6 commits intoOct 19, 2026
Conversation
The executor hook that clears the compile cache before each request did nothing when Action View caches templates, as in production, but it still read `ActionView::Base.cache_template_loading` on every request. That reads `ActionView::Resolver`'s `@@caching` class variable, which non-main Ractors can't read on Ruby 4.0, so under Rails' experimental `Rails.application.ractorize!` every request failed once view_component was loaded. Decide once, when Action View loads, whether to register the hook. Rails' Action View railtie likewise registers its view reloader only when template caching is off. The hook still checks the flag on each run, so toggling it at runtime, with caching off at boot, works as before.
Resolve conflicts with ViewComponent#2730's template watcher. Move hook registration into CompileCache.watch_templates so the cached-templates case is tested in-process instead of in a subprocess, and drop the runtime caching check, which only existed to support a test that toggled caching after boot. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f2e356ff-16cd-4517-8e63-a8600747d3fa
Since template caching is now decided at boot, with_template_caching also skips the template watcher hook the sandbox registered. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f2e356ff-16cd-4517-8e63-a8600747d3fa
…olation Boot a fresh development or production app per test in a forked process, the way railties tests do, instead of an Open3 subprocess or an extracted method. These tests run in their own rake task since the sandbox is already booted in the test process. Inline the executor hook back into the engine, and restore ViewComponent#2730's integration tests that a previous commit dropped by mistake. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f2e356ff-16cd-4517-8e63-a8600747d3fa
Booting a production app tests template caching as decided at boot, so the sandbox no longer needs to fake it by skipping the watcher hook. Remove the now-unused with_template_caching helper. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f2e356ff-16cd-4517-8e63-a8600747d3fa
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.Suggestion cannot be applied right now. Please check back later.
What are you trying to accomplish?
Rails main has an experimental
Rails.application.ractorize!that serves requests from non-main Ractors on Ruby 4.0. Once view_component is loaded under it, every request fails, including requests that don't render a component:The executor hook in
engine.rbruns before every request and readsActionView::Base.cache_template_loading, which isActionView::Resolver's@@cachingclass variable. In production that flag is true, so the hook does nothing, but it still reads the flag.What approach did you choose and why?
The template watcher hook (from #2730) is now registered only if template caching is off when Action View loads. Inside
ActiveSupport.on_load(:action_view), Rails' ownaction_view.cachinghook has already set the flag, so this sees the app's real setting. The flag is checked once, at boot. Rails' Action View railtie makes the same decision for its own template cache expiry: it registers that hook only when templates aren't cached.Nothing changes in development and test: the hook is registered and watches templates as before.
I considered recomputing the flag from
config.action_view.cache_template_loadingandreloading_enabled?inafter_initialize, the way Rails' railtie does. I didn't, because it copies Rails' defaulting logic and misses apps that setActionView::Base.cache_template_loadingthemselves.Anything you want to highlight for special attention from reviewers?
New isolated tests.
test/isolated/template_caching_test.rbusesActiveSupport::Testing::Isolation, the way railties tests do, to boot a fresh app in a forked process for each test:ActionView::Resolver.caching?nor builds the template watcher.The two production tests fail if the boot-time check is removed.
These tests can't share a process with the sandbox, which is already booted in test, so they run in a new
rake isolated_testtask. That task is excluded fromrake testand runs as part ofall_tests.Moved test.
test_template_changes_are_not_reflected_on_new_request_when_cache_template_loading_is_trueturned template caching on after the sandbox had booted, but caching is now decided at boot. It moved to the isolated tests astest_template_changes_are_not_reflected_on_new_request_when_templates_are_cached, which boots a production app with a real component on disk, changes the template between requests, and checks the response doesn't change. Thewith_template_cachinghelper had no remaining callers, so it's removed.Scope. This only stops the per-request failure. Rendering a component in a Ractor still fails. The next error is
@__vc_compiler(base.rb, aCompilerholding aMutex), and after that comesCompileCache.cache(compile_cache.rb, amattr_accessor, so a class variable holding aSet). I'm happy to look at those separately.Rails side.
ActionView::Resolver.cachingis acattr_accessor. On Ruby master (4.1), non-main Ractors can read class variables that hold shareable values (see Make I18n locale switching work in non-main Ractors rails/rails#58974), and on 4.0 Rails may move it off a class variable. This change doesn't depend on either.Tests:
bundle exec rake test: 653 runs, 0 failuresbundle exec rake isolated_test: 3 runs, 0 failuresbundle exec rake engine_test: 5 runs, 0 failuresI also checked it in a Rails main app under
ractorize!, with requests running in a non-main Ractor. Before:GET /postsfailed with the error above. After:GET /posts -> 200andGET /stack/plain-helpers -> 200.Conformance case
test/isolated/template_caching_test.rb:test_executor_does_nothing_when_templates_are_cachedIt boots a production app in a forked process, wraps a request in the executor, and asserts that
ActionView::Resolver.caching?is never read and no template watcher is built. It needs no Ractor, so it runs on every Ruby in the matrix. It fails again if anything on the per-request executor path starts reading that class variable while templates are cached.