locked
Wincache Opcache breaks Drupal 8 install RRS feed

  • Question

  • User962350831 posted

    I was about to pull my hair on this while porting the Drupal SQL Server driver to Drupal 8, but after investigation I can only conclude that the Wincache Opcode Cache is breaking Drupal 8 install process.

    Why am I using still this Opcode cache instead of Zend's? Because Zend Opcache is broken on Windows, there's at least 10 bugs on the PHP issue queue  related to Zend Opcache randomly failing on windows.

    Steps to reproduce:

    1. Get the latest dev version of Drupal 8 with GIT:

    git clone --branch 8.0.x http://git.drupal.org/project/drupal.git

    2. Download clean PHP binaries from PHP (5.6.9 Non Thread Safe)

    http://windows.php.net/download/

    3. Add Wincache 1.3.7.4 to the extension DIR and enable the opcode cache and enable the MySQL pdo:

    extension=php_wincache.dll
    wincache.ocenabled=0
    wincache.ocachesize = 100
    extension=php_mysql.dll

    4. Configure an IIS site with D8 and the PHP and navigate to the installation URL, introduce the MySQL database details and strat the installation process.

    Then this errors shows up:

    An AJAX HTTP error occurred.
    HTTP Result Code: 200
    Debugging information follows.
    Path: /core/install.php?langcode=es&profile=standard&id=1&op=do_nojs&op=do
    StatusText: OK
    ResponseText: 
    Fatal error:  Class name must be a valid object or a string in D:\DRUPAL OFICIAL\d8\core\lib\Drupal\Core\Entity\Annotation\EntityType.php on line 63

    So I investigated and debugged the error with XDebug a few times to find out that this is probably some sort of encoding issue.

    The error originates because this line:

    $class = $values['entity_type_class'];

    is returning NULL ($class = null).

    But.... the $values array DOES contain a KEY named 'entity_type_class'. The only possible explanation for this is that the key inside the array and the 'entity_type_class' literal have different encodings or are not binary equal.

    But I tried all imaginable ways to determine if both strings (key and literal) had different encodings, and they look to be exactly the same, PHP's mb_detect_encoding reports they are both ASCII encoded, and using PHP's == and === operator on the literal and the key return TRUE.

    Saturday, May 16, 2015 11:03 PM

All replies

  • User409000176 posted

    wincache.ocenabled=0

    If you've disabled the opcache, then the opcache can't be the culprit. 

    Did you mean wincache.ocenabled=1 ?

    Thx!

        --E.

    Monday, May 18, 2015 2:09 PM
  • User962350831 posted

    Ups.. I'll have to make double sure that wincache is responsible for this. Can't imagine how the ocenabled=0 got in there.

    I'll post back after double checking.

    Greetings!

    Monday, May 18, 2015 2:43 PM
  • User962350831 posted

    Confirmed! I spent time on this because today I found a similar issue on D7, the problem manifests itself in such a strange way in the debugger you clearly see that the array item exists, but accessing the array item fails returning null. (the tests were run with the debugger extension completely disabled to discard this as a source of the issue).

    There have been changes in D8 since my last tests, and the error location has changed but it still of the same nature.

    I tried this on a clean PHP 5.6.9 freshly downloaded from the PHP site + a fresh wincache (1.3.7.4) binary from codeplex. All settings in PHP.ini were verified to be efectively setup before running the installation process by calling phpinfo(), and IIS reset between every test.

    Steps:

    1. Download PHP 5.6.9 and copy to desired location and enable the pdo_mysql extension in php.ini.

    2. Download Wincache 1.3.7.4 and copy to the extension folder.

    3. Clone a D8 copy to a local folder with GIT and in sites/all/default rename default.settings.php to settings.php

    4. Configure a site in IIS with PHP Manager that uses the previsouly setup PHP and D8.

    5. Browse your site and run the installer - all OK. You can provide MySQL database names on the fly and they will get created.

    6. Delete sites/all/default/files/* and settings.php to be able to start the installation again.

    7. Enable the wincache extension and set wincache.ocenabled=1

    8. Browse your site and run the installer - fails inmediately.

    9. Disable wincache, and enable zend opcache.

    10. Delete sites/all/default/files/* and settings.php to be able to install the installation again.

    11. Browse your site and run the installer - OK.

    I also tried to disable file_rerouting (with wincache opcache turned on) with no success.

    The issue is that accesing a specific item in an associative array returns null (when the item does exist) and this null gets carried on until an exception is thrown somewhere else in the code due to that null propagating.

    I have a remote feeling that this might be related to the PhpBackend storage backend. In D8 they were quite smart and knew that on cheap hosting and for hobbyists setting upc APC or Wincache was too much, so they created a storage backend that uses Opcache to store data.

    The implementation is in core\lib\Drupal\Core\Cache\PHpBackend.php and core\lib\Drupal\Componente\PhpStorage\FileStorage.php

    This cache backend writes php files to the disk with serialized data, and then retrieves the data with include_once().

    There is nothing else I can think of that could lead to such a bizarre situation and related to Opcode caching (that has proven stable for so long in D7).

    D8 is soooo complex that they even found serveral bugs in PHP itself. HHVM included Drupal (and other CMS's) into their test suite.

    Friday, May 22, 2015 9:25 PM
  • User409000176 posted

    Well, it sounds like disabling WinCache's opcode cache and using the Zend Opcode cache is the right answer.

    We've marked the WinCache opcode cache as deprecated as of 1.3.6.3, and it's disabled by default for PHP 5.5 and above.  The reason being is that Zend Opcache was added to the core product in PHP 5.5. Wherever possible, folks should be using the Zend Opcache, and getting away from WinCache's opcode cache.

    Thx!

        --E.

    Tuesday, May 26, 2015 6:21 PM
  • User962350831 posted

    Fair enough.

    For anyone having problems with Zend Opcache on Windows, this is what it is all about:

    https://github.com/zendtech/ZendOptimizerPlus/issues/167

    http://forums.iis.net/p/1216348/2084743.aspx?Removal+Of+Opcache+Feature+Zend+Opcache+Failure

    https://bugs.php.net/bug.php?id=69233

    https://bugs.php.net/bug.php?id=69183

    https://bugs.php.net/bug.php?id=69067

    I just wonder if there is any workaround for the ASLR issue on production environments that does work as the proposed ones in these issues simply reduce the probablity of the issue happening, but do not really prevent it.

    Wednesday, May 27, 2015 10:19 AM
  • User409000176 posted

    I just wonder if there is any workaround for the ASLR issue

    Hm.  WinCache definitely has the problem when subsequent processes aren't able to map the opcode cache segment at the same address as the php-cgi.exe instance that created the segment.  In that case, WinCache detects the problem, and falls back to a "local" opcode cache, scoped to the process instance.  Sure, you wind up paying the re-compilation price, but at least it gets cached.

    In recent builds I worked on improving the algorithm used to select the memory segment's base address.  Before, it just used whatever address the first instance used, and was subject to the ASLR problem.  Now, it goes to the top end of the virtual address space and walks backwards until it finds a "big enough" spot for the segment.  This strategy seemed to improve the rate at which we avoided falling back to a "local" opcode cache.  I wasn't able to measure the improvement, however.

    FYI, I sent the code I used to select a better base address to one of the PHP folks a few months ago.  I don't know if they incorporated it into Zend Opcache or not.

    Thx!

        --E.

    Thursday, May 28, 2015 3:48 PM
  • User962350831 posted

    This is not at all my field but... can both PHP and its extensions be compiled with ASLR disabled? Is it worth trying?

    Previously you had to opt in to allowing the linker to use ASLR. Now, you have to opt out:

    /DYNAMICBASE[:NO]
    

    (Visual Studio 2012: Configuration Properties -> Linker -> Advanced -> "Randomized Base Address")

    It looks like you can also disable ASLR system wide, not sure if that is a good idea at all...

    Thursday, May 28, 2015 8:46 PM
  • User409000176 posted

    PHP links with system DLLs, which will still use ASLR.  So, recompiling PHP without ASLR isn't really going to help you that much.  You still run the risk of having some random allocation get dropped in a place that will prevent you from mapping a large swath of memory.

    Thx!

        --E.

    Friday, May 29, 2015 6:28 PM