Updated: Comment #N
Problem/Motivation
When some code needs to rebuild the router, it calls setRebuildNeeded() instead of calling rebuild() directly, in order to prevent multiple rebuilds in one request.
RouterRebuildSubscriber subscribes to KernelEvents::TERMINATE to check if it was set to rebuild, and it runs then.
However, in the case of a rebuild triggered right before a redirect, the redirect will happen *before* the router even starts rebuilding, and the route rebuilding only gets about 30% done before the next request starts.
In the case of #2229303: Enable/add of search pages leads to route not found exception, the redirected page tries to use a route that has not been built yet, and it fatals.
Proposed resolution
N/A
Remaining tasks
Find some way to fix this.
User interface changes
N/A
API changes
N/A
| Comment | File | Size | Author |
|---|---|---|---|
| #49 | 2230091-44.patch | 726 bytes | leo liao |
| #43 | 2230091-43-interdiff.txt | 316 bytes | berdir |
| #43 | 2230091-43.patch | 7.53 KB | berdir |
| #41 | 2230091-41-interdiff.txt | 3.41 KB | berdir |
| #41 | 2230091-41.patch | 7.97 KB | berdir |
Comments
Comment #1
dawehnerThis should be it.
Comment #2
jhodgdonI manually tested this patch, and it fixes #2229303: Enable/add of search pages leads to route not found exception.
Comment #3
tim.plunkettWow, very simple and elegant. Thanks @dawehner!
We also need to add this to getRoutesByPath() in the same class.
As I understand it, until #2205527: Move configuration import lock to lock.persistent service since a lock can not exist beyond a single request goes in, we'll actually be rebuilding the router twice.
Comment #4
dawehnerSure, let's do it.
Comment #7
dawehnerThe reason for this failure is the following problem: People call getRouteByName during route rebuildling. (rest (yes rest!), config_translation),
which result in infinite recursion.
This is really fragile if you change anything. #2158571: Routes added in RouteSubscribers cannot be altered solves a couple of those problems,
by passing along the route collection during route rebuild time.
Comment #8
jhodgdonThere are good reasons for people calling getRouteByName (directly or indirectly) during dynamic route creation, for instance so that they can base their URLs on existing static routes. Presumably they wouldn't need the dynamic routes for this?
Comment #9
dawehnerI disagree that you should do that. It was indeed just implementation detail that this kinda worked in most cases currently.
In the linked patch you have events you can use which retrieve all the previous defined routes, just not accessible via the route provider interface.
Comment #10
jhodgdonThat sounds like a good idea. This should be documented on the getRouteByName and other methods that shouldn't be called during dynamic route creation.
Comment #11
tim.plunkett4: route_provider-2230091-4.patch queued for re-testing.
Comment #13
tim.plunkettPosting the backtrace in 3 chunks, to call out the important part:
So it is rebuilding, and runs the alter event.
The REST alter tries to find a definition of the entity:node resource
But the definitions were just cleared
So the REST EntityDerivative runs, and tries to find the route...
Comment #14
dawehnerIf things would be always simple ...
Comment #16
catchLooks critical to me.
Comment #17
catchMatcherDumper::dump() is supposed to do this in a transaction to avoid the empty router table issue. This issue proves that we can't rely on transactions for that - likely default transaction isolation level isn't enough to protect against this.
https://api.drupal.org/api/drupal/includes%21menu.inc/function/menu_get_... had a check for menu rebuild as well as an empty menu masks variable.
menu_rebuild() had a lock to prevent stampedes.
Looks like both of those need equivalents adding back.
Comment #18
dawehnerWell, the router rebuilder has a lock.
Comment #19
martin107 commentedPatch no longer applied, needed reroll. RouteProviderTests pass locally
Comment #21
martin107 commentedThis patch still applies, but much has changed in this area.. so I am about to retest.
Retest wipes away test data so I am providing a link to it here, currently 4 fails, 1 exception
https://qa.drupal.org/pifr/test/805923
Comment #23
dawehner@martin107
If you re-test a patch you can't really access previous results. In case these errors are helpful consider to just reupload a patch.
Comment #25
dawehnerposting the backtrace which lead to the current exceptions:
There seems to be something going on wrong during rebuild of the routes.
Comment #26
dawehnerSadly this is kind of similar as the failure in #2327965: drupal_flush_all_caches() doesn't clear all plugin caches
Comment #29
martin107 commentedI won't trigger another retest ... but just to highlight this extract from the test errors.
Allowed memory size of 335544320 bytes exhausted (tried to allocate 130968 bytes)
Comment #30
dawehnerYeah this is probably caused by some recursive call of the route rebuilding I can't really figure out yet.
Comment #31
mgiffordComment #32
jhodgdonmgifford - this issue was already marked "related" from that other one. Not really necessary to have it in the sidebar twice... :)
Comment #33
mgiffordSorry, thanks for removing it. Most postponed issues I was looking at weren't properly related to why they were postponed. I definitely made a few mistakes.
Comment #34
marthinal commentedIt fixes #2229303. Rerolled. Let's take a look at the test results.
Comment #36
fabianx commentedArchitectural question:
- Isn't it much simpler to decrease the priority of the rebuild to happen after the redirect.
Then check if a rebuild is needed on route request (like done in D7 with menu_reubild_needed()) - need to do that anyway as a process can die at any time ...
Comment #39
berdirTrying a combined patch of this an #2248297: Ensure routes are rebuilt when install modules, just to see what happens.
Comment #41
berdirOk, back to the previous patch. Found the problem, it was a recursion in config_translation.module.
Fixed that, checked with @tstoeckler, we no longer need that. Also added a recursion protection so we have something more useful to look at.
Comment #42
fabianx commentedThis is probably a debugging oversight?
Besides that: RTBC - Nice work!
This is touching this area of code, so adding related issue of #356399: Optimize the route rebuilding process to rebuild on write.
Comment #43
berdirYep.
Comment #44
fabianx commentedRTBC, then!
Comment #46
catchLooks great. Was wondering if we need to document more why we throw the exception, but can't really think of succint docs for that.
Committed/pushed to 8.0.x, thanks!
Comment #47
clemens.tolboomI get the new exception triggered on module enable / drush cache-rebuild. See #2351353: What to do with "Recursive router rebuild detected."
Comment #49
leo liao commentedPHP8.0, Drupal 9.3.16
- Install with standard profile.
- Go to Configuration > Search and Meta Data > Search pages (admin/config/search/pages)
- Add a new Content search page.
Symfony\Component\Routing\Exception\RouteNotFoundException: Route "search.view_test" does not exist. in Drupal\Core\Routing\RouteProvider->getRouteByName() (line 206 of docroot/core/lib/Drupal/Core/Routing/RouteProvider.php).