Problem/Motivation

Drupal GUI install fails on PHP 5.5 with certain commonly shipped versions of APCu. This is due to a bug in early versions in APCu, not in Drupal. Versions of APCu of 4.0.7 and later are known to be safe.

Originally, when this issue was encountered, Drupal 8 had a minimum PHP version of 5.4. This has since been raised to 5.5.9 (issue #2508231). It is believed that the faulty APCu was no longer being distributed by the time PHP 5.5.9 was released. Therefore, this issue has been closed.

Observed Symptoms

During a GUI install of Drupal 8 (either standard or minimal) exceptions are thrown from \Drupal\Component\FileCache\FileCache::getMultiple(). These are:reporting missing elements in $cached: either $cached['mtime'] or $cached['filepath'].
On Safari these cause the installation to stop. On Firefox a page refresh causes the installation to continue without obvious errors, but the installation is faulty resulting in a large number of unit test or other failures.

Notice: Undefined index: mtime in Drupal\Component\FileCache\FileCache->getMultiple() (line 115 of /Users/rob/Sites/drupalcon/drupaltemp/core/lib/Drupal/Component/FileCache/FileCache.php).
Drupal\Component\FileCache\FileCache->getMultiple(Array)
Drupal\Component\FileCache\FileCache->get('core/core.services.yml')
Drupal\Core\DependencyInjection\YamlFileLoader->load('core/core.services.yml')
Drupal\Core\DrupalKernel->compileContainer()
Drupal\Core\DrupalKernel->initializeContainer(1)
Drupal\Core\Installer\InstallerKernel->initializeContainer()
Drupal\Core\DrupalKernel->boot()
install_begin_request(Object, Array)
install_drupal(Object)

Environment

The environment being used for the install was:

  • OS: MacOS 10.10.3
  • Stack: MAMP 2.2
  • PHP: 5.5.3
  • APCu: 4.0.1

Detail

This has been traced back to key corruption in the $cached array. In one example the 'mtime' key has been replaced with '/[^A-' and the 'filepath' key has been replaced with 'original'. The values are as expected.

Cause Hypothesis

Alex Pott suggested that this might be caused by APCu serialisation being faulty and only working properly with APC. APC was replaced with APCu in PHP 5.5. He suggested that upgrading APCu to the latest version might fix the problem, but this turned out to be very difficult.

Proposed Resolution

The proposed resolution is to disable APC/APCu when using PHP >=5.5 and APCu < 4.0.7. A helper function in \Drupal\Component\Utility\Environment is used for detecting whether a suitable version of APC or APCu is installed, because it is known that this will help with fixing other specific caching issues in the future.
The three places where APC/APCu is detected with function_exists('apc_fetch') are replaced with calls to Environment::checkForCompatibleApcUserCache().

Comments

blackra’s picture

PHP = 5.5.21, APC = 4.07 seems to fix this problem.

fabianx’s picture

https://github.com/krakjoe/apcu/issues/35

This is the upstream issue for the bug, its an APC bug.

We should warn if version is prone to corruption, leaving active for that.

dawehner’s picture

We should warn if version is prone to corruption, leaving active for that.

That sounds like a great idea. Do we know which version causes the problem? For now our bisect range is 4.0.1 has issues 4.0.7 doesn't.

blackra’s picture

Status: Active » Needs review
StatusFileSize
new1.33 KB

We don't know exactly which versions are affected so I have rolled a patch based on the following assumptions:

The bug exists in APCu versions < 4.0.7
The bug does not exist in APC.

We could do something more sophisticated if we checked which serializer was in use, but I'm not sure whether it is worth it. The key point of this patch is to avoid affecting people who are not capable of upgrading their environment. I think it would be reasonable to assume that people who care about performance will find a way to fix it.

Do we need to flag this as an issue in the system status screen?

fabianx’s picture

Status: Needs review » Needs work

The patch is a great idea, but there is more cases in Drupal where we use function_exists('apc_fetch').

We should consolidate those into a helper class in Drupal\Component\Utility\Requirements and re-use from all places ...

That will also make a related issue easier ...

Thanks!

blackra’s picture

Status: Needs work » Needs review
StatusFileSize
new2.88 KB

Rolled a new patch. There is no Requirements. I assumed you meant Environment. If not, I can move it.

Note that I deleted your @todo about the testbot. Is that still relevant?

dawehner’s picture

  1. +++ b/core/lib/Drupal/Component/Utility/Environment.php
    @@ -42,4 +42,54 @@ public static function checkMemoryLimit($required, $memory_limit = NULL) {
    +  public static function checkForOriginalApc() {
    

    Do we need that as a public method?

  2. +++ b/core/lib/Drupal/Component/Utility/Environment.php
    @@ -42,4 +42,54 @@ public static function checkMemoryLimit($required, $memory_limit = NULL) {
    +    $compatibleApc = false;
    

    we don't use camelcase for local variables.

  3. +++ b/core/lib/Drupal/Component/Utility/Environment.php
    @@ -42,4 +42,54 @@ public static function checkMemoryLimit($required, $memory_limit = NULL) {
    +      $apcu_ext = new \ReflectionExtension('apcu');
    +      $apcu_version = $apcu_ext->getVersion();
    +      $compatibleApc = (version_compare($apcu_version, '4.0.7') >= 0);
    

    Maybe its worth to static cache that.

  4. +++ b/core/lib/Drupal/Core/DrupalKernel.php
    @@ -425,9 +425,8 @@ public function boot() {
    +      if (\Drupal\Component\Utility\Environment::checkForCompatibleApcUserCache()) {
    

    Let's use a use statement.

blackra’s picture

#7-1: The reason that the method is public is because there are other parts of the code that need this functionality for resolving other issues. Maybe it should be protected until those issues are addressed in case they don't happen until 8.1.x (minimum API changes).
Fabian, do you have any comments?

I think the rest of those comments seem sensible.

blackra’s picture

StatusFileSize
new4.12 KB

Updated in line with comment #7.

I have also made the other helper member protected: protected static function checkForApcu()

Note that I used a protected static member cache rather than a local cache deliberately to allow for easier overrides.

blackra’s picture

Issue summary: View changes

Updated the issue summary.

fabianx’s picture

Status: Needs review » Needs work

As much as I love it, there are other things in core calling function_exists('apc_fetch') and this should replace all of those ...

blackra’s picture

Issue summary: View changes
StatusFileSize
new5.49 KB

Updated to address #11

blackra’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 12: 2488730-12-check-for-apcu-version.patch, failed testing.

blackra’s picture

Status: Needs work » Needs review
StatusFileSize
new5.51 KB

Reroll of #12

fabianx’s picture

Little nit:

+++ b/core/lib/Drupal/Component/Utility/Environment.php
@@ -42,4 +49,71 @@ public static function checkMemoryLimit($required, $memory_limit = NULL) {
+    $compatible_apc = false;
+
+    // Have we already cached the answer?
+    if (isset(Environment::$cache_check_for_compatible_apc_user_cache)) {
+      $compatible_apc = Environment::$cache_check_for_compatible_apc_user_cache;
+    }
+    else {

We can directly return here and spare the remainder.

$compatible_apc is not needed before the below.

blackra’s picture

StatusFileSize
new5.44 KB

Little nit in #16 addressed plus a re-roll to deal with changes in \Drupal\Core\DrupalKernel

blackra’s picture

Issue summary: View changes
Status: Needs review » Closed (duplicate)

Originally, when this issue was encountered, Drupal 8 had a minimum PHP version of 5.4. This has since been raised to 5.5.9. It is believed that the faulty APCu was no longer being distributed by the time PHP 5.5.9 was released. Therefore, this issue is being marked as closed. The issue that forced the increase in PHP version was #2508231.

blackra’s picture