Problem/Motivation

Since #3163002: Make late runtime processor compatible with drush , purge will call all active plugins on every KernelEvents::TERMINATE event. This breaks sites using the Config Split module to have different purgers on different environments.

Steps to reproduce

- Have a site with purge + purge_processor_lateruntime installed
- Using Config Split, enable a module the provides a different purger (for example Acquia Purge), only on environments where the split is active.
- Generate a DB with the 3 modules enabled (which would mimic the "prod" database)
- Locally (where the split that enables Acquia Purge is NOT active), import config with drush config:import, to disable the split and have only the lateruntime processor purger available.

Expected:

Config import happens as usual, the "Acquia Purge" split is disabled, and nothing breaks.

Actual:

Config import breaks with:

> > In Container.php line 156:
> >                                                                           
> >   You have requested a non-existent service "acquia_purge.platforminfo".  
> >                                                                           
> > 
> > config:import [--preview [PREVIEW]] [--source SOURCE] [--partial] [--diff] [-h|--help] [-q|--quiet] [-v|vv|vvv|--verbose] [-V|--version] [--ansi] [--no-ansi] [-n|--no-interaction] [-d|--debug] [-y|--yes] [--no] [--remote-host REMOTE-HOST] [--remote-user REMOTE-USER] [-r|--root ROOT] [-l|--uri URI] [--simulate] [--pipe] [-D|--define DEFINE] [--quiet-abort [QUIET-ABORT]] [--druplicon] [--notify] [--xh-link XH-LINK] [--force-on-live] [--] <command> [<label>]
> > 
> > 

Fatal error: Uncaught LogicException: ::setPurgers() hasn't been called! in /var/lib/tugboat/docroot/modules/contrib/purge/src/Plugin/Purge/Purger/CapacityTracker.php:266
Stack trace:
#0 /var/lib/tugboat/docroot/modules/contrib/purge/src/Plugin/Purge/Queue/QueueService.php(145): Drupal\purge\Plugin\Purge\Purger\CapacityTracker->getRemainingInvalidationsLimit()
#1 /var/lib/tugboat/docroot/modules/contrib/purge/modules/purge_processor_lateruntime/src/EventSubscriber/LateRuntimeProcessor.php(87): Drupal\purge\Plugin\Purge\Queue\QueueService->claim()
#2 [internal function]: Drupal\purge_processor_lateruntime\EventSubscriber\LateRuntimeProcessor->onKernelTerminate(Object(Symfony\Component\HttpKernel\Event\TerminateEvent), 'kernel.terminat...', Object(Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher))
#3 /var/lib/tugboat/docroot/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(142): call_user_func(Array, Object(Symfony\Component\HttpKernel\Event\TerminateEvent), 'kernel.terminat...', Obj in /var/lib/tugboat/docroot/modules/contrib/purge/src/Plugin/Purge/Purger/CapacityTracker.php on line 266

Proposed resolution

My suggestion is to directly revert #3163002: Make late runtime processor compatible with drush

As stated in #3163002-8: Make late runtime processor compatible with drush and subsequent comments, the expectation for drush users is that a CLI execution would NOT trigger web-related events.

Remaining tasks

User interface changes

API changes

Data model changes

Issue fork purge-3264141

Command icon 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

marcoscano created an issue. See original summary.

marcoscano’s picture

Issue summary: View changes
rp7’s picture

My suggestion is to directly revert #3163002: Make late runtime processor compatible with drush

As stated in #3163002-8: Make late runtime processor compatible with drush and subsequent comments, the expectation for drush users is that a CLI execution would NOT trigger web-related events.

I can't comment on whether the Purge module or the Config Split module is in the wrong here, but I believe the proposed resolution is incorrect or badly formulated at the least.

It makes it sound like the Purge module (and more explicitly the change in #3163002: Make late runtime processor compatible with drush) is causing CLI commands to trigger the KernelEvents::TERMINATE event, but that's not the case. Reverting the change in #3163002: Make late runtime processor compatible with drush will not prevent that from happening.

AFAICT this is works as designed in Drush since https://github.com/drush-ops/drush/issues/1252 landed. If it's this that really needs to be changed, than a ticket in Drush issues queue should be created.

marcoscano’s picture

Sorry for the badly worded initial description. (you can see I tried to improve it in #2 :) )

What I meant is that when KernelEvents::TERMINATE is dispatched, now purge calls $this->initialize() on the above-mentioned purger, which will call all active plugins from config, and it will break if the config in DB is not in sync with the codebase (which happens, for example, during the drush config:import command).

Prior to #3163002: Make late runtime processor compatible with drush, the $this->initialize() call was being made on KernelEvents::FINISH_REQUEST events, which aren't triggered by drush config:import

Hope it's clearer now! Thanks!

reszli’s picture

Indeed, we ran into the following errors after updating when running config import:

You have requested a non-existent service "custom_purge.default".

[error] diagnostics: ERROR: Purgers: There is no purger loaded which means that you need a module enabled to provide a purger plugin to clear your external cache or CDN.

second (or third) config import actually fixes it, but it's not ideal

japerry made their first commit to this issue’s fork.

japerry’s picture

During drush config:import, Config Split swaps out different config sets per environment. If the database config says "use purger X" but the codebase's service container doesn't know about purger X (because it's in a different Config Split environment), purge LateRuntimeProcessor::onKernelTerminate() fires and eventually calls initializePurgers(), which calls $this->pluginManager->createInstance($plugin_id, ['id' => $id]). That internally tries to construct a service that doesn't exist → ServiceNotFoundException.

The existing fix (88546a6e) is incomplete: It catches ServiceNotFoundException per purger in initializePurgers(), but the issue still surfaces during config imports because:

  1. The initialize() method in LateRuntimeProcessor calls purge.processors->get('lateruntime'), which calls initializePluginInstances(), which calls pluginManager->createInstance() — processor plugins can also throw ServiceNotFoundException.
  2. More fundamentally, the event subscriber should not run processing during a config import — it's the wrong time to do it since config is in a transient state.

The correct fix: In LateRuntimeProcessor::onKernelTerminate(), check if a config sync is in progress using Drupal's config.installer service (isSyncing()) and bail out early. This is the most correct approach: don't process the queue while config is being imported.

japerry’s picture

Status: Active » Needs review

  • japerry committed 3f1875b0 on 8.x-3.x
    Issue [#3264141] Don't run the LateRunTimeProcessor when performing a...
japerry’s picture

Status: Needs review » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.