Rules registers no listeners on rare occasions.

Created on 10 October 2016, over 7 years ago
Updated 21 March 2024, 3 months ago

I've run into a bug which is unfortunately not readily reproducible.

[Edit: You can reproduce this bug reliably with drush cron, among other drush commands. drush cache-rebuild does not trigger this issue, but many (most) other drush commands do.]

Seemingly randomly on container rebuilds the Generic Events Listener will not register events, thus critical functionality may not be triggered. I believe this is due to the state service not being available for some reason.

src/EventSubscriber/GenericEventSubscriber.php::getSubscribedEvents()
    // If there is no state service there is nothing we can do here. This static
    // method could be called early when the container is built, so the state
    // service might no always be available.
    if (!\Drupal::hasService('state')) {
      return [];
    }

Couldn't we fallback to querying for the rules events we want to subscribe to instead of just not subscribing to any of them? It would be expensive but much safer than returning nothing at all.

[Update: Newest patch adds some static events to trigger a container rebuild to get the dynamic subscriptions after the container is available, and logs the container rebuild with the triggering event.]

🐛 Bug report
Status

Needs work

Version

3.0

Component

Events

Created by

🇺🇸United States lahoosascoots

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • 🇺🇦Ukraine shoroshanska

    Setup: D10.0.0, PHP 8.1.9, typed_data 8.x-1.x-dev

    I got to this issue after rebuilding the cache via drush - drush cr.
    The patch #102 solves the issue for me.

    drush cc router also gets to a state that rules fire.

  • 🇺🇸United States Christian DeLoach

    @drupal-ramesh, be sure to apply both patches. There are two patches, one for Core and another for Rules. It looks like you may have installed the Rules patch only.

  • 🇦🇺Australia richard.walker.ardc

    I've been using the patches at #102, which for me solve the immediate issue. However, I just noticed that they seem to break something else: after installing the patch, install.php no longer shows the nice "Drupal already installed" page but a page showing exceptions with a stacktrace.

    To reproduce:
    Install rules without this patch.
    Go to .../core/install.php. Observe the "Drupal already installed" page.
    Install patch #102. Reload .../core/install.php.

    I see a page with this content:

    Additional uncaught exception thrown while handling exception.
    Original
    
    Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: You have requested a non-existent service "state". in Symfony\Component\DependencyInjection\ContainerBuilder->getDefinition() (line 1030 of /...path-omitted.../dev/vendor/symfony/dependency-injection/ContainerBuilder.php).
    
    Symfony\Component\DependencyInjection\ContainerBuilder->getDefinition('state') (Line: 600)
    Symfony\Component\DependencyInjection\ContainerBuilder->doGet('state', 1) (Line: 558)
    Symfony\Component\DependencyInjection\ContainerBuilder->get('state') (Line: 488)
    Drupal::state() (Line: 89)
    Drupal\rules\EventSubscriber\GenericEventSubscriber::getSubscribedEvents() (Line: 37)
    Drupal\Core\DependencyInjection\Compiler\RegisterEventSubscribersPass->process(Object) (Line: 94)
    Symfony\Component\DependencyInjection\Compiler\Compiler->compile(Object) (Line: 762)
    Symfony\Component\DependencyInjection\ContainerBuilder->compile() (Line: 1344)
    Drupal\Core\DrupalKernel->compileContainer() (Line: 948)
    Drupal\Core\DrupalKernel->initializeContainer() (Line: 20)
    Drupal\Core\Installer\InstallerKernel->initializeContainer() (Line: 487)
    Drupal\Core\DrupalKernel->boot() (Line: 426)
    install_begin_request(Object, Array) (Line: 116)
    install_drupal(Object) (Line: 48)
    
    Additional
    
    Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: You have requested a non-existent service "theme.manager". in Symfony\Component\DependencyInjection\ContainerBuilder->getDefinition() (line 1030 of /...path-omitted.../dev/vendor/symfony/dependency-injection/ContainerBuilder.php).
    
    Symfony\Component\DependencyInjection\ContainerBuilder->getDefinition('theme.manager') (Line: 600)
    Symfony\Component\DependencyInjection\ContainerBuilder->doGet('theme.manager', 1) (Line: 558)
    Symfony\Component\DependencyInjection\ContainerBuilder->get('theme.manager') (Line: 659)
    Drupal::theme() (Line: 22)
    _drupal_maintenance_theme() (Line: 506)
    drupal_maintenance_theme() (Line: 1025)
    install_display_output(Array, Array) (Line: 271)
    _drupal_log_error(Array, 1) (Line: 365)
    _drupal_exception_handler(Object)
    
    

    Undo the patches. Reload .../core/install.php. You get the "Drupal already installed" page again.

  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 7.3 & MySQL 5.7
    last update 12 months ago
    392 pass, 7 fail
  • 🇩🇪Germany mxh Offenburg

    Sorry for the noise here, was trying for yet another approach. Maybe it was even already discussed and tryed before here, didn't fully understand every detail that was raised in this issue.

    Attached a patch that is using following approach:

    • Excluding the GenericEventSubscriber from the general service collector, as it may be called very early where not all container services are yet available (especially the state service).
    • Instead, GenericEventSubscriber::getSubscribedEvents would be called on behalf of the event dispatcher in the moment the event_dispatcher service is being instantiated by the service container.

    This approach might be more reliable, but with the cost of performance as it would read from the state key-value table everytime the event_dispatcher is being instantiated. Might be interesting to see whether that is noticeable.

    I tried using the decorator pattern first on the event_dispatcher service, but unfortunately then the Drupal\Core\DependencyInjection\Compiler\RegisterEventSubscribersPass does not work anymore (maybe because of decorating a service, its definition is not yet available on that point, could be a core bug). So the only way I saw was to override the service definition itself.

    Haven't thoroughly tested this approach, was just curious whether it may be a way without the need to patch core itself.

  • The last submitted patch, 131: 2816033-event-dispatcher-131.patch, failed testing. View results
    - codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

  • 🇩🇪Germany mxh Offenburg

    Setting back to NR in favor of #102.

  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 7.3 & MySQL 5.7
    last update 11 months ago
    PHPLint Failed
  • 🇩🇪Germany mxh Offenburg

    Adjusted patch from 131 a bit so that the event dispatcher can be refreshed on runtime.

  • 🇺🇸United States TR Cascadia

    Thanks for looking at this. I haven't sat down to try out your patch yet, but I'm really interested in seeing new approaches to the problem.

    I don't know if I've explicitly said it before, but the problem as I see it is that in order to allow for configurable rules, Rules needs to be able to dynamically change event subscribers. But Symfony/Drupal only builds the list of event subscribers once, and that happens when the container is built. The architecture of Symfony/Drupal does not currently permit event subscribers to be changed dynamically, so what Rules has to do here is work-around this limitation in Symfony/Drupal. And the work-around we use doesn't always work.

    What Rules does now is, when we change event subscribers (for example when we add/modify/delete a rule) we force a container rebuild in order to force Symfony/Drupal to recalculate its list of event subscribers.

    The reason this sometimes fails is that during the container rebuild, when Symfony/Drupal is calculating what the event subscribers are, we can't guarantee that ANY service defined in the container is available yet - because the container is incomplete at this point. So in public static function getSubscribedEvents(), we can't currently count on using ANY service because it may not work.

    My hack in #102 adds a core change to pass the "old" container into getSubscribedEvents() so that we can use the services of the "old" container while Symfony/Drupal is in the middle of building the new container. This IS guaranteed to work, but it requires a change to core.

    We need a way to persist the list of events that Rules uses across container rebuilds, so that when Symfony/Drupal builds a new container we can tell it which events we want to subscribe to. This is not a static list that we can hardwire into some static getSubscribedEvents() method - it is a dynamic list that we have to determine on-the-fly based on the existing Rules configurations. No matter how you store this dynamic list, via the state service like we currently use, or explicitly in the DB, or on the file system, or in configuration, we need to use a service in getSubscribedEvents() to retrieve this list. And that's the problem.

  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 7.3 & MySQL 5.7
    last update 11 months ago
    410 pass, 2 fail
  • 🇩🇪Germany mxh Offenburg

    we need to use a service in getSubscribedEvents() to retrieve this list. And that's the problem.

    Looks like this may be a circumstance that wasn't enough considered when the performance optimization was implemented within Drupal core regards the event dispatcher. As we cannot rely on service availability in the moment that method is being currently invoked, we might need to find a different point where the services are available.

    I've just recognized that the event dispatcher service allows to add an event subscriber on runtime. So I tried another approach, using a lazy event subscriber. This might be better than my initial approach from #134, because that doesn't require the event dispatcher service to be replaced by a Rules-specific implementation. Attached a patch for the lazy subscriber approach. For that approach the arguments mentioned in #131 regards performance and container rebuilds remain the same.

  • The last submitted patch, 136: 2816033-lazy-subscriber-136.patch, failed testing. View results
    - codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

  • 🇩🇪Germany mxh Offenburg

    Setting back to NR in favor of #102 and for manual review of #134 and #136.

  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 7.3 & MySQL 5.7
    last update 11 months ago
    414 pass
  • 🇪🇸Spain budalokko Girona

    Patch in #136 fixes the issue for me on drush cr. Checked the failing tests on the patch and they are due to the logger re-addition not being needed anymore, and generating duplicated logs:

        // The logger instance has changed, refresh it.
        $this->logger = $this->container->get('logger.channel.rules_debug');
        $this->logger->addLogger($this->debugLog);
    

    probably a result of

    A benefit of this approach is that there is no more need to trigger a full rebuild of the service container once an event has been added or deleted within a reaction rule config.

    Attached patch remove this re-addition in all kernel tests as it seems not needed anymore.

  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 7.3 & MySQL 5.7
    last update 11 months ago
    360 pass, 40 fail
  • 🇪🇸Spain budalokko Girona

    (..) but I'm really interested in seeing new approaches to the problem.

    Well in that case, find attached a patch with yet another approach to choose from. It's a simple workaround without patching core:

    • Container might not be available but Database is.
    • state service is just a wrapper around key_value service, and this has a very simple storage structure so query directly the key_value database table.

    Actually it is the one used by Business Rules (see their repo).

  • 🇩🇪Germany mxh Offenburg

    Attached patch remove this re-addition in all kernel tests as it seems not needed anymore.

    Makes sense, thanks for fixing the tests.
    I currently prefer the patch #139. If that approach may be viable for others too, we could also consider removing the state usage and instead make use of cache.backend.chainedfast that may hold the list of events, read out from the Rules configurations. That may further optimize the runtime performance a bit.

    Container might not be available but Database is.

    That's actually not guaranteed either, despite the fact that the database service may be more likely available.

    state service is just a wrapper around key_value service, and this has a very simple storage structure so query directly the key_value database table.

    That's true for the default (core) implementation of the state service. But in theory, the service could be replaced or decorated such that it behaves differently, such as not using the database table and instead using a separate key-value database. If I recall correctly, this is what you can do with the Redis module.

    Setting back to NR for 139/102/140.

  • 🇺🇸United States TR Cascadia

    Sorry, I haven't had a chance to look into #136 in detail yet. I've been away a lot.

    Re: #139

    Checked the failing tests on the patch and they are due to the logger re-addition not being needed anymore, and generating duplicated logs

    Thank you for figuring this out, this is a very good thing! I have never like that code in the Kernel tests - it was a hack needed to keep the tests running. It's good to know that was needed as a direct result of what is done here in the event subscriber, and it will be good to get rid of that stuff.

    Re: #140

    Container might not be available but Database is.
    state service is just a wrapper around key_value service, and this has a very simple storage structure so query directly the key_value database table.

    I looked at the code in that other module and agree with @mxh that building direct SQL calls like that other module does is not a good idea for a variety of reasons. We take great care in Drupal to abstract our storage, and putting explicit SQL into this important method sort of defeats all the rest of the architecture in Drupal. Your patch in #140 is better because it uses the Drupal database API rather that using SQL, but we should still try to avoid that IMO. The database service would be even better, but the problem we have here is that the availability of any service can't be guaranteed at this point in the container build (this is something I did try already instead of the state service).

    However, as a quick fix I think #140 could be used in the near term to avoid this problem, if we can't figure out a long-term fix. Because it doesn't rely on a core patch, it's more usable than #102 which has been my current recommendation.

    I'm still hopeful that #136/#139 will be a good solution. I just need to put in the time to run it through its paces.

  • 🇺🇸United States AaronBauman Philadelphia

    As someone stumbling on this thread from elsewhere, i'm super confused about why there are so many different patches, and which one i should use.

    Can someone please update the IS to clarify what is going on and how to move forward here?

  • Open in Jenkins → Open on Drupal.org →
    Core: 10.1.x + Environment: PHP 8.2 & MySQL 8
    last update 9 months ago
    414 pass
  • 🇺🇸United States TR Cascadia

    Agreed. I will work on the issue summary over the weekend. It will essentially be the information I gave above in #135 and #102.

    But the short answer is:

    The patch in #102 is small and works and but it hacks core. Thus, it's not a long-term solution.

    The patch in #139 looks promising but needs some testing from the community. And hopefully a test case. I have been preoccupied over the past few months - I only do Drupal in my spare time and I haven't had a lot of that recently. If you can test this out and post some feedback here that would be highly appreciated.

  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 7.3 & MySQL 5.7
    last update 9 months ago
    360 pass, 40 fail
  • Open in Jenkins → Open on Drupal.org →
    Core: 10.1.x + Environment: PHP 8.2 & MySQL 8
    last update 9 months ago
    360 pass, 40 fail
  • 🇺🇸United States TR Cascadia
  • 🇺🇸United States AaronBauman Philadelphia

    OK, that's helpful, thank you.

    The patch (#80) showed up on a site I inherited, so honestly I don't even know why it's there.
    Now I'm working on D10 updates for same, and this patch failed to apply, which led me to this thread.

    I'll try 139 and see how it goes

  • 🇨🇦Canada joel_osc

    Just ran into this problem, patch in 139 fixed it for me! Thank-you @budalokko and @TR!

  • 🇨🇳China splash112

    Patch from #139 works for me, problem solved on my site.

  • 🇺🇸United States spiritcapsule

    I tried #139 on 3.0.0-alpha8 and got this error:
    Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: You have requested a non-existent service "rules.event_subscriber.lazy". Did you mean this: "rules.event_subscriber"? in Drupal\Component\DependencyInjection\Container->get() (line 157 of core/lib/Drupal/Component/DependencyInjection/Container.php).

  • 🇨🇳China splash112

    Check if web\modules\contrib\rules\src\EventSubscriber\LazyGenericEventSubscriber.php is here, where it should be. It might have ended up elsewhere.

  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 7.3 & MySQL 5.7
    last update 6 months ago
    415 pass
  • 🇪🇸Spain budalokko Girona

    I've created a test case that demonstrates the issue. Attached patch `2816033-no-subscribed-events-153-test-only.patch` only contains the test, so it should fail.

    The other `2816033-no-subscribed-events-153-test-and-fix.patch` attachment contains the test and also mxh's fix from #136 and #139, so hopefully should pass.

    Interdiff against #139.

  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 7.3 & MySQL 5.7
    last update 6 months ago
    415 pass
  • 🇪🇸Spain budalokko Girona

    Sry the noise.
    Last test-only patch did not fail in CI. Here I try to visit `update.php` with the same path as core tests do.

  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 7.3 & MySQL 5.7
    last update 6 months ago
    406 pass, 2 fail
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 7.4 & MySQL 8
    last update 6 months ago
    406 pass, 2 fail
  • Open in Jenkins → Open on Drupal.org →
    Core: 10.1.x + Environment: PHP 8.2 & MySQL 8
    last update 6 months ago
    406 pass, 2 fail
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.0 & MySQL 5.7
    last update 6 months ago
    406 pass, 2 fail
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 7.3 & MySQL 5.7
    last update 6 months ago
    415 pass
  • 🇪🇸Spain budalokko Girona

    Previous issues were due to Drupal CI not matching current PHP minimal version :)

    Attached again:

    - A test only patch that demonstrates the issue.
    - A test + the mxh lazy subscriber fix from #136-#139.
    - interdiff against #139.

    Only test results against PHP >= 8.1 should be taken into account please.

    Removed the "Needs tests" patch as this should be complete now and ready for review. It's worth noting this is exactly the same patch in #136-#139 already working for 2 users. Just tests added.

  • Open in Jenkins → Open on Drupal.org →
    Core: 10.1.x + Environment: PHP 8.2 & MySQL 8
    last update 6 months ago
    415 pass
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 7.3 & MySQL 5.7
    last update 6 months ago
    406 pass, 2 fail
  • Open in Jenkins → Open on Drupal.org →
    Core: 10.1.x + Environment: PHP 8.2 & MySQL 8
    last update 6 months ago
    406 pass, 2 fail
  • 🇬🇧United Kingdom 2dareis2do

    I had situation with where my rules were being applied inconsistently. I thought I might have issue with custom rules condition plugin, however I could not even step though and debug. After installing this patch, I am now able to debug, although it would seem issue is here not with custom module.

    Drupal 10.2.2

    Rules version: '8.x-3.0-alpha8'

    Patch applies without issue.

    Thanks

  • First commit to issue fork.
  • Merge request !21Rules registers no listeners on rare occasions. → (Open) created by dcam
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 7.3 & MySQL 5.7
    last update 5 months ago
    415 pass
  • Pipeline finished with Canceled
    5 months ago
    Total: 119s
    #86428
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 7.3 & MySQL 5.7
    last update 5 months ago
    415 pass
  • Pipeline finished with Failed
    5 months ago
    Total: 559s
    #86430
  • Status changed to Needs work 5 months ago
  • 🇺🇸United States dcam

    I moved the patch into an MR because that's easier for me to review on my work laptop. I think the changes are really close to being ready. I mainly wanted to raise the point about the code duplication, but then I found the comments that need to be updated.

  • 🇩🇪Germany d4t3r

    I tried #156 and it solved my issue. Thanks!

  • 🇬🇧United Kingdom 2dareis2do

    I have been using this patch 156, and it seems to help.

    Difficult to say definitively, but I have found that rules sometimes appear to be triggered on new node publish, other times they do not.

  • First commit to issue fork.
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 7.3 & MySQL 5.7
    last update 4 months ago
    415 pass
  • 🇺🇸United States dcam

    @chaitanyadessai could you explain why a second MR was necessary? I diffed the changes from the two MRs. The only real difference is that you incorrectly added spaces before some docblock delimiters.

  • 🇩🇪Germany d4t3r

    Update: I removed the patch again. It turns out, that my comment #162 is a seperate issue.
    So this is a 'go' for this patch from my side. Sorry for the wrong information. I will edit the comment and open a new issue.

  • 🇬🇧United Kingdom davej

    Patch #156 has been successful here on a site where the rules stopped firing. Without the patch, disabling & re-enabling the rules would get them working for a short time, then they would silently stop firing again. With the patch, they have been firing OK for over a month.

Production build 0.69.0 2024