Skip to content

Register dependency tracker so nested partials invalidate parent caches#620

Open
rosa wants to merge 1 commit into
rails:mainfrom
rosa:register-dependency-tracker
Open

Register dependency tracker so nested partials invalidate parent caches#620
rosa wants to merge 1 commit into
rails:mainfrom
rosa:register-dependency-tracker

Conversation

@rosa

@rosa rosa commented Jul 16, 2026

Copy link
Copy Markdown
Member

We ran into a caching issue at Basecamp with a template that looked like this:

json.cache! ... do
  # ...
  
    json.people people, partial: "people/person", as: :person
end

We changed the people/_person.json.jbuilder template and deployed, yet responses using that template kept returning a stale cached version of the person.

We traced this down to nothing registering Jbuilder's dependency tracker with Action View. Its railtie requires it, but nothing registers it with ActionView::DependencyTracker. Without a registered tracker, find_dependencies returns [] for every .jbuilder template, so the digestor sees no nested-partial dependencies: a json.cache! fragment that renders inner partials never mixes those partials' digests into its own cache key. Editing an inner partial then leaves outer fragments serving stale cached JSON until they happen to invalidate for some unrelated reason. ERB doesn't have this problem because Action View registers ERBTracker.

This is a regression from #504, which rewrote the tracker as a standalone class and dropped the register_tracker :jbuilder call (and the ERBTracker inheritance that used to supply it) without re-adding it. It has shipped unregistered since 2.12.0.

Register the tracker from the same on_load(:action_view) block that registers the template handler, and declare supports_view_paths? so register_tracker passes the view paths through to the tracker. Otherwise, Rails wraps it in a lambda that drops them and the tracker's own wildcard (# Template Dependency: foo/*) resolution silently returns nothing.

Jbuilder ships `Jbuilder::DependencyTracker` and its railtie requires it,
but nothing registers it with `ActionView::DependencyTracker`. Without a
registered tracker, `find_dependencies` returns `[]` for every `.jbuilder`
template, so the digestor sees no nested-partial dependencies: a
`json.cache!` fragment that renders inner partials never mixes those
partials' digests into its own cache key. Editing an inner partial then
leaves outer fragments serving stale cached JSON until they happen to
invalidate for some unrelated reason. ERB doesn't have this problem
because Action View registers `ERBTracker`.

This is a regression from rails#504, which rewrote the tracker as a standalone
class and dropped the `register_tracker :jbuilder` call (and the `ERBTracker`
inheritance that used to supply it) without re-adding it. It has shipped
unregistered since 2.12.0.

Register the tracker from the same `on_load(:action_view)` block that
registers the template handler, and declare `supports_view_paths?` so
`register_tracker passes` the view paths through to the tracker.
Otherwise, Rails wraps it in a lambda that drops them and the tracker's
own wildcard (`# Template Dependency: foo/*`) resolution silently returns
nothing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant