Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent layout cache corruption in edge case #12314

Merged

Conversation

scottsb
Copy link
Member

@scottsb scottsb commented Nov 16, 2017

Description

When layout is loaded from the cache, it ought to repopulate the list of applied layout handles to prevent any chance of a layout handle being reapplied later, layering on top of the XML loaded from cache. This pull request adds that missing functionality.

The bug caused by the status quo is triggered when layout is loaded early before the page actually begins to render and before all layout handles may be applied. (Layout is loaded early, for example, when $resultPage->getConfig()->getTitle() is called, as happens in multiple core controllers, including checkout.) If this initial layout load matches a cache key but the latter layout load (with all layout handles assembled) does not, then when the latter layout load occurs, the guard condition which attempts to block duplicate layout handle application (in _merge of Magento\Framework\View\Model\Layout\Merge) does not get triggered, since the layout handles loaded from cache were not populated.

This duplicate XML generally does not cause issues, because earlier nodes are replaced by later nodes via the _overrideElementWorkaround method in \Magento\Framework\View\Layout\ScheduledStructure\Helper, and since the duplicate nodes are the same as the originals, there's no problem. However, because this method removes the entire tree (including descendants) of the first instance of duplicated nodes, it can lead to nodes missing from the final structure, depending on the layout order sequence. (For example, if a parent node such as <container name="content" label="Main Content Area"/> ends up appearing after some element that was supposed to be inserted into that container, the child block never gets re-added.) This method's algorithm is a little clunky itself and could probably be refined, but by merging this fix, we avoid at least one scenario where this algorithm is even needed.

Manual testing scenarios

As far as I've seen this does not cause any issues in core alone, but it can cause issues when custom modules extend core in standard ways. This describes one concrete case where this can occur, but it is likely to occur in other cases as well.

  1. Create a custom module:
    1. Add an afterExecute plugin on Magento\Checkout\Controller\Index\Index.
    2. In this plugin method, conditionally add a layout handle if the cart contains a particular product.
  2. Enable and clear layout cache.
  3. Add a product to the cart other than the one checked in the plugin condition.
  4. Load checkout. (Checkout should appear correctly.)
  5. Add the product to the cart checked in the plugin condition.
  6. Load checkout again. (Without this fix, checkout will break, as the customer.customer.data block will not be loaded. With this fix, checkout will load correctly.)

@scottsb scottsb changed the title Prevent edge case layout cache corruption Prevent layout cache corruption in edge case Nov 16, 2017
@ishakhsuvarov ishakhsuvarov self-assigned this Nov 18, 2017
@ishakhsuvarov ishakhsuvarov added this to the November 2017 milestone Nov 18, 2017
@okorshenko okorshenko modified the milestones: December 2017, January 2018 Jan 8, 2018
@ladle3000
Copy link

@scottsb How can I add this fix to my M 2.2 installation? I'm hitting this exact error I believe when Magestore GiftCard Module is running on my store.

@scottsb
Copy link
Member Author

scottsb commented Jan 28, 2018

@snoroozi You could create a preference in a local module for this class, overriding the load() method with the change from this PR. Or you might be able to use the PR patch with this patch application module (if the PR patch doesn't work, it probably would only need a little fine tuning).

@okorshenko okorshenko modified the milestones: January 2018, February 2018 Feb 7, 2018
@okorshenko okorshenko modified the milestones: February 2018, March 2018 Mar 1, 2018
@okorshenko okorshenko modified the milestones: March 2018, April 2018 Apr 16, 2018
@magento-engcom-team
Copy link
Contributor

@scottsb thank you for contributing. Please accept Community Contributors team invitation here to gain extended permissions for this repository.

@okorshenko okorshenko modified the milestones: April 2018, May 2018 May 18, 2018
@okorshenko okorshenko removed this from the May 2018 milestone May 31, 2018
@magento-engcom-team magento-engcom-team merged commit a97cc6d into magento:2.2-develop Jun 6, 2018
@magento-engcom-team
Copy link
Contributor

Hi @scottsb. Thank you for your contribution.
Changes from your Pull Request will be available with the upcoming 2.2.6 release.

Please, consider to port this solution to 2.3 release line.
You may use Porting tool to port commits automatically.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants