Suggested commit message: Issue #2363351 by chx, larowlan: CMI (or anything cached) can'\''t be used from a Service Provider.

Problem/Motivation

The cache_factory service needs %cache_default_bin_backends. This parameter is added by ListCacheBinsPass. This runs after service providers which, as a corollary can't use CMI. LanguageServiceProvider is an example which tries to use CMI and abuses the bootstrap config storage to do so because it can't use the normal service. There's a @todo to replace it with config.storage. This issue is about resolving that @todo.

Another example that if you would like to try to access a module's path in a service provider you get a You have requested a non-existent parameter "cache_default_bin_backends". :

final class MY_MODULE_ServiceProvider implements ServiceProviderInterface {

  /**
   * {@inheritdoc}
   */
  public function register(ContainerBuilder $container): void {
    /** @var \Drupal\Core\Extension\ModuleHandlerInterface $module_handler */
    $module_handler = $container->get('module_handler');
    try {
      $path = $module_handler->getModule(MY_MODULE)->getPath();
    } catch (\Exception $e) {
      throw new LogicException('Unable to identify installation path of this module.');
    }
  }

Proposed resolution

Add cache_default_bin_backends: null by default and if NULL return NullBackend unconditionally from the CacheBackend.

Remaining tasks

User interface changes

API changes

Issue fork drupal-2363351

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

dawehner’s picture

We could just provide an empty cache_default_bin_backendsinside our core.services.yml file?

chx’s picture

Status: Active » Needs review
StatusFileSize
new388 bytes

Easy to fix.

fabianx’s picture

Status: Needs review » Reviewed & tested by the community

Makes sense to me.

alexpott’s picture

Status: Reviewed & tested by the community » Needs review

What would happen if a cache service defines a default backend wouldn't this mean that potentially we switch backend during a request?

chx’s picture

Yeah well but what else do you plan to ... do? Cos that thing is a compile pass running waaaaay later than the service providers and alters.

tim.plunkett’s picture

Priority: Normal » Major
chx’s picture

Issue summary: View changes
StatusFileSize
new1.05 KB

Note that in particular the problem with CMI is circumvented by LanguageServiceProvider by using (abusing?) BootstrapConfigStorageFactory::get.

Also, alexpott is totally right. This is not a good approach. Quite probably the solution is to NullBackend it. Proposal in the issue summary.

berdir’s picture

Related, https://github.com/md-systems/file_entity/blob/8.x-2.x/src/FileEntitySer... is my workaround for checking if a module is enabled in a ServiceProvider.

dawehner queued 7: 2363351_7.patch for re-testing.

chx’s picture

So how do we proceed now?

chx’s picture

Assigned: Unassigned » chx

We will add a comment and go with this patch.

jhedstrom’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

Patch no longer applies.

chx’s picture

Assigned: chx » Unassigned
chx’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
StatusFileSize
new1.03 KB

 

chx’s picture

StatusFileSize
new1.27 KB

Comment.

jhedstrom’s picture

This fix makes sense to me. I looked briefly at writing a test for this, but couldn't find really any existing service container level tests.

chx’s picture

StatusFileSize
new6.69 KB

Status: Needs review » Needs work

The last submitted patch, 17: 2363351_17.patch, failed testing.

The last submitted patch, 17: 2363351_17.patch, failed testing.

chx’s picture

Status: Needs work » Needs review
StatusFileSize
new5.99 KB
new1.48 KB

Status: Needs review » Needs work

The last submitted patch, 20: 2363351_20.patch, failed testing.

The last submitted patch, 20: 2363351_20.patch, failed testing.

chx’s picture

Status: Needs work » Needs review
StatusFileSize
new8.07 KB
new3.78 KB

Status: Needs review » Needs work

The last submitted patch, 23: 2363351_23.patch, failed testing.

The last submitted patch, 23: 2363351_23.patch, failed testing.

chx’s picture

Status: Needs work » Needs review

Time for reviews! The remaining few fails, as far as I can see are just test failures and not actual functionality regressions, of course I will fix them but the changes will be test only changes. So , the architecture is what it is and time for reviews.

chx’s picture

Issue summary: View changes
chx’s picture

Status: Needs review » Needs work
StatusFileSize
new12.94 KB
chx’s picture

Status: Needs work » Needs review

Ops.

chx’s picture

Issue summary: View changes

Suggesting crediting larowlan who pointed out where the kernel service gets defined and thus solved the last failures.

fabianx’s picture

Architecture wise this makes sense to me.

catch’s picture

The only question I have - if there was a cache set or delete during the compiler pass, then it'll do that on the null backend and be a no-op. In most cases container rebuild = cache clear anyway, so it won't make any difference. Also doing deletes there would be strange.

We might just need some docs why this doesn't matter though.

alan evans’s picture

So, I realise my opinion might not have great weight on this issue, but this patch makes sense to me ... I've been using it partly as an excuse to learn some of the moving parts involved. I have one question below, which I think largely stems from a lack of understanding on my part:

