Problem / Motivation
Running drush updatedb takes over 2 minutes on sites that use the jsonapi_views module, even when there are no pending database updates. The delay occurs entirely inside drupal_flush_all_caches(), specifically inside \Drupal\Core\Routing\RouteBuilder::rebuild(), triggered by jsonapi_views calling ResourceTypeRepository::get() for every view and bundle combination.
Root cause chain
drush updbalways callsdrupal_flush_all_caches()unconditionally — even when no schema updates ran. This is by design for compatibility with updates that rely on cache clearing. It runs inside the UpdateKernel.- UpdateKernel replaces the entire cache factory with
UpdateBackend(UpdateCacheBackendFactory, introduced in #3055443: Switch to a null backend for all caches for running the database updates).UpdateBackendextendsNullBackend: all cache reads return nothing, all writes are discarded. cache.jsonapi_resource_typesis aBackendChain(introduced in #3018287: ResourceTypeRepository computes ResourceType value objects on *every request*) combining an in-memory backend and the default DB backend. This is the service injected intoResourceTypeRepository.UpdateBackendwraps — and effectively replaces — the entireBackendChain. The memory layer inside the chain is never reached becauseUpdateCacheBackendFactorydecoratescache_factorybefore the chain is consulted.ResourceTypeRepository::all()has no instance-level fallback cache. Every call checks$this->cache->get('jsonapi.resource_types'), gets nothing back fromNullBackend, then performs a full rebuild: iterates all entity types × all bundles, callscreateResourceType()andcalculateRelatableResourceTypes()for each, and dispatches events. This is expensive (1–8 seconds per call depending on site size).jsonapi_viewsgenerates routes by callingResourceTypeRepository::get()once per view+bundle. On a moderately sized site this triggers dozens of fullall()rebuilds, totalling ~120 seconds during a singledrush updb.
Measurements (Drupal 10.6.8, 187 modules, 23 views, 9 entity types)
| State | drush updb time |
|---|---|
| Before any fix | ~2 minutes |
| After jsonapi_views [#3484714] patch only | ~80 seconds |
After adding $allResourceTypes instance cache to all() |
~4 seconds |
Profiling was done by adding error_log() timing instrumentation to drupal_flush_all_caches() and RouteBuilder::rebuild() step by step. The full breakdown:
[FLUSH] cache_flush+deleteAll: 0.19s [FLUSH] rebuildContainer: 0.09s [FLUSH] plugin cache_clearer: 0.03s [ROUTER] getRouteDefinitions (YAML scan): 0.03s [ROUTER] route object creation loop: 120.38s ← all time here [ROUTER] jsonapi_views provider: 116.16s ← single contributor [ROUTER] dumper->dump: 0.27s
The 116 seconds in jsonapi_views came from repeated calls to ResourceTypeRepository::all() with a NullBackend active.
Steps to reproduce
- Install Drupal with
jsonapiandjsonapi_viewsenabled. - Create 10 or more Views over entity types that have multiple bundles (e.g.
node). - Run
time drush updb. - Observe the delay after
[success] No pending updates.
Proposed resolution
Add an instance-level property to ResourceTypeRepository that caches the result of all() within the same service instance. This is not affected by which cache backend is active.
Why this should be safe
- No stale data risk across container rebuilds. A new
ResourceTypeRepositoryinstance is created every timerebuildContainer()is called (which happens insidedrupal_flush_all_caches()). The property resets with each new instance. - No change to normal web request behavior. The existing
BackendChainmemory layer already handles intra-request caching in production. The property only adds value when that layer is bypassed — i.e. whenUpdateBackendis active or any other context whereNullBackendis substituted. - This mirrors the approach proposed in #3016733: Simplify ResourceTypeRepository; use a protected property in place of an in-memory cache bin., which was superseded by the
BackendChainsolution in #3018287: ResourceTypeRepository computes ResourceType value objects on *every request*. TheBackendChainapproach has this blind spot in the UpdateKernel, which was not discussed at the time.
Remaining concern
Even with this core fix applied, jsonapi_views still calls ResourceTypeRepository::get() once per view+bundle on the first all() within a new container instance. The companion issue [#3484714] (already RTBC) addresses that separately by caching results per entity type in jsonapi_views/src/Routing/Routes.php. Both fixes together are what produce the ~4 second result above.
Issue fork drupal-3591918
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
manuel garcia commentedComment #5
manuel garcia commentedComment #6
smustgrave commentedThank you for reporting
Fixes would need to land in the main branch first but as a bug will need test coverage
Comment #9
manuel garcia commentedThank you @smustgrave for having a look so quickly.
I have now closed the
11.4.xMR and opened a new one formain. Tests still pending.Comment #10
manuel garcia commentedOK I added a test to
ResourceTypeRepositoryTestand cleaned things up a little bit. I had to haveResourceTypeRepositoryimpelementCacheTagsInvalidatorInterfacein order to reset the$this->allproperty when the relevant tags are invalidated.