Problem/Motivation

I just upgraded to 2.4, and now I am getting the error:

\Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container.

Steps to reproduce

I have not yet determined the steps to consistently reproduce, therefore I am setting this to "support request" and not "bug report".

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

Issue fork memcache-3302086

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

alberto56 created an issue. See original summary.

alberto56’s picture

Title: After upgrade to 2.4, on Drupal 9.4.5 and PHP 8.0.21, \Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container. » After upgrade to 2.4, on Acquia, Drupal 9.4.5 and PHP 8.0.21, \Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container.

Specifying in the title that this only happens on Acquia environments.

alberto56’s picture

Confirming that downgrading to 2.3 fixes this issue.

salah1’s picture

Category: Support request » Bug report

I can confirm that i run into the exact error with Drupal 9.4.5 (not Acquia) , and had to downgrade to "memcache-8.x-2.3".
I'm running PHP: PHP 8.0.12

Steps to reproduce:
-Upgrade Drupal core to 9.4.5 (composer update drupal/core "drupal/core-*" --with-all-dependencies )
-Upgrade memcache to 2.4 (composer update to get other modules after satisfied with composer update --dry-run)
-Run drush updatedbdb
shows the error indicated on the title of this issue.

alberto56’s picture

Title: After upgrade to 2.4, on Acquia, Drupal 9.4.5 and PHP 8.0.21, \Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container. » After upgrade to 2.4, Drupal 9.4.5 and PHP 8.0.21, \Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container.

Thanks, I will update the title to remove reference to Acquia

zcht’s picture

Priority: Normal » Critical
Related issues: +#3302009: PHP Fatal error after upgrading to 2.4

I can confirm, the same problem after the update to 2.4. Solution so far, a rollback to 2.3

Drupal 9.4.x, PHP 7.4.x

japerry’s picture

Status: Active » Postponed (maintainer needs more info)

Is this occurring only when you run updb? I cannot get this error to occur on Acquia hosting (or locally). The updb hook was just there to reload the container, try clearing cache and see if it still fails.

zcht’s picture

the problem occurs when trying to clear the cache, access the page, execute other drush commands. this has nothing to do with Acquia, it occurs locally as well as on the server.

damien laguerre’s picture

StatusFileSize
new508 bytes

I can confirm the bug.

The website encountered an unexpected error. Please try again later.
Drupal\Core\DependencyInjection\ContainerNotInitializedException: \Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container. in Drupal::getContainer() (line 169 of /web/core/lib/Drupal.php).
Drupal::getContainer() (Line: 732)
Drupal::time() (Line: 101)
Drupal\memcache\MemcacheBackend->__construct() (Line: 83)
Drupal\Component\DependencyInjection\PhpArrayContainer->createService() (Line: 176)
Drupal\Component\DependencyInjection\Container->get() (Line: 550)
Drupal\Core\DrupalKernel->getCachedContainerDefinition() (Line: 895)
Drupal\Core\DrupalKernel->initializeContainer() (Line: 472)
Drupal\Core\DrupalKernel->boot() (Line: 708)
Drupal\Core\DrupalKernel->handle() (Line: 19)

The time service could not be load.

Changing this in MemcacheBackend:
$this->time = $time_service ?? \Drupal::time();
into that:
$this->time = $time_service;

makes it work.

zcht’s picture

Status: Postponed (maintainer needs more info) » Needs review
it-cru’s picture

Patch from #9 fix the issue for me with Drupal 9.4.5, memcache 2.4 module and PHP 7.4

japerry’s picture

StatusFileSize
new3.98 KB

This patch should lazy load the time service if its not been called yet.