  • - I've checked that the null cache backend setup makes sense (to me at least)
  • - as you already noted, the tests need to be re-added and fixed
  • - I've checked that the modified language methods are not used in the previous form elsewhere
  • - there will be a couple of nitpicks (comment wrapping and caps) but probably makes sense to deal with those once the patch including test fixes is ready for review

Manual testing done:

  • - OK enable language module
  • - OK apply the patch,
  • - OK test out rebuild on an existing site
  • - OK rough check site
  • - OK test new site install in non-English
  • - OK rough check site
  • - OK put in debug to sanity check / review befores and afters of $this->services during compilation

So here's the one question: I'm sure this is just me missing background on this but what's our guarantee that it's ok to remove all non-synthetic services in the compile method here? Are there no non-synthetic services that are valid at that point? Is it the case that not being synthetic, they'd get automatically reinstantiated later as needed?

I put in some debugging here too and discovered really quite a lot of definitions that are not flagged as synthetic... seems to make sense.

chx’s picture

> So here's the one question: I'm sure this is just me missing background on this but what's our guarantee that it's ok to remove all non-synthetic services in the compile method here?

Yes it's OK, see a bit later why.

> Are there no non-synthetic services that are valid at that point?

Who knows and who cares? It's simpler to wipe them all.

> Is it the case that not being synthetic, they'd get automatically reinstantiated later as needed?

Indeed. Non-synthetic services were created from definition and they can be created again. And this is why we rather wipe them all rather than wonder which one stores a cache bin in a property.

fabianx’s picture

#33, #34:

Already now only synthetic services are persisted from ContainerBuilder to Container, so this is fine.

The removal of persisted services is only needed for KernelTestBase*, where currently the ContainerBuilder is used for testing the system and not the Container for various reasons.

alan evans’s picture

Many thanks chx and fabianx - that's helpful.

chx’s picture

#35 it's not about services persisted from one container to the next but about services created while compiling. For example, LanguageServiceProvider will create the config.storage service which in turn has @cache.config injected into it. If we didn't remove this service then every config get in this request would use the nullbackend which is very sub optimal.

fabianx’s picture

#37: Yes, but because no services that are created during compiling are persisted from one container to the next, we can be very sure that removing those services from the compiled container that are created during compilation is safe as well :).

chx’s picture

StatusFileSize
new9.35 KB

Let's try this prettier version. I had this before but it failed or so I thought.

fabianx’s picture

+++ b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php
@@ -100,6 +108,17 @@ private function synchronize($id) {
+    // Remove all non-synthetic services set by compiler passes. For example,
+    // cache bins are forced to NulLBackend before the ListCacheBinsPass and
+    // so cache services might got stored as NullBackend.

Lets rephrase to something like:

Remove all services constructed during compiler passes. Only keep those that have been explicitly set.

chx’s picture

Well, the code does that -- but why? If you want I can change the comment but I prefer the longer explanation on why.

fabianx’s picture

What I meant is to - lets rephrase to:

+    // Remove all services constructed during compiler passes. Only keep those that have been explicitly set. For example,
+    // cache bins are forced to NulLBackend before the ListCacheBinsPass and
+    // so cache services might got stored as NullBackend.

The current text is misleading as:

"Remove all non-synthetic services set by compiler passes."

set is wrong, because those are the ones we keep.

non-synthetic is wrong too, because we don't distinguish here between synthetic and non-synthetic.

Does that make more sense now?

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

larowlan’s picture

Status: Needs review » Needs work

For #42, assuming this is still an issue

alexpott’s picture

Reading this issue makes me realise we are doing a tonne of unnecessary work. Now that config is stored in the db there's no need to duplicate everything in the cache_config table. We can lose all the complexity and have faster rebuilds and sites after rebuild.

mxr576’s picture

I have just bumped into this issue, #39 does not apply on >= 8.9. How we should move forward? Should I try to reroll the patch or @alexpott's comment in #52 change anything? Based on my understanding that describes a different issue.

mxr576’s picture

Title: CMI (or anything cached) can't be used from a Service Provider » Cached services can't be used in service providers/modifiers
Version: 8.9.x-dev » 9.2.x-dev
StatusFileSize
new666 bytes

Okay, so many things have changed since #39 was born 5 years ago, today, it seems only adding this one-liner to core.services.yml fixes the issue. (Test coverage could be still needed to warrant this issue does not occur again.)

mxr576’s picture

Issue summary: View changes
mxr576’s picture

My fix in #54 seems to be working but I bumped into another interesting issue with Redis which could be slightly related to this one...

alexpott’s picture

Issue tags: +Needs tests

It'd be great to add test coverage here.

mxr576’s picture

This MR essentially contains my patch from #54 and also one important addition, a mitigation for a workaround like you can see here:
https://github.com/amazeeio/drupal-example-simple/blob/46d71e3885dc9fca7...
https://github.com/amazeeio/drupal-example-simple/blob/46d71e3885dc9fca7...

The null cache backend has to be registered because by default it is not available. I do not see a reason why it should not available by default, it does not mean it is in use. Although codes like you can see in the description of #3201355 may trigger calls that try to access to null cache backend as a fallback.

It'd be great to add test coverage here.

I agree although I cannot promise when I am going to have time for that. I could use some help on this.
(First I would like to make sure that the latest changes do not break anything. )

mxr576’s picture

mxr576’s picture

@alexpott Here is a minimal POC test that confirms the missing cache_default_bin_backends issue exists. This can be only reproduced if we remove the cache_factory override from the KernelTestBase because that change removes what is the key to reproduce this issue:

core.services.yml

  cache_factory:
    class: Drupal\Core\Cache\CacheFactory
    arguments: ['@settings', '%cache_default_bin_backends%']
    calls:
      - [setContainer, ['@service_container']]

This test does not prove that cache.backend.null should be always defined, although as I wrote in #59, I do not see why it should not be.

mxr576’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests

ok, now I need some feedback

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

berdir’s picture

Status: Needs review » Needs work

This is also blocking #2575105: Use cache collector for state now it seems, which makes state use a cache and as a result, ContainerRebuildTestServiceProvider, which does use state, fails.

Added some comments about the comments, didn't think too much about the tests but the state issue will add more functional test coverage then...

neclimdul’s picture

It probably doesn't make sense to have the null backend definition in sites/development.services.yml if its in core after this.

@alexpott I'm curious what complexity you where referring to. Currently we seem to only be adding things so not sure we really removing complexity. Happy to dig in though, removing complexity is my jam.

mxr576’s picture

Status: Needs work » Needs review
mxr576’s picture

Version: 9.3.x-dev » 9.4.x-dev

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

borisson_’s picture

Issue tags: +Bug Smash Initiative

This issue is difficult to understand, as most things that have to do with the cache system. @neclimdul asked a great question in #65, that should probably be answered. Because as they said, reducing complexity is great but I don't see this issue already doing that.

I think we need to also add a test that the null backend is available without the development services being included?

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs Review Queue Initiative

This issue is being reviewed by the kind folks in Slack, #needs-review-queue-initiative. We are working to keep the size of Needs Review queue [2700+ issues] to around 400 (1 month or less), following Review a patch or merge request as a guide.

As mentioned this is difficult. But what I can gather is that there is still discussion being done.
#65 needs an answer
and possible more tests from #69.

mxr576’s picture

Rebased the MR, this is what I could do quickly. I am also pinging @alexpot on Slack to ask if he can answer #65.

alexpott’s picture

The complexity I'm referring to is the fact that in core we have both the config table and cache_config table. These tables are basically the same. The config cache really should just be fast cache backend - i.e APCu / memcache / redis. So maybe we're not losing complexity but we losing a layer. None of this is in scope here. I think I just realised this when looking at the code in the earlier patches.

mxr576’s picture

I think we need to also add a test that the null backend is available without the development services being included?

@borisson_ Can you please explain what you meant by this? The proposed changed _removes_ cache.backend.null from development.services.yml

--- a/core/assets/scaffold/files/development.services.yml
+++ b/core/assets/scaffold/files/development.services.yml
@@ -4,6 +4,3 @@
 # 'example.settings.local.php' file, which sits next to this file.
 parameters:
   http.response.debug_cacheability_headers: true
-services:
-  cache.backend.null:
-    class: Drupal\Core\Cache\NullBackendFactory
borisson_’s picture

Status: Needs work » Reviewed & tested by the community

From #65:

It probably doesn't make sense to have the null backend definition in sites/development.services.yml if its in core after this.

I agree, It was removed in the latest MR from development.services so that is fixed now.

From #69:

I think we need to also add a test that the null backend is available without the development services being included?

We don't specifically test that services are available for any other services, so I actually don't think we need to do it here either.

I only have one more question about the comment next to the cache.backend.null definition in the services.yml, I'm not sure we need to specify the intended usecase and that comment probably is ok to be deleted. Not holding this issue up for that though.

berdir’s picture

> The complexity I'm referring to is the fact that in core we have both the config table and cache_config table. These tables are basically the same. The config cache really should just be fast cache backend - i.e APCu / memcache / redis.

We already use the ChainedFast backend by default for the config bin, it's only if APCu is _not_ available that it's kinda unnecessary, but we don't have a mechanism right now to automatically fall back to the null backend if apcu is not available, we just always fall back to the default bin then. At best we could document that _if_ you can't use APCu or redis/memcache then you could consider to manually the config bin to use the null backend.

We can't _only_ use APCu either, as that does not support multiple multiple servers and I think even on a single server, there's drush to consider?

mxr576’s picture

I'm not sure we need to specify the intended usecase and that comment probably is ok to be deleted.

I have removed the comment to eliminate all roadblocks :)

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

I think the change to \Drupal\Core\Cache\CacheFactory is unnecessary and should be removed. And if this results in a broken test run then we need to do the work to understand why.

I also don't understand why we need to move the null backend out of development.services to fix this issue. I think that this is a remnant of an earlier attempt at a fix.

mxr576’s picture

I think the change to \Drupal\Core\Cache\CacheFactory is unnecessary

Answered this in an MR comment. TL;DR it is needed, but no Drupal core tests catch the issue that I see on downstream.

I also don't understand why we need to move the null backend out of development.services to fix this issue. I think that this is a remnant of an earlier attempt at a fix.

TBH, I do not see an issue with having a null service/object registered, but you were right, it looks like it is not needed to make Drupal core tests pass and also everything worked fine on my guinea pig downstream project without it.

berdir’s picture

> how the "field config import fails when the site is installed from configuration" thing can be covered with tests?

Is this really happening on a clean site with just core and and patches? My guess is that this only happens in combination with specific additional modules/and or patches.

Reminder that #2575105: Use cache collector for state is blocked on this issue and is currently failing. Combining those patches should give us a testbed to test the different fixes and which changes are required exactly.

andypost’s picture

Status: Needs work » Needs review

I looks like it needs another review

berdir’s picture

Status: Needs review » Needs work

The new development compiler pass is now triggering this reliably on HEAD, will look into it in the next days.

berdir’s picture

Status: Needs work » Needs review

I'm proposing we remove the snippet from CacheFactory for now. It seems unrelated to the main change here, which has been blocking progress on issues like state caching for a very long time.

Lets open a follow-up issue to see if we can figure out when that's not needed. Could be a drush problem as well, try to reproduce with the drupal core UI installer?

berdir’s picture

Ok, I overlooked the part that this appears to be a regression introduced by this issue, but still, without a way to reproduce or understand why it's required, I'm not sure about keeping that change here.

Another thing to debug would be a backtrace when exactly that is needed/called. I did this in the same place as the removed part and got nothing during a regular install:

if (InstallerKernel::installationAttempted()) {
  debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
}

Not even with the condition removed, which kind of makes sense as the whole class is meant to be replaced during the installer. What do you get?

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs followup

Brought this up in #needs-review-queue-initiative channel and lets try and remove the code in question from #83/#84 to a follow up.

Will admit cache is one of my worst components but want to help keep the issue moving where I can. Also can help test where needed.

berdir’s picture

Status: Needs work » Needs review
StatusFileSize
new3.46 KB

Patch against 11.x-dev from the MR. The mentioned code is already removed from the MR and it still applies, but can't change the target.

Best way to test this is combined with #2575105: Use cache collector for state. I'll upload a combined patch there next. This was already RTBC before and the reason it was pushed back has been removed, so I think it could go back to that.

I'm unsure about the follow and if that's really needed. I'd suggest @mxr576 or someone else who can reproduce that problem opens it *if* it can be reproduced, because without a way to reproduce, there's nothing we can do.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs followup

Understood on the follow up.

And seems the code in question was removed that I could tell so I'll remark this.

  • catch committed 28db985e on 11.x
    Issue #2363351 by mxr576, chx, Berdir, Fabianx, alexpott, Alan Evans,...
catch’s picture

Status: Reviewed & tested by the community » Fixed

After nine years the functional change here is the same as the original patch from 2014. I think we should get this in to unblock the state caching issue, and if we do end up being able to reproduce the config import issues, we can fix in a follow-up or worst case roll back. 10.2 is still some time away.

Committed 28db985 and pushed to 11.x. Thanks!

Status: Fixed » Closed (fixed)

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