Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
When uninstalling a module from drush I was left with a broken site with a exception being thrown during a route rebuild. Drush could probably help but the route rebuilder should be more resilient as well since there are many reasons a plugin could go away.
Steps to reproduce:
- Install the minimal profile
- Install the Rest module
- Uninstall the Node module
- Kaboom
Proposed resolution
Options:
1) Try catch around instantiation.
2) Check the plugin existence explicitly.
Remaining tasks
Tests, review.
RC eligible. Fatal error bug fix.
Comment | File | Size | Author |
---|---|---|---|
#21 | 2609152-21.patch | 2.02 KB | MattA |
#2 | rest_plugins_can_cause-2609152-2.patch | 1.05 KB | neclimdul |
Comments
Comment #2
neclimdulHere's the quick fix I applied. This should be pretty "exceptional" so I like the exception approach.
Needs tests. Needs docs. Need to decide if a watchdog would help?
Comment #3
neclimdulComment #4
catchWhile #2589967: Rebuild routes immediately when modules are installed is about installation, is this related to the fact that the actual uninstall of the module vs. the route rebuild happen out of sync?
Given enabled resources come from config, how is the route rebuild happening before the config gets updated?
Comment #5
dawehnerIts a general problem that we haven't thought at all about when we catch our exceptions and when not.
This sounds like we forgot the rebuild the router when we uninstall in Drush?
Comment #6
EclipseGc CreditAttribution: EclipseGc commentedJust looking at the code, this could benefit from calling hasDefinition before trying to get the instance, also createInstance instead of getInstance.
Eclipse
Comment #7
Crell CreditAttribution: Crell at Palantir.net commentedI think I'd prefer to check for the existence of the plugin before instantiating, rather than catching the exception. This is a small part of route rebuild so an extra function call or three isn't going to really hurt anything. However, will this also self-clean on the next rebuild (ie, it only happens during the rebuild right after the module is disabled) or does it indicate we have stale config data lying around?
Comment #8
EclipseGc CreditAttribution: EclipseGc commentedAlso, #2579235: Resource plugin manager needlessly calls wrong method to instantiate plugins touches the same code, and if we're messing with getInstance() here, we might as well remove that call in favor of createInstance() and simplify the patch in 2579235 further.
Eclipse
Comment #9
neclimdulre #5, no drush is rebuilding and that is why it is blowing up. Its like the uninstall page specifically doesn't? not sure yet why one fails and the other doesn't.
Comment #10
neclimdul99.999(maybe a few more 9s even)% of rebuilds will never need the check making this "exceptional" which is why I went with the try/catch.
Comment #11
catchI'd like to know why we hit this though - it could be missing config dependencies, it could be drush rebuilding the router before the config item gets updated.
Comment #12
neclimdul#11I'll see what I can find. It could be possible for this to happen to developers building plugins too.
Comment #13
MattA CreditAttribution: MattA commentedJust ran into this myself. It does not appear to be a problem with drush as it is enough to simply visit rebuild.php after uninstalling your module.
It seems drupal_rebuild() calls drupal_flush_all_caches() which calls the rebuild method of the 'router.builder' service. This then calls the rest module's route subscriber which reads rest.settings from config and contains the plugin id for the now uninstalled plugin.
re: #7
Since rest.settings is not cleared during a rebuild, this means that we have stale data sitting around, which also means that all future rebuilds will end with a fatal error as well.
As for the patch, here are some quick-fix options (long-term: use config entities/dependencies for this instead?), sorted by preference:
Comment #14
MattA CreditAttribution: MattA commentedPending #8 and #13.
Comment #15
catchI think #13.1 ought to be enough without #13.2 - if it's not that's another problem.
Bumping to major - for me this is borderline critical - it's both a fatal error via the UI and a data-integrity issue, given modules providing plugins in general can expect to have them correctly handled by configuration dependencies for cases like fields/views etc you'd not expect to be manually removing things from other people's config objects.
Comment #16
alexpottI think this is a issue with rest.settings.yml being a simple config file - it should have been broken up into a config entity per entity type and then dependencies would have been the solution. But I think we're well past that point. The simplest solution to problem would seem to be that the Rest module should implement hook_modules_uninstalled to remove entity types when a module that supplies them is removed.
Comment #17
catchYes #16 is exactly my thoughts here.
#15 didn't actually bump this to major, doing that now.
Comment #18
neclimdulNo, I'm pretty sure we need the try catch. As noted in the summary "the route rebuilder should be more resilient as well since there are many reasons a plugin could go away." With derivatives and alters, config can change the existence of plugins. Also, during development, developers could rename or remove plugins as they solve problems leaving the rebuild broken.
Comment #19
alexpott@neclimdul fair enough but we also need to clean up the cause of this particular problem - rest.settings.
Comment #20
Crell CreditAttribution: Crell at Palantir.net commentedUnder the circumstances I'd support both trying to clean up the data (as described in #16) and catching the exception. But the exception, if caught, should log rather than just swallow silently as it indicates there is still a problem to be resolved.
Comment #21
MattA CreditAttribution: MattA commentedHad a few minutes to spare. Includes changes from #13.1/#16 and an if-block version of #2 with the added log warning as per #20. Still needs tests.
Other notes:
Only module uninstalls will trigger the clean up of rest.settings, so it's still a pretty small band-aid over the data-integrity part of the issue.
The other log messages in alterRoutes() probably don't work because the placeholder and replacement keys are different?!?!
Comment #22
MattA CreditAttribution: MattA commentedShould be removed.
Comment #23
catchIf we're not going to let the exception get thrown (although I still don't see why we wouldn't during development), this should be a trigger_error() so that the error is visible and causes test failures.
Comment #24
alexpottAdding steps to reproduce to the IS.
Comment #25
alexpottComment #26
dawehnerYou could simplify this code by using
$resources = array_intersect_key($resources, $definitions);
we no longer need this
Comment #27
MattA CreditAttribution: MattA commentedHuh, was going through my issue feed and it seems like I came across this problem 5 months ago and forgot about it. This issue is actually a duplicate of #2397271: REST configuration fails if it contains a plugin reference that does not exist (but the patch there only fixes the fatal error).
That issue also lists another interesting way to reproduce the problem though. Apparently you can't install a profile that contains a rest.settings.yml file with settings for plugins defined by a custom module.
As per #9 in that issue, it also means that we shouldn't clean up rest.settings in the route subscriber, and should add a comment to that effect in whichever patch is chosen.
Comment #28
neclimdulStill prefer the try/catch to the unnecessary method call but *shrug*
Comment #29
catchI also prefer the try/catch - it's a bit more honest about what's happening, given this is an invalid state.
Comment #30
Wim LeersThis is indeed a duplicate of #2397271: REST configuration fails if it contains a plugin reference that does not exist.
And the eventual solution we want is #2308745: Remove rest.settings.yml, use rest_resource config entities.
Comment #31
xjm