Skip to content

Commit

Permalink
Merge branch '2.4-develop' of github.com:magento/magento2 into 1703-d…
Browse files Browse the repository at this point in the history
…eleted-tags-not-removed
  • Loading branch information
yolouiese committed Aug 14, 2020
2 parents 3b350fd + 7198c76 commit bf77de0
Show file tree
Hide file tree
Showing 125 changed files with 4,670 additions and 1,722 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,9 @@
<actionGroup ref="FilterProductGridByNameActionGroup" stepKey="filterBundleProductOptionsDownToName">
<argument name="product" value="BundleProduct"/>
</actionGroup>
<click selector="{{AdminProductFiltersSection.allCheckbox}}" stepKey="SelectAllOnly1"/>
<waitForPageLoad stepKey="loading2"/>

<!--Delete-->
<click selector="{{AdminProductFiltersSection.actions}}" stepKey="ClickOnActionsChangingView"/>
<click selector="{{AdminProductFiltersSection.delete}}" stepKey="ClickDelete"/>
<click selector="//button[@class='action-primary action-accept']" stepKey="ConfirmDelete"/>
<waitForPageLoad stepKey="loading3"/>
<actionGroup ref="AdminDeleteAllProductsFromGridActionGroup" stepKey="selectAndDeleteProducts"/>

<!--Locating delete message-->
<seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="deleteMessage"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,7 @@
<actionGroup ref="BundleProductFilter" stepKey="FilterForOnlyBundleProducts"/>

<!--Delete-->
<click selector="{{AdminProductFiltersSection.allCheckbox}}" stepKey="SelectAllOnly1"/>
<waitForPageLoad stepKey="loading"/>
<click selector="{{AdminProductFiltersSection.actions}}" stepKey="ClickOnActionsChangingView"/>
<click selector="{{AdminProductFiltersSection.delete}}" stepKey="ClickDelete"/>
<click selector="//button[@class='action-primary action-accept']" stepKey="ConfirmDelete"/>
<waitForPageLoad stepKey="loading3"/>
<actionGroup ref="AdminDeleteAllProductsFromGridActionGroup" stepKey="selectAndDeleteProducts"/>

<!--Locating delete message-->
<seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="deleteMessage"/>
Expand Down
62 changes: 36 additions & 26 deletions app/code/Magento/CacheInvalidate/Model/PurgeCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,24 @@
namespace Magento\CacheInvalidate\Model;

use Magento\Framework\Cache\InvalidateLogger;
use Magento\PageCache\Model\Cache\Server;
use Laminas\Http\Client\Adapter\Socket;
use Laminas\Uri\Uri;