mglaman’s picture

  1. +++ b/src/MemcacheBackend.php
    @@ -79,7 +79,7 @@ class MemcacheBackend implements CacheBackendInterface {
    @@ -98,7 +98,7 @@ class MemcacheBackend implements CacheBackendInterface {
    
    @@ -98,7 +98,7 @@ class MemcacheBackend implements CacheBackendInterface {
         $this->memcache = $memcache;
         $this->checksumProvider = $checksum_provider;
         $this->timestampInvalidator = $timestamp_invalidator;
    -    $this->time = $time_service ?? \Drupal::time();
    +    $this->time = $time_service;
    

    trigger_error when null

  2. +++ b/src/MemcacheBackend.php
    @@ -246,6 +246,15 @@ class MemcacheBackend implements CacheBackendInterface {
    +  /**
    +   * Lazy Load in the time interface.
    +   *
    +   * @return \Drupal\Component\Datetime\TimeInterface
    +   */
    +  private function getTime() {
    +    return $this->time ?? \Drupal::time();
    +  }
    

    Talked this over in Slack, and the only reasonable way I can see handling this is using a lazy-load method.

    It'd be good to document a `@todo remove after memcache:x.x`

  3. +++ b/src/MemcacheBackendFactory.php
    @@ -51,7 +51,7 @@ class MemcacheBackendFactory implements CacheFactoryInterface {
    -  public function __construct(MemcacheDriverFactory $memcache_factory, CacheTagsChecksumInterface $checksum_provider, TimestampInvalidatorInterface $timestamp_invalidator, TimeInterface $time_service) {
    +  public function __construct(MemcacheDriverFactory $memcache_factory, CacheTagsChecksumInterface $checksum_provider, TimestampInvalidatorInterface $timestamp_invalidator, TimeInterface $time_service = NULL) {
         $this->memcacheFactory = $memcache_factory;
    

    needs @trigger_error to catch missing arg

  4. +++ b/src/MemcacheBackendFactory.php
    @@ -68,12 +68,15 @@ class MemcacheBackendFactory implements CacheFactoryInterface {
    +    // BC Layer for returning time service.
    +    $time = $this->time ?? \Drupal::time();
    

    this should have same "todo" remove after note

japerry’s picture

StatusFileSize
new5.29 KB

Incorporated changes above.

benjifisher’s picture

I have been looking at this issue today with @ShaunLaws. Maybe we did something wrong, but we tried passing the time service to the constructor and it did not work.

Specifically, we changed these lines in settings.local.php:

        'cache.container' => [
          'class' => 'Drupal\memcache\MemcacheBackend',
          'arguments' => ['container', '@memcache.backend.cache.container', '@cache_tags_provider.container', '@memcache.timestamp.invalidator.bin'],
        ],

to

        'cache.container' => [
          'class' => 'Drupal\memcache\MemcacheBackend',
          'arguments' => ['container', '@memcache.backend.cache.container', '@cache_tags_provider.container', '@memcache.timestamp.invalidator.bin', '@datetime.time'],
        ],

We get the following error:

In Container.php line 156:

2022-08-09T13:54:49.351-06:00

2022-08-09T13:54:49.351-06:00You have requested a non-existent service "datetime.time".

That makes sense: if $time_service ?? \Drupal::time() in the constructor leads to the error message in the issue summary, then the container is not initialized. In that case, adding '@datetime.time' like this is not going to work, either.

If that was the wrong fix, then please include an update to README.txt as part of this issue.

If I am right, and there is no way to inject the service, then it does not make sense to deprecate calling the constructor without the time service.

berdir’s picture

This is by design and nothing can be done about it in core. If you define a bootstrap container you need to define all required services, you can't just reference something. That's why you hardcode the service definitions in settings.local.php.

The only possible workaround is to make the fallback to $_SERVER['REQUEST_TIME'] and not the global container. So a getRequestTime() helper method that returns the timestamp, not a service.

benjifisher’s picture

StatusFileSize
new2.6 KB
new3.76 KB
new3.31 KB

I am attaching a patch based on #14, removing the deprecation notices. I am attaching interdiffs comparing it to the patches in #12 and #14.

Compared to 8.x-2.x, the only change to the factory class is adding the @param comment and making the new parameter optional. I do not see any reason to complicate the factory class.

Doing it this way supports the documented configuration in settings.php. It also works if the factory class is instantiated when the container is available, and the time service is passed. It also allows testing with a mocked time service.

An alternative is to drop the dependence on the time service that was added in 8.x-2.4. As in the last paragraph of #16, just implement getRequestTime() in the backend class, returning $_SERVER['REQUEST_TIME']. If you like, I can prepare a patch that does that.

benjifisher’s picture

StatusFileSize
new4.26 KB

There was some further discussion on Slack, with @Berdir, @mglaman, @japerry, and me. Two points from @Berdir:

... defining a service with missing dependencies that might fail if the wrong method is called does not seem a reliable fix
... if that code is not called on the bootstrap container then it might continue to work... for now, but it sounds like a time bomb, and another possibly unrelated change in the future might suddenly break it

I opened a MR, so you can review in two steps. The first step is a partial revert of #3288536: Automated Drupal 10 compatibility fixes. The second adds getRequestTime() as a static method on the Backend class. It is basically the same as the version in the Time service, except that it skips the part where it tries to get the request time from the RequestStack.

I am also adding the changes as a patch. There is no interdiff, since this is a different approach.

orakili’s picture

FWIW, regarding #15, I got it working by adding the following in the `bootstrap_container_definition` in the settings.php

      'request_stack' => [
        'class' => 'Drupal\Core\Http\RequestStack',
        'tags' => ['name' => 'persist'],
      ],
      'datetime.time' => [
        'class' => 'Drupal\Component\Datetime\Time',
        'arguments' => ['@request_stack'],
      ],

in addition to injecting the '@datetime.time' service

      'cache.container' => [
        'class' => 'Drupal\memcache\MemcacheBackend',
        'arguments' => ['container', '@memcache.backend.cache.container', '@cache_tags_provider.container', '@memcache.timestamp.invalidator.bin', '@datetime.time'],
      ],
neclimdul’s picture

+++ b/src/MemcacheBackend.php
@@ -124,6 +116,18 @@ class MemcacheBackend implements CacheBackendInterface {
+    return (int) $_SERVER['REQUEST_TIME'] ?? time();

This seemed bad but y'all are 100% right about bootstrap containers. More services in bootstrap or you need this. Icky problem.

Thanks for the patch. Seems like it might be "good enough" but if not it might save the next person a few minutes of head scratching if this issue as noted in the current release notes as a known issue.

shaunlaws’s picture

Using the container definition service information from orakili, I was able to test all of the patches.

Both #14 and #17 worked without the injection of the time service.

They also worked with the injection of the time service using this definition:

    $settings['bootstrap_container_definition'] = [
      'parameters' => [],
      'services' => [
        # Dependencies.
        'request_stack' => [
          'class' => 'Drupal\Core\Http\RequestStack',
          'tags' => ['name' => 'persist'],
        ],
        'datetime.time' => [
          'class' => 'Drupal\Component\Datetime\Time',
          'arguments' => ['@request_stack'],
        ],
        <snip - other services>
        'cache.container' => [
          'class' => 'Drupal\memcache\MemcacheBackend',
          'arguments' => ['container', '@memcache.backend.cache.container', '@cache_tags_provider.container', '@memcache.timestamp.invalidator.bin', '@datetime.time'],
        ],
      ],
    ];

For #14 without the time service injection, I didn’t see the deprecation error message, but maybe I was just looking in the wrong place.

#18 also worked without injecting the time service.

berdir’s picture

Yes, that's exactly what I suggested initially. That said, I hadn't realized that datetime.time depends on the request stack. I suspect you end up with an empty request stack then and just like some patches only worked because nothing within the bootstrap container actually needs the current time, the same might be true for that using earlier patches because they would fail as soon as code actually calls getRequest() and expects something to be returned. So it's probably indeed safer to drop the time service dependency entirely.

  • japerry committed aa265f1 on 8.x-2.x authored by benjifisher
    Issue #3302086 by benjifisher, japerry, Damien LAGUERRE: After upgrade...
japerry’s picture

Status: Needs review » Fixed

Unfortunately, I don't see a better way to do this at the bootstrap level. The approach works fine for this use case and doesn't appear to cause issues with the previous release, since we're injecting an unused service. Fixed!

benjifisher’s picture

I am updating my contribution attribution.

it-cru’s picture

@japerry: Could you create a new 2.x release please. Otherwise I think you will get new opened issues reported by people who don't see that this issue is currently only fixed in dev branch.

jcnventura’s picture

Please create a new release ASAP.

japerry’s picture

Released in 8.x-2.5!

jcnventura’s picture

Thanks a lot!!

Status: Fixed » Closed (fixed)

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