mirror of
https://github.com/bdnugget/magento2.git
synced 2026-11-07 09:17:35 +00:00
Varnish 6.0 is half a decade old. 4.x and 5.x have been EOL'd years ago and there shouldn't be any more distribution carrying those, there's no good reason to keep them around
175 lines
5.2 KiB
PHP
175 lines
5.2 KiB
PHP
<?php
|
|
/**
|
|
* Copyright © Magento, Inc. All rights reserved.
|
|
* See COPYING.txt for license details.
|
|
*/
|
|
namespace Magento\CacheInvalidate\Model;
|
|
|
|
use Exception;
|
|
use Generator;
|
|
use Magento\Framework\Cache\InvalidateLogger;
|
|
use Magento\PageCache\Model\Cache\Server;
|
|
use Laminas\Http\Client\Adapter\Socket;
|
|
use Laminas\Uri\Uri;
|
|
|
|
/**
|
|
* Invalidate external HTTP cache(s) based on tag pattern
|
|
*/
|
|
class PurgeCache
|
|
{
|
|
public const HEADER_X_MAGENTO_TAGS_PATTERN = 'X-Magento-Tags-Pattern';
|
|
|
|
/**
|
|
* @var Server
|
|
*/
|
|
protected $cacheServer;
|
|
|
|
/**
|
|
* @var SocketFactory
|
|
*/
|
|
protected $socketAdapterFactory;
|
|
|
|
/**
|
|
* @var InvalidateLogger
|
|
*/
|
|
private $logger;
|
|
|
|
/**
|
|
* Batch size of the purge request.
|
|
*
|
|
* Based on default Varnish 6 http_req_hdr_len size minus a 512 bytes margin for method,
|
|
* header name, line feeds etc.
|
|
*
|
|
* @see https://varnish-cache.org/docs/6.0/reference/varnishd.html
|
|
*
|
|
* @var int
|
|
*/
|
|
private $maxHeaderSize;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param Server $cacheServer
|
|
* @param SocketFactory $socketAdapterFactory
|
|
* @param InvalidateLogger $logger
|
|
* @param int $maxHeaderSize
|
|
*/
|
|
public function __construct(
|
|
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 array|string $tags
|
|
* @return bool Return true if successful; otherwise return false
|
|
*/
|
|
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->chunkTags($tags);
|
|
foreach ($formattedTagsChunks as $formattedTagsChunk) {
|
|
if (!$this->sendPurgeRequestToServers($socketAdapter, $servers, $formattedTagsChunk)) {
|
|
$successful = false;
|
|
}
|
|
}
|
|
|
|
return $successful;
|
|
}
|
|
|
|
/**
|
|
* Split tags into batches to suit Varnish max. header size
|
|
*
|
|
* @param array $tags
|
|
* @return Generator
|
|
*/
|
|
private function chunkTags(array $tags): Generator
|
|
{
|
|
$currentBatchSize = 0;
|
|
$formattedTagsChunk = [];
|
|
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 = [];
|
|
$currentBatchSize = 0;
|
|
}
|
|
|
|
$currentBatchSize += strlen($formattedTag ?: '');
|
|
$formattedTagsChunk[] = $formattedTag;
|
|
}
|
|
if (!empty($formattedTagsChunk)) {
|
|
yield implode('|', $formattedTagsChunk);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send curl purge request to servers to invalidate cache by tags pattern
|
|
*
|
|
* @param Socket $socketAdapter
|
|
* @param Uri[] $servers
|
|
* @param string $formattedTagsChunk
|
|
* @return bool Return true if successful; otherwise return false
|
|
*/
|
|
private function sendPurgeRequestToServers(Socket $socketAdapter, array $servers, string $formattedTagsChunk): bool
|
|
{
|
|
$headers = [self::HEADER_X_MAGENTO_TAGS_PATTERN => $formattedTagsChunk];
|
|
$unresponsiveServerError = [];
|
|
foreach ($servers as $server) {
|
|
$headers['Host'] = $server->getHost();
|
|
try {
|
|
$socketAdapter->connect($server->getHost(), $server->getPort());
|
|
$socketAdapter->write(
|
|
'PURGE',
|
|
$server,
|
|
'1.1',
|
|
$headers
|
|
);
|
|
$socketAdapter->read();
|
|
$socketAdapter->close();
|
|
} catch (Exception $e) {
|
|
$unresponsiveServerError[] = "Cache host: " . $server->getHost() . ":" . $server->getPort() .
|
|
"resulted in error message: " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
$errorCount = count($unresponsiveServerError);
|
|
|
|
if ($errorCount > 0) {
|
|
$loggerMessage = implode(" ", $unresponsiveServerError);
|
|
|
|
if ($errorCount == count($servers)) {
|
|
$this->logger->critical(
|
|
'No cache server(s) could be purged ' . $loggerMessage,
|
|
compact('servers', 'formattedTagsChunk')
|
|
);
|
|
return false;
|
|
}
|
|
|
|
$this->logger->warning(
|
|
'Unresponsive cache server(s) hit' . $loggerMessage,
|
|
compact('servers', 'formattedTagsChunk')
|
|
);
|
|
}
|
|
|
|
$this->logger->execute(compact('servers', 'formattedTagsChunk'));
|
|
return true;
|
|
}
|
|
}
|