/**
* PurgeCache model
* Invalidate external HTTP cache(s) based on tag pattern
*/
class PurgeCache
{
const HEADER_X_MAGENTO_TAGS_PATTERN = 'X-Magento-Tags-Pattern';

/**
* @var \Magento\PageCache\Model\Cache\Server
* @var Server
*/
protected $cacheServer;

/**
* @var \Magento\CacheInvalidate\Model\SocketFactory
* @var SocketFactory
*/
protected $socketAdapterFactory;

Expand All @@ -39,39 +42,46 @@ class PurgeCache
*
* @var int
*/
private $requestSize = 7680;
private $maxHeaderSize;

/**
* Constructor
*
* @param \Magento\PageCache\Model\Cache\Server $cacheServer
* @param \Magento\CacheInvalidate\Model\SocketFactory $socketAdapterFactory
* @param Server $cacheServer
* @param SocketFactory $socketAdapterFactory
* @param InvalidateLogger $logger
* @param int $maxHeaderSize
*/
public function __construct(
\Magento\PageCache\Model\Cache\Server $cacheServer,
\Magento\CacheInvalidate\Model\SocketFactory $socketAdapterFactory,
InvalidateLogger $logger
Server $cacheServer,
SocketFactory $socketAdapterFactory,
InvalidateLogger $logger,
int $maxHeaderSize = 7680
) {
$this->cacheServer = $cacheServer;
$this->socketAdapterFactory = $socketAdapterFactory;
$this->logger = $logger;
$this->maxHeaderSize = $maxHeaderSize;
}

/**
* Send curl purge request to invalidate cache by tags pattern
*
* @param string $tagsPattern
* @param array|string $tags
* @return bool Return true if successful; otherwise return false
*/
public function sendPurgeRequest($tagsPattern)
public function sendPurgeRequest($tags)
{
if (is_string($tags)) {
$tags = [$tags];
}

$successful = true;
$socketAdapter = $this->socketAdapterFactory->create();
$servers = $this->cacheServer->getUris();
$socketAdapter->setOptions(['timeout' => 10]);

$formattedTagsChunks = $this->splitTags($tagsPattern);
$formattedTagsChunks = $this->chunkTags($tags);
foreach ($formattedTagsChunks as $formattedTagsChunk) {
if (!$this->sendPurgeRequestToServers($socketAdapter, $servers, $formattedTagsChunk)) {
$successful = false;
Expand All @@ -82,24 +92,24 @@ public function sendPurgeRequest($tagsPattern)
}

/**
* Split tags by batches
* Split tags into batches to suit Varnish max. header size
*
* @param string $tagsPattern
* @param array $tags
* @return \Generator
*/
private function splitTags($tagsPattern)
private function chunkTags(array $tags): \Generator
{
$tagsBatchSize = 0;
$currentBatchSize = 0;
$formattedTagsChunk = [];
$formattedTags = explode('|', $tagsPattern);
foreach ($formattedTags as $formattedTag) {
if ($tagsBatchSize + strlen($formattedTag) > $this->requestSize - count($formattedTagsChunk) - 1) {
foreach ($tags as $formattedTag) {
// Check if (currentBatchSize + length of next tag + number of pipe delimiters) would exceed header size.
if ($currentBatchSize + strlen($formattedTag) + count($formattedTagsChunk) > $this->maxHeaderSize) {
yield implode('|', $formattedTagsChunk);
$formattedTagsChunk = [];
$tagsBatchSize = 0;
$currentBatchSize = 0;
}

$tagsBatchSize += strlen($formattedTag);
$currentBatchSize += strlen($formattedTag);
$formattedTagsChunk[] = $formattedTag;
}
if (!empty($formattedTagsChunk)) {
Expand All @@ -110,12 +120,12 @@ private function splitTags($tagsPattern)
/**
* Send curl purge request to servers to invalidate cache by tags pattern
*
* @param \Laminas\Http\Client\Adapter\Socket $socketAdapter
* @param \Laminas\Uri\Uri[] $servers
* @param Socket $socketAdapter
* @param Uri[] $servers
* @param string $formattedTagsChunk
* @return bool Return true if successful; otherwise return false
*/
private function sendPurgeRequestToServers($socketAdapter, $servers, $formattedTagsChunk)
private function sendPurgeRequestToServers(Socket $socketAdapter, array $servers, string $formattedTagsChunk): bool
{
$headers = [self::HEADER_X_MAGENTO_TAGS_PATTERN => $formattedTagsChunk];
$unresponsiveServerError = [];
Expand Down Expand Up @@ -145,14 +155,14 @@ private function sendPurgeRequestToServers($socketAdapter, $servers, $formattedT
if ($errorCount == count($servers)) {
$this->logger->critical(
'No cache server(s) could be purged ' . $loggerMessage,
compact('server', 'formattedTagsChunk')
compact('servers', 'formattedTagsChunk')
);
return false;
}

$this->logger->warning(
'Unresponsive cache server(s) hit' . $loggerMessage,
compact('server', 'formattedTagsChunk')
compact('servers', 'formattedTagsChunk')
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

use Magento\Framework\Event\ObserverInterface;

/**
* Clear configured Varnish hosts when triggering a full cache flush (e.g. from the Cache Management admin dashboard)
*/
class FlushAllCacheObserver implements ObserverInterface
{
/**
Expand Down Expand Up @@ -43,7 +46,7 @@ public function __construct(
public function execute(\Magento\Framework\Event\Observer $observer)
{
if ($this->config->getType() == \Magento\PageCache\Model\Config::VARNISH && $this->config->isEnabled()) {
$this->purgeCache->sendPurgeRequest('.*');
$this->purgeCache->sendPurgeRequest(['.*']);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public function execute(Observer $observer)
$tags[] = sprintf($pattern, $tag);
}
if (!empty($tags)) {
$this->purgeCache->sendPurgeRequest(implode('|', array_unique($tags)));
$this->purgeCache->sendPurgeRequest(array_unique($tags));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ protected function setUp(): void
'cacheServer' => $this->cacheServer,
'socketAdapterFactory' => $socketFactoryMock,
'logger' => $this->loggerMock,
'maxHeaderSize' => 256
]
);
}
Expand All @@ -64,6 +65,7 @@ protected function setUp(): void
public function testSendPurgeRequest($hosts)
{
$uris = [];
/** @var array $host */
foreach ($hosts as $host) {
$port = isset($host['port']) ? $host['port'] : Server::DEFAULT_PORT;
$uris[] = UriFactory::factory('')->setHost($host['host'])
Expand Down Expand Up @@ -92,7 +94,50 @@ public function testSendPurgeRequest($hosts)
$this->loggerMock->expects($this->once())
->method('execute');

$this->assertTrue($this->model->sendPurgeRequest('tags'));
$this->assertTrue($this->model->sendPurgeRequest(['tags']));
}

public function testSendMultiPurgeRequest()
{
$tags = [
'(^|,)cat_p_95(,|$)', '(^|,)cat_p_96(,|$)', '(^|,)cat_p_97(,|$)', '(^|,)cat_p_98(,|$)',
'(^|,)cat_p_99(,|$)', '(^|,)cat_p_100(,|$)', '(^|,)cat_p_10038(,|$)', '(^|,)cat_p_142985(,|$)',
'(^|,)cat_p_199(,|$)', '(^|,)cat_p_300(,|$)', '(^|,)cat_p_12038(,|$)', '(^|,)cat_p_152985(,|$)',
'(^|,)cat_p_299(,|$)', '(^|,)cat_p_400(,|$)', '(^|,)cat_p_13038(,|$)', '(^|,)cat_p_162985(,|$)',
];

$tagsSplitA = array_slice($tags, 0, 12);
$tagsSplitB = array_slice($tags, 12, 4);

$uri = UriFactory::factory('')->setHost('localhost')
->setPort(80)
->setScheme('http');

$this->cacheServer->expects($this->once())
->method('getUris')
->willReturn([$uri]);

$this->socketAdapterMock->expects($this->exactly(2))
->method('connect')
->with($uri->getHost(), $uri->getPort());

$this->socketAdapterMock->expects($this->exactly(2))
->method('write')
->withConsecutive(
[
'PURGE', $uri, '1.1',
['X-Magento-Tags-Pattern' => implode('|', $tagsSplitA), 'Host' => $uri->getHost()]
],
[
'PURGE', $uri, '1.1',
['X-Magento-Tags-Pattern' => implode('|', $tagsSplitB), 'Host' => $uri->getHost()]
]
);

$this->socketAdapterMock->expects($this->exactly(2))
->method('close');

$this->assertTrue($this->model->sendPurgeRequest($tags));
}

/**
Expand Down Expand Up @@ -130,6 +175,6 @@ public function testSendPurgeRequestWithException()
$this->loggerMock->expects($this->once())
->method('critical');

$this->assertFalse($this->model->sendPurgeRequest('tags'));
$this->assertFalse($this->model->sendPurgeRequest(['tags']));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function testFlushAllCache()
Config::VARNISH
);

$this->purgeCache->expects($this->once())->method('sendPurgeRequest')->with('.*');
$this->purgeCache->expects($this->once())->method('sendPurgeRequest')->with(['.*']);
$this->model->execute($this->observerMock);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ protected function setUp(): void
public function testInvalidateVarnish()
{
$tags = ['cache_1', 'cache_group'];
$pattern = '((^|,)cache_1(,|$))|((^|,)cache_group(,|$))';
$pattern = ['((^|,)cache_1(,|$))', '((^|,)cache_group(,|$))'];

$this->configMock->expects($this->once())->method('isEnabled')->willReturn(true);
$this->configMock->expects(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->

<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
<actionGroup name="AdminDeleteAllProductsFromGridActionGroup">
<annotations>
<description>Select and delete products in product grid.</description>
</annotations>
<click selector="{{AdminProductFiltersSection.allCheckbox}}" stepKey="selectAllProducts"/>
<click selector="{{AdminProductFiltersSection.actions}}" stepKey="clickOnActionsChangingView"/>
<click selector="{{AdminProductFiltersSection.delete}}" stepKey="clickDelete"/>
<click selector="//button[@class='action-primary action-accept']" stepKey="confirmDelete"/>
<waitForPageLoad stepKey="waitingProductGridLoad"/>
</actionGroup>
</actionGroups>
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,24 @@
<testCaseId value="https://studio.cucumber.io/projects/131313/test-plan/folders/1320712/scenarios/4931106"/>
<group value="product"/>
</annotations>

<before>
<actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
<createData entity="simpleProductWithShortNameAndSku" stepKey="createSimpleProduct"/>
<createData entity="SimpleProduct2" stepKey="createSimpleProduct"/>
</before>

<after>
<actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/>
<deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/>
<actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndex"/>
<actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/>
<actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/>
</after>
<amOnPage url="{{AdminProductIndexPage.url}}?filters[name]=$$createSimpleProduct.name$$" stepKey="navigateToProductGridWithFilters"/>

<amOnPage url="{{AdminProductIndexPage.url}}?filters[name]=$createSimpleProduct.name$" stepKey="navigateToProductGridWithFilters"/>
<waitForPageLoad stepKey="waitForProductGrid"/>
<see selector="{{AdminProductGridSection.productGridNameProduct($$createSimpleProduct.name$$)}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProduct"/>
<see selector="{{AdminProductGridSection.productGridNameProduct($createSimpleProduct.name$)}}" userInput="$createSimpleProduct.name$" stepKey="seeProduct"/>
<waitForElementVisible selector="{{AdminProductGridFilterSection.enabledFilters}}" stepKey="waitForEnabledFilters"/>
<seeElement selector="{{AdminProductGridFilterSection.enabledFilters}}" stepKey="seeEnabledFilters"/>
<see selector="{{AdminProductGridFilterSection.enabledFilters}}" userInput="Name: $$createSimpleProduct.name$$" stepKey="seeProductNameFilter"/>
<see selector="{{AdminProductGridFilterSection.enabledFilters}}" userInput="Name: $createSimpleProduct.name$" stepKey="seeProductNameFilter"/>
</test>
</tests>
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
namespace Magento\CatalogImportExport\Test\Unit\Model\Export;

use Magento\Catalog\Model\Product\LinkTypeProvider;
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
use Magento\CatalogImportExport\Model\Export\Product;
use Magento\CatalogImportExport\Model\Export\Product\Type\Factory;
use Magento\CatalogImportExport\Model\Export\RowCustomizer\Composite;
use Magento\Eav\Model\Config;
use Magento\Eav\Model\Entity\Collection\AbstractCollection;
use Magento\Eav\Model\Entity\Type;
use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory as AttributeSetCollectionFactory;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\Logger\Monolog;
Expand Down Expand Up @@ -83,7 +85,7 @@ class ProductTest extends TestCase
protected $attrSetColFactory;

/**
* @var \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory|MockObject
* @var CategoryCollectionFactory|MockObject
*/
protected $categoryColFactory;

Expand Down Expand Up @@ -174,15 +176,14 @@ protected function setUp(): void
->onlyMethods(['create'])
->getMock();

$this->attrSetColFactory = $this->getMockBuilder(
\Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory::class
)->addMethods(['setEntityTypeFilter'])
$this->attrSetColFactory = $this->getMockBuilder(AttributeSetCollectionFactory::class)
->disableOriginalConstructor()
->addMethods(['setEntityTypeFilter'])
->onlyMethods(['create'])
->getMock();

$this->categoryColFactory = $this->getMockBuilder(
\Magento\Catalog\Model\ResourceModel\Category\CollectionFactory::class
)->addMethods(['addNameToResult'])
$this->categoryColFactory = $this->getMockBuilder(CategoryCollectionFactory::class)
->disableOriginalConstructor()->addMethods(['addNameToResult'])
->onlyMethods(['create'])
->getMock();

Expand Down
Loading

0 comments on commit bf77de0

Please sign in to comment.