diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..0134467
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,48 @@
+name: CI
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ phpunit:
+ name: PHPUnit (PHP ${{ matrix.php_version }})
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php_version: [ '8.1', '8.5' ]
+
+ steps:
+ - uses: actions/checkout@v7
+ - uses: php-actions/composer@v6
+ with:
+ php_version: ${{ matrix.php_version }}
+ - name: PHPUnit Tests
+ uses: php-actions/phpunit@v4
+ with:
+ php_version: ${{ matrix.php_version }}
+ php_extensions: xdebug
+ bootstrap: vendor/autoload.php
+ configuration: phpunit.xml
+ coverage_text: true
+ env:
+ XDEBUG_MODE: coverage
+
+ phpstan:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v7
+ - uses: php-actions/composer@v6
+ with:
+ php_version: '8.1'
+ - uses: php-actions/phpstan@v3
+ with:
+ php_version: '8.1'
+ configuration: phpstan.neon
diff --git a/.gitignore b/.gitignore
index 3c3629e..66f204b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,6 @@
node_modules
+composer.lock
+vendor
+Packages
+.phpunit.cache
+.phpunit.result.cache
diff --git a/Classes/Aspects/CacheUrlMappingAspect.php b/Classes/Aspects/CacheUrlMappingAspect.php
index 24bad75..3a31b58 100644
--- a/Classes/Aspects/CacheUrlMappingAspect.php
+++ b/Classes/Aspects/CacheUrlMappingAspect.php
@@ -4,18 +4,20 @@
namespace Flowpack\DecoupledContentStore\Aspects;
-use Flowpack\DecoupledContentStore\Exception;
use Flowpack\DecoupledContentStore\Core\Infrastructure\ContentReleaseLogger;
+use Flowpack\DecoupledContentStore\Exception;
use Flowpack\DecoupledContentStore\NodeRendering\Dto\DocumentNodeCacheKey;
use Flowpack\DecoupledContentStore\NodeRendering\Dto\DocumentNodeCacheValues;
use Flowpack\DecoupledContentStore\NodeRendering\Extensibility\NodeRenderingExtensionManager;
use Flowpack\DecoupledContentStore\NodeRendering\Render\DocumentRenderer;
use Flowpack\DecoupledContentStore\NodeRendering\Render\RenderExceptionExtractor;
+use Neos\Cache\Exception\InvalidDataException;
+use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Aop\JoinPointInterface;
use Neos\Fusion\Core\Cache\CacheSegmentParser;
+use Neos\Utility\Exception\PropertyNotAccessibleException;
use Neos\Utility\ObjectAccess;
-use Neos\ContentRepository\Domain\Model\NodeInterface;
/**
* This aspect creates the root cache entry which maps the URL to the root cache identifier during rendering.
@@ -102,13 +104,22 @@ public function getCurrentEvaluateAndControllerContext(JoinPointInterface $joinP
/**
* @Flow\After("method(Neos\Fusion\Core\Cache\ContentCache->processCacheSegments())")
+ * @throws Exception
+ * @throws \JsonException
+ * @throws \Neos\Cache\Exception
+ * @throws InvalidDataException
+ * @throws \Neos\Fusion\Exception
+ * @throws PropertyNotAccessibleException
*/
- public function storeRootCacheIdentifier(JoinPointInterface $joinPoint)
+ public function storeRootCacheIdentifier(JoinPointInterface $joinPoint): void
{
if (!$this->isActive) {
return;
}
- if (!isset($this->currentEvaluateContext['cacheIdentifierValues']['node']) || !$this->currentEvaluateContext['cacheIdentifierValues']['node'] instanceof NodeInterface) {
+ if (
+ !isset($this->currentEvaluateContext['cacheIdentifierValues']['node'])
+ || !$this->currentEvaluateContext['cacheIdentifierValues']['node'] instanceof NodeInterface
+ ) {
return;
}
@@ -122,7 +133,17 @@ public function storeRootCacheIdentifier(JoinPointInterface $joinPoint)
if (!$storeCacheEntries) {
$content = $joinPoint->getMethodArgument('content');
$extractedExceptionDto = RenderExceptionExtractor::extractRenderingException($content);
- throw new Exception('Cache was disabled for ' . $url . ' with node ' . $node->getContextPath() . ', but no exception was handled by the publishing. This could be caused by a missing publishing aware @exceptionHandler in Fusion.' . ($extractedExceptionDto !== null ? "\nException extracted from output: {$extractedExceptionDto}" : ''), 1539156004);
+ throw new Exception(
+ 'Cache was disabled for '
+ . $url
+ . ' with node '
+ . $node->getContextPath()
+ . ', but no exception was handled by the publishing. This could be caused by a missing publishing aware @exceptionHandler in Fusion.'
+ . (
+ $extractedExceptionDto !== null ? "\nException extracted from output: {$extractedExceptionDto}" : ''
+ ),
+ 1539156004
+ );
}
$content = $joinPoint->getMethodArgument('content');
@@ -141,13 +162,20 @@ public function storeRootCacheIdentifier(JoinPointInterface $joinPoint)
throw new \RuntimeException('TODO Logger not found - should never happen');
}
if ($this->urlIsMatchingBlacklist($url)) {
- $logger->info(sprintf('Skipping URL %s, because it matches the blacklist %s', $url, $this->urlExcludelistRegex));
+ $logger->info(sprintf(
+ 'Skipping URL %s, because it matches the blacklist %s',
+ $url,
+ $this->urlExcludelistRegex
+ ));
return;
}
if ($rootIdentifier === null) {
- throw new Exception('Could not find root cache identifier for ' . $url . ', possible rendering error?', 1491394849);
+ throw new Exception(
+ 'Could not find root cache identifier for ' . $url . ', possible rendering error?',
+ 1491394849
+ );
}
$logger->debug('Mapping URL ' . $url . ' to ' . $rootIdentifier . ' with tags ' . implode(', ', $rootTags));
@@ -155,11 +183,22 @@ public function storeRootCacheIdentifier(JoinPointInterface $joinPoint)
$arguments = $this->getCurrentArguments($node);
// TODO: To make parallel rendering possible, we need to make sure that the cache key also includes the currently rendered workspace, as the node might originate from a base workspace (usually live). See `DocumentNodeCacheKey`.
$rootKey = DocumentNodeCacheKey::fromNodeAndArguments($node, $arguments);
- $rootCacheValues = DocumentNodeCacheValues::create($rootIdentifier, $url)
- ->withMetadata('renderTime', (int)(microtime(true) * 1000) - $this->renderTimestamp);
+ $rootCacheValues = DocumentNodeCacheValues::create($rootIdentifier, $url)->withMetadata(
+ 'renderTime',
+ (int) ( microtime(true) * 1000 ) - $this->renderTimestamp
+ );
// allow other document metadata generators here
- $rootCacheValues = $this->nodeRenderingExtensionManager->runDocumentMetadataGenerators($node, $arguments, $this->controllerContext, $rootCacheValues);
- $this->contentCacheFrontend->set($rootKey->redisKeyName(), json_encode($rootCacheValues), $rootTags);
+ $rootCacheValues = $this->nodeRenderingExtensionManager->runDocumentMetadataGenerators(
+ $node,
+ $arguments,
+ $this->controllerContext,
+ $rootCacheValues
+ );
+ $this->contentCacheFrontend->set(
+ $rootKey->redisKeyName(),
+ json_encode($rootCacheValues, JSON_THROW_ON_ERROR),
+ $rootTags
+ );
$this->mappingWasWrittenForCurrentDocument = true;
}
@@ -174,7 +213,7 @@ protected function getCurrentUrl(): string
$url = $httpRequest->getUri();
$url = $url->withQuery('');
- return (string)$url;
+ return (string) $url;
}
/**
@@ -214,7 +253,7 @@ public function beforeDocumentRendering(ContentReleaseLogger $contentReleaseLogg
{
$this->isActive = true;
$this->contentReleaseLogger = $contentReleaseLogger;
- $this->renderTimestamp = (int)(microtime(true) * 1000);
+ $this->renderTimestamp = (int) ( microtime(true) * 1000 );
$this->mappingWasWrittenForCurrentDocument = false;
}
@@ -225,7 +264,9 @@ public function afterDocumentRendering(): void
// error), so we make some noise - otherwise the content release just fails with the retry limit and no hint
// about the reason. {@see storeRootCacheIdentifier()}
if (!$this->mappingWasWrittenForCurrentDocument && $this->contentReleaseLogger !== null) {
- $this->contentReleaseLogger->warn('No "doc--..." mapping entry was written for this rendering, so it can never be added to the content release. Either the rendering was fully served from the content cache (then the content cache entries of this node need to be flushed before re-rendering), or its URL is excluded via nodeRendering.urlExcludelistRegex while the node is still part of the enumeration.');
+ $this->contentReleaseLogger->warn(
+ 'No "doc--..." mapping entry was written for this rendering, so it can never be added to the content release. Either the rendering was fully served from the content cache (then the content cache entries of this node need to be flushed before re-rendering), or its URL is excluded via nodeRendering.urlExcludelistRegex while the node is still part of the enumeration.'
+ );
}
$this->isActive = false;
diff --git a/Classes/Aspects/FixedAssetHandlingInContentCacheFlusherAspect.php b/Classes/Aspects/FixedAssetHandlingInContentCacheFlusherAspect.php
index ca2f2a9..20eee64 100644
--- a/Classes/Aspects/FixedAssetHandlingInContentCacheFlusherAspect.php
+++ b/Classes/Aspects/FixedAssetHandlingInContentCacheFlusherAspect.php
@@ -1,5 +1,7 @@
persistenceManager->getIdentifierByObject($asset);
- $assetCacheTag = "AssetDynamicTag_" . $assetIdentifier;
+ $assetCacheTag = 'AssetDynamicTag_' . $assetIdentifier;
// WHY: ContentCacheFlusher has no public api to flush tags directly
$tagsToFlush = ObjectAccess::getProperty($contentCacheFlusher, 'tagsToFlush', true);
- $tagsToFlush[$assetCacheTag] = sprintf('which were tagged with "%s" because asset "%s" has changed.', $assetCacheTag, $assetIdentifier);
+ $tagsToFlush[$assetCacheTag] = sprintf(
+ 'which were tagged with "%s" because asset "%s" has changed.',
+ $assetCacheTag,
+ $assetIdentifier
+ );
ObjectAccess::setProperty($contentCacheFlusher, 'tagsToFlush', $tagsToFlush, true);
$usageReferences = $this->assetService->getUsageReferences($asset);
foreach ($usageReferences as $assetUsage) {
// get node that uses the asset
- $context = $this->_contextFactory->create(
- [
- 'workspaceName' => $assetUsage->getWorkspaceName(),
- 'dimensions' => $assetUsage->getDimensionValues(),
- 'invisibleContentShown' => true,
- 'removedContentShown' => true]
- );
+ $context = $this->_contextFactory->create([
+ 'workspaceName' => $assetUsage->getWorkspaceName(),
+ 'dimensions' => $assetUsage->getDimensionValues(),
+ 'invisibleContentShown' => true,
+ 'removedContentShown' => true
+ ]);
$node = $context->getNodeByIdentifier($assetUsage->getNodeIdentifier());
@@ -132,11 +137,15 @@ public function registerAssetChange(JoinPointInterface $joinPoint)
$workspaceHash = $this->cachingHelper->renderWorkspaceTagForContextNode($context->getWorkspaceName());
// 1. flush asset with workspace hash
- $assetCacheTagWithWorkspace = "AssetDynamicTag_" . $workspaceHash . "_" . $assetIdentifier;
+ $assetCacheTagWithWorkspace = 'AssetDynamicTag_' . $workspaceHash . '_' . $assetIdentifier;
// WHY: ContentCacheFlusher has no public api to flush tags directly
$tagsToFlush = ObjectAccess::getProperty($contentCacheFlusher, 'tagsToFlush', true);
- $tagsToFlush[$assetCacheTagWithWorkspace] = sprintf('which were tagged with "%s" because asset "%s" has changed.', $assetCacheTagWithWorkspace, $assetIdentifier);
+ $tagsToFlush[$assetCacheTagWithWorkspace] = sprintf(
+ 'which were tagged with "%s" because asset "%s" has changed.',
+ $assetCacheTagWithWorkspace,
+ $assetIdentifier
+ );
ObjectAccess::setProperty($contentCacheFlusher, 'tagsToFlush', $tagsToFlush, true);
// 2. flush all nodes on path to parent document node (a bit excessive, but for now it works)
diff --git a/Classes/Aspects/FixedNodeLinkHandlingInContentCacheFlusherAspect.php b/Classes/Aspects/FixedNodeLinkHandlingInContentCacheFlusherAspect.php
index d65f0bc..c9fb9d6 100644
--- a/Classes/Aspects/FixedNodeLinkHandlingInContentCacheFlusherAspect.php
+++ b/Classes/Aspects/FixedNodeLinkHandlingInContentCacheFlusherAspect.php
@@ -1,5 +1,7 @@
registerNodeChange())")
*/
@@ -37,7 +37,11 @@ public function registerNodeChange(JoinPointInterface $joinPoint)
$tagName = 'NodeDynamicTag_' . $node->getIdentifier();
$contentCacheFlusher = $joinPoint->getProxy();
$tagsToFlush = ObjectAccess::getProperty($contentCacheFlusher, 'tagsToFlush', true);
- $tagsToFlush[$tagName] = sprintf('which were tagged with "%s" because node "%s" has changed.', $tagName, $node->getIdentifier());
+ $tagsToFlush[$tagName] = sprintf(
+ 'which were tagged with "%s" because node "%s" has changed.',
+ $tagName,
+ $node->getIdentifier()
+ );
ObjectAccess::setProperty($contentCacheFlusher, 'tagsToFlush', $tagsToFlush, true);
}
}
diff --git a/Classes/BackendUi/BackendUiDataService.php b/Classes/BackendUi/BackendUiDataService.php
index 0c7a174..73a15cc 100644
--- a/Classes/BackendUi/BackendUiDataService.php
+++ b/Classes/BackendUi/BackendUiDataService.php
@@ -63,17 +63,36 @@ class BackendUiDataService
public function loadBackendOverviewData(RedisInstanceIdentifier $redisInstanceIdentifier)
{
$contentReleaseIds = $this->redisContentReleaseService->fetchAllReleaseIds($redisInstanceIdentifier);
- $metadata = $this->redisContentReleaseService->fetchMetadataForContentReleases($redisInstanceIdentifier, ...$contentReleaseIds);
+ $metadata = $this->redisContentReleaseService->fetchMetadataForContentReleases(
+ $redisInstanceIdentifier,
+ ...$contentReleaseIds
+ );
$counts = $this->redisEnumerationRepository->countMultiple($redisInstanceIdentifier, ...$contentReleaseIds);
- $iterationsCounts = $this->redisRenderingStatisticsStore->countMultipleRenderingStatistics($redisInstanceIdentifier, ...$contentReleaseIds);
- $errorCounts = $this->redisRenderingErrorManager->countMultipleErrors($redisInstanceIdentifier, ...$contentReleaseIds);
- $lastRenderingStatisticsEntries = $this->redisRenderingStatisticsStore->getLastRenderingStatisticsEntry($redisInstanceIdentifier, ...$contentReleaseIds);
- $firstRenderingStatisticsEntries = $this->redisRenderingStatisticsStore->getFirstRenderingStatisticsEntry($redisInstanceIdentifier, ...$contentReleaseIds);
+ $iterationsCounts = $this->redisRenderingStatisticsStore->countMultipleRenderingStatistics(
+ $redisInstanceIdentifier,
+ ...$contentReleaseIds
+ );
+ $errorCounts = $this->redisRenderingErrorManager->countMultipleErrors(
+ $redisInstanceIdentifier,
+ ...$contentReleaseIds
+ );
+ $lastRenderingStatisticsEntries = $this->redisRenderingStatisticsStore->getLastRenderingStatisticsEntry(
+ $redisInstanceIdentifier,
+ ...$contentReleaseIds
+ );
+ $firstRenderingStatisticsEntries = $this->redisRenderingStatisticsStore->getFirstRenderingStatisticsEntry(
+ $redisInstanceIdentifier,
+ ...$contentReleaseIds
+ );
$result = [];
foreach ($contentReleaseIds as $contentReleaseId) {
- $lastRendering = RenderingStatistics::fromJsonString($lastRenderingStatisticsEntries->getResultForContentRelease($contentReleaseId));
- $firstRendering = RenderingStatistics::fromJsonString($firstRenderingStatisticsEntries->getResultForContentRelease($contentReleaseId));
+ $lastRendering = RenderingStatistics::fromJsonString($lastRenderingStatisticsEntries->getResultForContentRelease(
+ $contentReleaseId
+ ));
+ $firstRendering = RenderingStatistics::fromJsonString($firstRenderingStatisticsEntries->getResultForContentRelease(
+ $contentReleaseId
+ ));
$metadataForContentRelease = $metadata->getResultForContentRelease($contentReleaseId);
$countForContentRelease = $counts->getResultForContentRelease($contentReleaseId);
@@ -86,36 +105,59 @@ public function loadBackendOverviewData(RedisInstanceIdentifier $redisInstanceId
is_int($countForContentRelease) ? $countForContentRelease : 0,
is_int($iterationsCountForContentRelease) ? $iterationsCountForContentRelease : 0,
is_int($errorCountForContentRelease) ? $errorCountForContentRelease : 0,
- $lastRendering->getTotalJobs() > 0 ? round($lastRendering->getRenderedJobs()
- / $lastRendering->getTotalJobs() * 100) : 100,
+ $lastRendering->getTotalJobs() > 0
+ ? round(( $lastRendering->getRenderedJobs() / $lastRendering->getTotalJobs() ) * 100)
+ : 100,
$firstRendering->getRenderedJobs(),
- $contentReleaseId->equals($this->redisReleaseSwitchService->getCurrentRelease($redisInstanceIdentifier)),
- $metadataForContentRelease instanceof ContentReleaseMetadata ? $metadataForContentRelease->getContentReleaseSize() : null
+ $contentReleaseId->equals($this->redisReleaseSwitchService->getCurrentRelease(
+ $redisInstanceIdentifier
+ )),
+ $metadataForContentRelease instanceof ContentReleaseMetadata
+ ? $metadataForContentRelease->getContentReleaseSize()
+ : null
);
}
return $result;
}
- public function loadDetailsData(ContentReleaseIdentifier $contentReleaseIdentifier, RedisInstanceIdentifier $redisInstanceIdentifier): ?ContentReleaseDetails
- {
- $contentReleaseMetadata = $this->redisContentReleaseService->fetchMetadataForContentRelease($contentReleaseIdentifier, $redisInstanceIdentifier);
+ public function loadDetailsData(
+ ContentReleaseIdentifier $contentReleaseIdentifier,
+ RedisInstanceIdentifier $redisInstanceIdentifier
+ ): ?ContentReleaseDetails {
+ $contentReleaseMetadata = $this->redisContentReleaseService->fetchMetadataForContentRelease(
+ $contentReleaseIdentifier,
+ $redisInstanceIdentifier
+ );
if (!$contentReleaseMetadata) {
return null;
}
- $contentReleaseJob = $this->prunnerApiService->loadJobDetail($contentReleaseMetadata->getPrunnerJobId()->toJobId());
-
- $manualTransferJobs = count($contentReleaseMetadata->getManualTransferJobIds()) ? array_map(function (PrunnerJobId $item) {
- return $this->prunnerApiService->loadJobDetail($item->toJobId());
- }, $contentReleaseMetadata->getManualTransferJobIds()) : [];
+ $contentReleaseJob = $this->prunnerApiService->loadJobDetail(
+ $contentReleaseMetadata->getPrunnerJobId()->toJobId()
+ );
- $renderingStatistics = array_map(function(string $item) {
- return RenderingStatistics::fromJsonString($item);
- }, $this->redisRenderingStatisticsStore->getRenderingStatistics($contentReleaseIdentifier, $redisInstanceIdentifier));
+ $manualTransferJobs = count($contentReleaseMetadata->getManualTransferJobIds())
+ ? array_map(function (PrunnerJobId $item) {
+ return $this->prunnerApiService->loadJobDetail($item->toJobId());
+ }, $contentReleaseMetadata->getManualTransferJobIds())
+ : [];
+
+ $renderingStatistics = array_map(
+ function (string $item) {
+ return RenderingStatistics::fromJsonString($item);
+ },
+ $this->redisRenderingStatisticsStore->getRenderingStatistics(
+ $contentReleaseIdentifier,
+ $redisInstanceIdentifier
+ )
+ );
- $renderingErrorCount = count($this->redisRenderingErrorManager->getRenderingErrors($contentReleaseIdentifier, $redisInstanceIdentifier));
+ $renderingErrorCount = count($this->redisRenderingErrorManager->getRenderingErrors(
+ $contentReleaseIdentifier,
+ $redisInstanceIdentifier
+ ));
$currentReleaseIdentifier = $this->redisReleaseSwitchService->getCurrentRelease($redisInstanceIdentifier);
diff --git a/Classes/BackendUi/Dto/ContentReleaseDetails.php b/Classes/BackendUi/Dto/ContentReleaseDetails.php
index 8375a1e..10802e6 100644
--- a/Classes/BackendUi/Dto/ContentReleaseDetails.php
+++ b/Classes/BackendUi/Dto/ContentReleaseDetails.php
@@ -31,8 +31,16 @@ class ContentReleaseDetails
*/
private array $renderingStatistics;
- public function __construct(ContentReleaseIdentifier $contentReleaseIdentifier, ?Job $job, int $enumeratedDocumentNodesCount, array $renderingStatistics, int $renderingErrorCount, bool $isActive, array $manualTransferJobIds, ?float $contentReleaseSize = null)
- {
+ public function __construct(
+ ContentReleaseIdentifier $contentReleaseIdentifier,
+ ?Job $job,
+ int $enumeratedDocumentNodesCount,
+ array $renderingStatistics,
+ int $renderingErrorCount,
+ bool $isActive,
+ array $manualTransferJobIds,
+ ?float $contentReleaseSize = null
+ ) {
$this->contentReleaseIdentifier = $contentReleaseIdentifier;
$this->job = $job;
$this->enumeratedDocumentNodesCount = $enumeratedDocumentNodesCount;
@@ -106,5 +114,4 @@ public function getManualTransferJobs(): array
{
return $this->manualTransferJobs;
}
-
}
diff --git a/Classes/BackendUi/Dto/ContentReleaseOverviewRow.php b/Classes/BackendUi/Dto/ContentReleaseOverviewRow.php
index 0064ff1..fcae6d0 100644
--- a/Classes/BackendUi/Dto/ContentReleaseOverviewRow.php
+++ b/Classes/BackendUi/Dto/ContentReleaseOverviewRow.php
@@ -23,10 +23,17 @@ class ContentReleaseOverviewRow
private bool $isActive;
private ?float $releaseSize;
- public function __construct(ContentReleaseIdentifier $contentReleaseIdentifier, ?ContentReleaseMetadata $metadata,
- int $enumeratedDocumentNodesCount, int $iterationsCount, int $errorCount,
- float $progress, int $renderedUrlCount, bool $isActive, ?float $releaseSize)
- {
+ public function __construct(
+ ContentReleaseIdentifier $contentReleaseIdentifier,
+ ?ContentReleaseMetadata $metadata,
+ int $enumeratedDocumentNodesCount,
+ int $iterationsCount,
+ int $errorCount,
+ float $progress,
+ int $renderedUrlCount,
+ bool $isActive,
+ ?float $releaseSize
+ ) {
$this->contentReleaseIdentifier = $contentReleaseIdentifier;
$this->metadata = $metadata;
$this->enumeratedDocumentNodesCount = $enumeratedDocumentNodesCount;
@@ -85,5 +92,4 @@ public function getReleaseSize(): ?float
{
return $this->releaseSize;
}
-
}
diff --git a/Classes/BackendUi/FusionObjects/RetrieveFlashMessagesImplementation.php b/Classes/BackendUi/FusionObjects/RetrieveFlashMessagesImplementation.php
index 7bdbdde..0266e7b 100644
--- a/Classes/BackendUi/FusionObjects/RetrieveFlashMessagesImplementation.php
+++ b/Classes/BackendUi/FusionObjects/RetrieveFlashMessagesImplementation.php
@@ -1,5 +1,7 @@
flashMessageService->getFlashMessageContainerForRequest($this->runtime->getControllerContext()->getRequest())->getMessagesAndFlush();
+ return $this->flashMessageService
+ ->getFlashMessageContainerForRequest($this->runtime->getControllerContext()->getRequest())
+ ->getMessagesAndFlush();
}
}
diff --git a/Classes/BackendUi/RenderingErrorExtractor.php b/Classes/BackendUi/RenderingErrorExtractor.php
index d82152f..2eea22c 100644
--- a/Classes/BackendUi/RenderingErrorExtractor.php
+++ b/Classes/BackendUi/RenderingErrorExtractor.php
@@ -35,8 +35,8 @@ public function extractErrorBlocks(string $log): array
$paragraphs = preg_split('/\n\s*\n/', $log) ?: [];
$hits = [];
foreach ($paragraphs as $index => $paragraph) {
- $hasError = (bool)preg_match('/(?:^|] )ERROR /m', $paragraph);
- $hasTrace = (bool)preg_match('/^#\d+ /m', $paragraph);
+ $hasError = (bool) preg_match('/(?:^|] )ERROR /m', $paragraph);
+ $hasTrace = (bool) preg_match('/^#\d+ /m', $paragraph);
if (!$hasError && !$hasTrace) {
continue;
}
diff --git a/Classes/BackendUi/WorkerErrorLogAggregator.php b/Classes/BackendUi/WorkerErrorLogAggregator.php
index 4dbac3f..f2da94b 100644
--- a/Classes/BackendUi/WorkerErrorLogAggregator.php
+++ b/Classes/BackendUi/WorkerErrorLogAggregator.php
@@ -37,7 +37,8 @@ class WorkerErrorLogAggregator
*/
public function aggregate(Job $job): array
{
- $renderTasks = $job->getTaskResults()
+ $renderTasks = $job
+ ->getTaskResults()
->filteredByPrefix('render_')
->withoutTasks('render_finished', 'render_orchestrator');
@@ -58,8 +59,9 @@ public function aggregate(Job $job): array
// Real failures (non-SIGTERM) carry the actual error — show them first. Within both groups,
// sort worker names naturally (1, 2, ..., 10 instead of 1, 10, ..., 2).
usort($erroredTasks, static function (TaskResult $a, TaskResult $b): int {
- $killedComparison = ($a->getExitCode() === self::EXIT_CODE_SIGTERM ? 1 : 0)
- <=> ($b->getExitCode() === self::EXIT_CODE_SIGTERM ? 1 : 0);
+ $killedComparison =
+ ( $a->getExitCode() === self::EXIT_CODE_SIGTERM ? 1 : 0 )
+ <=> ( $b->getExitCode() === self::EXIT_CODE_SIGTERM ? 1 : 0 );
return $killedComparison !== 0 ? $killedComparison : strnatcasecmp($a->getName(), $b->getName());
});
diff --git a/Classes/Command/ContentReleaseEventsCommandController.php b/Classes/Command/ContentReleaseEventsCommandController.php
index 3bf2e2e..9afe977 100644
--- a/Classes/Command/ContentReleaseEventsCommandController.php
+++ b/Classes/Command/ContentReleaseEventsCommandController.php
@@ -1,4 +1,5 @@
explode('=', $s, 2), explode(',', $where)), 1, 0) : [];
$groupBy = $groupBy ? explode(',', $groupBy) : [];
$this->output("Filters: \n");
- if($where) {
- foreach ($where as $key=>$value) {
+ if ($where) {
+ foreach ($where as $key => $value) {
$this->output(" $key = \"$value\"\n");
}
} else {
diff --git a/Classes/Command/ContentReleasePrepareCommandController.php b/Classes/Command/ContentReleasePrepareCommandController.php
index b79e1e4..465dbe1 100644
--- a/Classes/Command/ContentReleasePrepareCommandController.php
+++ b/Classes/Command/ContentReleasePrepareCommandController.php
@@ -1,4 +1,5 @@
output, $contentReleaseIdentifier);
- $this->redisContentReleaseService->createContentRelease($contentReleaseIdentifier, $prunnerJobId, $logger, $workspaceName, $accountId);
+ $this->redisContentReleaseService->createContentRelease(
+ $contentReleaseIdentifier,
+ $prunnerJobId,
+ $logger,
+ $workspaceName,
+ $accountId
+ );
}
public function ensureAllOtherInProgressContentReleasesWillBeTerminatedCommand(string $contentReleaseIdentifier): void
@@ -59,9 +70,14 @@ public function registerManualTransferJobCommand(string $contentReleaseIdentifie
$this->redisContentReleaseService->registerManualTransferJob($contentReleaseIdentifier, $prunnerJobId, $logger);
}
- public function flushContentCacheIfRequiredCommand(string $contentReleaseIdentifier, bool $flushContentCache = false): void
- {
- $logger = ContentReleaseLogger::fromConsoleOutput($this->output, ContentReleaseIdentifier::fromString($contentReleaseIdentifier));
+ public function flushContentCacheIfRequiredCommand(
+ string $contentReleaseIdentifier,
+ bool $flushContentCache = false
+ ): void {
+ $logger = ContentReleaseLogger::fromConsoleOutput(
+ $this->output,
+ ContentReleaseIdentifier::fromString($contentReleaseIdentifier)
+ );
if (!$flushContentCache) {
$logger->info('Not flushing content cache');
return;
diff --git a/Classes/Command/ContentReleaseSwitchCommandController.php b/Classes/Command/ContentReleaseSwitchCommandController.php
index b25906c..0955151 100644
--- a/Classes/Command/ContentReleaseSwitchCommandController.php
+++ b/Classes/Command/ContentReleaseSwitchCommandController.php
@@ -1,4 +1,5 @@
output, $contentReleaseIdentifier);
$redisInstanceIdentifier = RedisInstanceIdentifier::fromString($redisInstanceIdentifier);
- $this->redisReleaseSwitchService->switchContentRelease($redisInstanceIdentifier, $contentReleaseIdentifier, $logger);
+ $this->redisReleaseSwitchService->switchContentRelease(
+ $redisInstanceIdentifier,
+ $contentReleaseIdentifier,
+ $logger
+ );
}
-}
\ No newline at end of file
+}
diff --git a/Classes/Command/ContentReleaseTransferCommandController.php b/Classes/Command/ContentReleaseTransferCommandController.php
index fc5feef..c312dd5 100644
--- a/Classes/Command/ContentReleaseTransferCommandController.php
+++ b/Classes/Command/ContentReleaseTransferCommandController.php
@@ -1,4 +1,5 @@
output, $contentReleaseIdentifier);
- $logger->info(sprintf('Validating URL count of content release %s (threshold: %d%% of the currently live release).', $contentReleaseIdentifier->getIdentifier(), $this->validReleaseUrlCountThreshold * 100));
-
- $currentlyLiveReleaseIdentifier = $this->redisReleaseSwitchService->getCurrentRelease(RedisInstanceIdentifier::primary());
+ $logger->info(sprintf(
+ 'Validating URL count of content release %s (threshold: %d%% of the currently live release).',
+ $contentReleaseIdentifier->getIdentifier(),
+ $this->validReleaseUrlCountThreshold * 100
+ ));
+
+ $currentlyLiveReleaseIdentifier = $this->redisReleaseSwitchService->getCurrentRelease(
+ RedisInstanceIdentifier::primary()
+ );
if ($currentlyLiveReleaseIdentifier === null) {
$logger->info('Did not find a previous Content Release; thus exiting early (OK).');
$this->logCompletion($logger, $startedAt);
@@ -62,21 +68,39 @@ public function validateCommand(string $contentReleaseIdentifier)
$currentUrlsCount = $this->redisEnumerationRepository->count($currentlyLiveReleaseIdentifier);
$newUrlsCount = $this->redisEnumerationRepository->count($contentReleaseIdentifier);
- $minimumUrlsCount = (int)ceil($this->validReleaseUrlCountThreshold * $currentUrlsCount);
+ $minimumUrlsCount = (int) ceil($this->validReleaseUrlCountThreshold * $currentUrlsCount);
$logger->info('Previous URL Count: ' . $currentUrlsCount);
$logger->info('New URL Count: ' . $newUrlsCount);
- $logger->info(sprintf('Minimum URL Count for automatic switch: %d (new release has %d%% of the previous one).', $minimumUrlsCount, $currentUrlsCount > 0 ? round($newUrlsCount / $currentUrlsCount * 100) : 100));
-
- $alreadyRegisteredErrorCount = count($this->redisRenderingErrorManager->getRenderingErrors($contentReleaseIdentifier));
+ $logger->info(sprintf(
+ 'Minimum URL Count for automatic switch: %d (new release has %d%% of the previous one).',
+ $minimumUrlsCount,
+ $currentUrlsCount > 0 ? round(( $newUrlsCount / $currentUrlsCount ) * 100) : 100
+ ));
+
+ $alreadyRegisteredErrorCount = count($this->redisRenderingErrorManager->getRenderingErrors(
+ $contentReleaseIdentifier
+ ));
if ($alreadyRegisteredErrorCount > 0) {
- $logger->warn(sprintf('%d rendering error(s) are already registered for this release; the pipeline will abort in validate_finished.', $alreadyRegisteredErrorCount));
+ $logger->warn(sprintf(
+ '%d rendering error(s) are already registered for this release; the pipeline will abort in validate_finished.',
+ $alreadyRegisteredErrorCount
+ ));
}
- if ($newUrlsCount < $this->validReleaseUrlCountThreshold * $currentUrlsCount) {
- $message = sprintf('Invalid release due to low URL count: (has %d of currently %d, need at least %d for automatic switch)', $newUrlsCount, $currentUrlsCount, $this->validReleaseUrlCountThreshold * $currentUrlsCount);
+ if ($newUrlsCount < ( $this->validReleaseUrlCountThreshold * $currentUrlsCount )) {
+ $message = sprintf(
+ 'Invalid release due to low URL count: (has %d of currently %d, need at least %d for automatic switch)',
+ $newUrlsCount,
+ $currentUrlsCount,
+ $this->validReleaseUrlCountThreshold * $currentUrlsCount
+ );
$logger->error($message);
- $this->redisRenderingErrorManager->registerRenderingError($contentReleaseIdentifier, [], new Exception($message, 1493387482));
+ $this->redisRenderingErrorManager->registerRenderingError(
+ $contentReleaseIdentifier,
+ [],
+ new Exception($message, 1493387482)
+ );
$this->logCompletion($logger, $startedAt);
exit(1);
} else {
@@ -93,7 +117,10 @@ public function validateCommand(string $contentReleaseIdentifier)
*/
private function logCompletion(ContentReleaseLogger $logger, float $startedAt): void
{
- $logger->info(sprintf('contentReleaseValidation:validate finished in %.2f seconds.', microtime(true) - $startedAt));
+ $logger->info(sprintf(
+ 'contentReleaseValidation:validate finished in %.2f seconds.',
+ microtime(true) - $startedAt
+ ));
}
public function ensureNoValidationErrorsExistCommand(string $contentReleaseIdentifier)
@@ -102,7 +129,11 @@ public function ensureNoValidationErrorsExistCommand(string $contentReleaseIdent
$logger = ContentReleaseLogger::fromConsoleOutput($this->output, $contentReleaseIdentifier);
$errors = $this->redisRenderingErrorManager->getRenderingErrors($contentReleaseIdentifier);
- $logger->info(sprintf('Checking rendering errors of content release %s: %d found.', $contentReleaseIdentifier->getIdentifier(), count($errors)));
+ $logger->info(sprintf(
+ 'Checking rendering errors of content release %s: %d found.',
+ $contentReleaseIdentifier->getIdentifier(),
+ count($errors)
+ ));
foreach ($errors as $error) {
$logger->error('Rendering Error: ' . $error);
diff --git a/Classes/Command/ContentStorePruneCommandController.php b/Classes/Command/ContentStorePruneCommandController.php
index 829c4a2..56b58c0 100644
--- a/Classes/Command/ContentStorePruneCommandController.php
+++ b/Classes/Command/ContentStorePruneCommandController.php
@@ -9,7 +9,6 @@
use Neos\Flow\Cli\CommandController;
use Neos\Flow\Annotations as Flow;
-
class ContentStorePruneCommandController extends CommandController
{
/**
@@ -24,5 +23,4 @@ public function pruneRedisInstanceCommand(string $redisInstanceIdentifier)
$this->redisPruneService->pruneRedisInstance($redisInstanceIdentifier);
}
-
}
diff --git a/Classes/Command/ContentStorePublishCommandController.php b/Classes/Command/ContentStorePublishCommandController.php
index 271aaf4..70867be 100644
--- a/Classes/Command/ContentStorePublishCommandController.php
+++ b/Classes/Command/ContentStorePublishCommandController.php
@@ -1,4 +1,5 @@
contentReleaseManager->cancelAllRunningContentReleases();
$this->contentReleaseManager->startFullContentRelease();
}
-
}
diff --git a/Classes/Command/NodeEnumerationCommandController.php b/Classes/Command/NodeEnumerationCommandController.php
index c7e75ad..9a72be8 100644
--- a/Classes/Command/NodeEnumerationCommandController.php
+++ b/Classes/Command/NodeEnumerationCommandController.php
@@ -29,4 +29,4 @@ public function enumerateAllNodesCommand(string $contentReleaseIdentifier)
// TODO: is the NodeEnumerator called anywhere WITH a site? (in the old version)
$this->nodeEnumerator->enumerateAndStoreInRedis(null, $logger, $contentReleaseIdentifier);
}
-}
\ No newline at end of file
+}
diff --git a/Classes/Command/NodeRenderingCommandController.php b/Classes/Command/NodeRenderingCommandController.php
index d49cea4..f469261 100644
--- a/Classes/Command/NodeRenderingCommandController.php
+++ b/Classes/Command/NodeRenderingCommandController.php
@@ -35,7 +35,10 @@ public function orchestrateRenderingCommand(string $contentReleaseIdentifier)
$contentReleaseIdentifier = ContentReleaseIdentifier::fromString($contentReleaseIdentifier);
$logger = ContentReleaseLogger::fromConsoleOutput($this->output, $contentReleaseIdentifier);
- InterruptibleProcessRuntime::create($this->nodeRenderOrchestrator->renderContentRelease($contentReleaseIdentifier, $logger))->runUntilEnd();
+ InterruptibleProcessRuntime::create($this->nodeRenderOrchestrator->renderContentRelease(
+ $contentReleaseIdentifier,
+ $logger
+ ))->runUntilEnd();
}
public function renderWorkerCommand(string $contentReleaseIdentifier, string $rendererIdentifier)
@@ -44,6 +47,10 @@ public function renderWorkerCommand(string $contentReleaseIdentifier, string $re
$rendererIdentifier = RendererIdentifier::fromString($rendererIdentifier);
$logger = ContentReleaseLogger::fromConsoleOutput($this->output, $contentReleaseIdentifier);
- InterruptibleProcessRuntime::create($this->nodeRenderer->render($contentReleaseIdentifier, $logger, $rendererIdentifier))->runUntilEnd();
+ InterruptibleProcessRuntime::create($this->nodeRenderer->render(
+ $contentReleaseIdentifier,
+ $logger,
+ $rendererIdentifier
+ ))->runUntilEnd();
}
-}
\ No newline at end of file
+}
diff --git a/Classes/ContentReleaseManager.php b/Classes/ContentReleaseManager.php
index 27310f6..f7f2a5f 100644
--- a/Classes/ContentReleaseManager.php
+++ b/Classes/ContentReleaseManager.php
@@ -7,11 +7,11 @@
use Flowpack\DecoupledContentStore\Core\Domain\ValueObject\ContentReleaseIdentifier;
use Flowpack\DecoupledContentStore\Core\Domain\ValueObject\RedisInstanceIdentifier;
use Flowpack\DecoupledContentStore\Core\Infrastructure\RedisClientManager;
-use Neos\ContentRepository\Domain\Model\Workspace;
-use Flowpack\Prunner\ValueObject\JobId;
-use Neos\Flow\Annotations as Flow;
use Flowpack\Prunner\PrunnerApiService;
+use Flowpack\Prunner\ValueObject\JobId;
use Flowpack\Prunner\ValueObject\PipelineName;
+use Neos\ContentRepository\Domain\Model\Workspace;
+use Neos\Flow\Annotations as Flow;
use Neos\Flow\Security\Context;
/**
@@ -19,7 +19,6 @@
*/
class ContentReleaseManager
{
-
/**
* @Flow\Inject
* @var PrunnerApiService
@@ -47,36 +46,49 @@ class ContentReleaseManager
const REDIS_CURRENT_RELEASE_KEY = 'contentStore:current';
const NO_PREVIOUS_RELEASE = 'NO_PREVIOUS_RELEASE';
- public function startIncrementalContentRelease(string $currentContentReleaseId = null, Workspace $workspace = null, array $additionalVariables = []): ContentReleaseIdentifier
- {
+ public function startIncrementalContentRelease(
+ ?string $currentContentReleaseId = null,
+ ?Workspace $workspace = null,
+ array $additionalVariables = []
+ ): ContentReleaseIdentifier {
$contentReleaseId = ContentReleaseIdentifier::create();
// the currentContentReleaseId is not used in any pipeline step in this package, but is a common need in other
// use cases in extensions, e.g. calculating the differences between current and new release
- $this->prunnerApiService->schedulePipeline(PipelineName::create('do_content_release'), array_merge($additionalVariables, [
- 'contentReleaseId' => $contentReleaseId,
- 'currentContentReleaseId' => $this->resolveCurrentContentReleaseId($currentContentReleaseId),
- 'validate' => true,
- 'flushContentCache' => false,
- 'workspaceName' => $workspace !== null ? $workspace->getName() : 'live',
- 'accountId' => $this->getAccountId(),
- ]));
+ $this->prunnerApiService->schedulePipeline(
+ PipelineName::create('do_content_release'),
+ array_merge($additionalVariables, [
+ 'contentReleaseId' => $contentReleaseId,
+ 'currentContentReleaseId' => $this->resolveCurrentContentReleaseId($currentContentReleaseId),
+ 'validate' => true,
+ 'flushContentCache' => false,
+ 'workspaceName' => $workspace !== null ? $workspace->getName() : 'live',
+ 'accountId' => $this->getAccountId()
+ ])
+ );
return $contentReleaseId;
}
// the validate parameter can be used to intentionally skip the validation step for this release
- public function startFullContentRelease(bool $validate = true, string $currentContentReleaseId = null, Workspace $workspace = null, array $additionalVariables = []): ContentReleaseIdentifier
- {
+ public function startFullContentRelease(
+ bool $validate = true,
+ ?string $currentContentReleaseId = null,
+ ?Workspace $workspace = null,
+ array $additionalVariables = []
+ ): ContentReleaseIdentifier {
$contentReleaseId = ContentReleaseIdentifier::create();
- $this->prunnerApiService->schedulePipeline(PipelineName::create('do_content_release'), array_merge($additionalVariables, [
- 'contentReleaseId' => $contentReleaseId,
- 'currentContentReleaseId' => $this->resolveCurrentContentReleaseId($currentContentReleaseId),
- 'validate' => $validate,
- 'flushContentCache' => true,
- 'workspaceName' => $workspace !== null ? $workspace->getName() : 'live',
- 'accountId' => $this->getAccountId(),
- ]));
+ $this->prunnerApiService->schedulePipeline(
+ PipelineName::create('do_content_release'),
+ array_merge($additionalVariables, [
+ 'contentReleaseId' => $contentReleaseId,
+ 'currentContentReleaseId' => $this->resolveCurrentContentReleaseId($currentContentReleaseId),
+ 'validate' => $validate,
+ 'flushContentCache' => true,
+ 'workspaceName' => $workspace !== null ? $workspace->getName() : 'live',
+ 'accountId' => $this->getAccountId()
+ ])
+ );
return $contentReleaseId;
}
diff --git a/Classes/Controller/BackendController.php b/Classes/Controller/BackendController.php
index 6c06a4a..9172773 100644
--- a/Classes/Controller/BackendController.php
+++ b/Classes/Controller/BackendController.php
@@ -1,4 +1,7 @@
redisClientManager->getRedis($contentStore);
$storeSize = $redis->info('memory')['used_memory_human'];
$currentConfigEpoch = $this->configEpochSettings['current'] ?? null;
@@ -112,14 +116,24 @@ public function indexAction(?string $contentStore = null)
$this->view->assign('redisContentStores', array_keys($this->redisContentStores));
$this->view->assign('storeSize', $storeSize);
$this->view->assign('toggleFromConfigEpoch', $configEpochRedis);
- $this->view->assign('toggleToConfigEpoch', $configEpochRedis === $currentConfigEpoch ? $previousConfigEpoch : $currentConfigEpoch);
+ $this->view->assign(
+ 'toggleToConfigEpoch',
+ $configEpochRedis === $currentConfigEpoch ? $previousConfigEpoch : $currentConfigEpoch
+ );
$this->view->assign('showToggleConfigEpochButton', $showToggleConfigEpochButton);
}
- public function detailsAction(string $contentReleaseIdentifier, ?string $contentStore = null, ?string $detailTaskName = '', ?string $prunnerJobId = '', bool $showAllRenderingErrors = false)
- {
+ public function detailsAction(
+ string $contentReleaseIdentifier,
+ ?string $contentStore = null,
+ ?string $detailTaskName = '',
+ ?string $prunnerJobId = '',
+ bool $showAllRenderingErrors = false
+ ) {
$contentReleaseIdentifier = ContentReleaseIdentifier::fromString($contentReleaseIdentifier);
- $contentStore = $contentStore ? RedisInstanceIdentifier::fromString($contentStore) : RedisInstanceIdentifier::primary();
+ $contentStore = $contentStore
+ ? RedisInstanceIdentifier::fromString($contentStore)
+ : RedisInstanceIdentifier::primary();
$this->view->assign('contentReleaseIdentifier', $contentReleaseIdentifier);
$this->view->assign('contentStore', $contentStore->getIdentifier());
@@ -131,13 +145,15 @@ public function detailsAction(string $contentReleaseIdentifier, ?string $content
if ($detailTaskName !== '') {
$this->view->assign('detailTaskName', $detailTaskName);
- $this->view->assign('jobLogs', $this->prunnerApiService->loadJobLogs($prunnerJobId ? PrunnerJobId::fromString($prunnerJobId)->toJobId() : $detailsData->getJob()->getId(), $detailTaskName));
+ $this->view->assign('jobLogs', $this->prunnerApiService->loadJobLogs(
+ $prunnerJobId ? PrunnerJobId::fromString($prunnerJobId)->toJobId() : $detailsData->getJob()->getId(),
+ $detailTaskName
+ ));
} elseif ($showAllRenderingErrors && $detailsData->getJob() !== null) {
$this->view->assign('workerErrorLogs', $this->workerErrorLogAggregator->aggregate($detailsData->getJob()));
}
}
-
public function publishAllAction()
{
$this->contentReleaseManager->cancelAllRunningContentReleases();
@@ -160,12 +176,18 @@ public function removeAction(string $contentReleaseIdentifier, string $redisInst
}
$contentReleaseIdentifierToRemove = ContentReleaseIdentifier::fromString($contentReleaseIdentifier);
- $redisInstanceIdentifier = $redisInstanceIdentifier ? RedisInstanceIdentifier::fromString($redisInstanceIdentifier) : RedisInstanceIdentifier::primary();
+ $redisInstanceIdentifier = $redisInstanceIdentifier
+ ? RedisInstanceIdentifier::fromString($redisInstanceIdentifier)
+ : RedisInstanceIdentifier::primary();
$bufferedOutput = new BufferedOutput();
$logger = ContentReleaseLogger::fromSymfonyOutput($bufferedOutput, $contentReleaseIdentifierToRemove);
- $this->contentReleaseCleaner->removeRelease($contentReleaseIdentifierToRemove, $redisInstanceIdentifier, $logger);
+ $this->contentReleaseCleaner->removeRelease(
+ $contentReleaseIdentifierToRemove,
+ $redisInstanceIdentifier,
+ $logger
+ );
$this->redirect('index', null, null, ['contentStore' => $redisInstanceIdentifier->getIdentifier()]);
}
@@ -178,23 +200,34 @@ public function switchAction(string $contentReleaseIdentifier, string $redisInst
}
$contentReleaseIdentifier = ContentReleaseIdentifier::fromString($contentReleaseIdentifier);
- $redisInstanceIdentifier = $redisInstanceIdentifier ? RedisInstanceIdentifier::fromString($redisInstanceIdentifier) : RedisInstanceIdentifier::primary();
+ $redisInstanceIdentifier = $redisInstanceIdentifier
+ ? RedisInstanceIdentifier::fromString($redisInstanceIdentifier)
+ : RedisInstanceIdentifier::primary();
$bufferedOutput = new BufferedOutput();
$logger = ContentReleaseLogger::fromSymfonyOutput($bufferedOutput, $contentReleaseIdentifier);
- $this->redisReleaseSwitchService->switchContentRelease($redisInstanceIdentifier, $contentReleaseIdentifier, $logger);
+ $this->redisReleaseSwitchService->switchContentRelease(
+ $redisInstanceIdentifier,
+ $contentReleaseIdentifier,
+ $logger
+ );
$this->redirect('index', null, null, ['contentStore' => $redisInstanceIdentifier->getIdentifier()]);
}
- public function switchContentReleaseOnOtherInstanceAction(string $targetRedisInstanceIdentifier, string $contentReleaseIdentifier)
- {
+ public function switchContentReleaseOnOtherInstanceAction(
+ string $targetRedisInstanceIdentifier,
+ string $contentReleaseIdentifier
+ ) {
$redis = $this->redisClientManager->getPrimaryRedis();
$currentContentReleaseId = $redis->get('contentStore:current');
- $this->prunnerApiService->schedulePipeline(PipelineName::create('manually_transfer_content_release'),
- ['contentReleaseId' => $contentReleaseIdentifier, 'currentContentReleaseId' => $currentContentReleaseId ?: ContentReleaseManager::NO_PREVIOUS_RELEASE, 'redisInstanceId' => $targetRedisInstanceIdentifier]);
+ $this->prunnerApiService->schedulePipeline(PipelineName::create('manually_transfer_content_release'), [
+ 'contentReleaseId' => $contentReleaseIdentifier,
+ 'currentContentReleaseId' => $currentContentReleaseId ?: ContentReleaseManager::NO_PREVIOUS_RELEASE,
+ 'redisInstanceId' => $targetRedisInstanceIdentifier
+ ]);
$this->redirect('index', null, null, ['contentStore' => $targetRedisInstanceIdentifier]);
}
diff --git a/Classes/Core/ConcurrentBuildLockService.php b/Classes/Core/ConcurrentBuildLockService.php
index 0d91187..7b86c90 100644
--- a/Classes/Core/ConcurrentBuildLockService.php
+++ b/Classes/Core/ConcurrentBuildLockService.php
@@ -1,5 +1,7 @@
redisContentReleaseService->fetchMetadataForContentRelease($contentReleaseIdentifier);
- $this->redisClientManager->getPrimaryRedis()->hSet(self::CONTENT_STORE_CONCURRENT_BUILD_LOCK, $metadata->getWorkspaceName(), (string)$contentReleaseIdentifier);
+ $this->redisClientManager->getPrimaryRedis()->hSet(
+ self::CONTENT_STORE_CONCURRENT_BUILD_LOCK,
+ $metadata->getWorkspaceName(),
+ (string) $contentReleaseIdentifier
+ );
}
public function assertNoOtherContentReleaseWasStarted(ContentReleaseIdentifier $contentReleaseIdentifier): void
{
$metadata = $this->redisContentReleaseService->fetchMetadataForContentRelease($contentReleaseIdentifier);
- $concurrentBuildLockStrings = $this->redisClientManager->getPrimaryRedis()->hGetAll(self::CONTENT_STORE_CONCURRENT_BUILD_LOCK);
+ $concurrentBuildLockStrings = $this->redisClientManager
+ ->getPrimaryRedis()
+ ->hGetAll(self::CONTENT_STORE_CONCURRENT_BUILD_LOCK);
$concurrentBuildLockStringForWorkspace = $concurrentBuildLockStrings[$metadata->getWorkspaceName()] ?? null;
if (!$concurrentBuildLockStringForWorkspace) {
- echo '!!!!! Hard-aborting the current job ' . $contentReleaseIdentifier->getIdentifier() . ' because the concurrentBuildLock does not exist.' . "\n\n";
+ echo
+ '!!!!! Hard-aborting the current job '
+ . $contentReleaseIdentifier->getIdentifier()
+ . ' because the concurrentBuildLock does not exist.'
+ . "\n\n"
+ ;
echo "This should never happen for correctly configured jobs (that run after prepare_finished).\n\n";
exit(1);
}
@@ -64,10 +77,19 @@ public function assertNoOtherContentReleaseWasStarted(ContentReleaseIdentifier $
if (!$contentReleaseIdentifier->equals($concurrentBuildLock)) {
// the concurrent build lock is different (i.e. newer) than our currently-running content release.
// Thus, we abort the in-progress content release as quickly as we can - by DYING.
- echo '!!!!! Hard-aborting the current job ' . $contentReleaseIdentifier->getIdentifier() . ' because the concurrentBuildLock for workspace "' . $metadata->getWorkspaceName() . '" contains ' . $concurrentBuildLock->getIdentifier() . "\n\n";
- echo "This is no error during deployment, but should never happen outside a deployment.\n\n It can only happen if two prunner instances run concurrently.\n\n";
+ echo
+ '!!!!! Hard-aborting the current job '
+ . $contentReleaseIdentifier->getIdentifier()
+ . ' because the concurrentBuildLock for workspace "'
+ . $metadata->getWorkspaceName()
+ . '" contains '
+ . $concurrentBuildLock->getIdentifier()
+ . "\n\n"
+ ;
+ echo
+ "This is no error during deployment, but should never happen outside a deployment.\n\n It can only happen if two prunner instances run concurrently.\n\n"
+ ;
exit(1);
}
}
-
}
diff --git a/Classes/Core/Domain/Dto/ContentReleaseBatchResult.php b/Classes/Core/Domain/Dto/ContentReleaseBatchResult.php
index 673f654..ff81269 100644
--- a/Classes/Core/Domain/Dto/ContentReleaseBatchResult.php
+++ b/Classes/Core/Domain/Dto/ContentReleaseBatchResult.php
@@ -1,9 +1,10 @@
results[(string)$contentReleaseIdentifier] ?? null;
+ return $this->results[(string) $contentReleaseIdentifier] ?? null;
}
-
}
diff --git a/Classes/Core/Domain/ValueObject/ContentReleaseIdentifier.php b/Classes/Core/Domain/ValueObject/ContentReleaseIdentifier.php
index 02e6ded..9d25880 100644
--- a/Classes/Core/Domain/ValueObject/ContentReleaseIdentifier.php
+++ b/Classes/Core/Domain/ValueObject/ContentReleaseIdentifier.php
@@ -1,5 +1,7 @@
identifier);
}
-}
\ No newline at end of file
+}
diff --git a/Classes/Core/Domain/ValueObject/RedisInstanceIdentifier.php b/Classes/Core/Domain/ValueObject/RedisInstanceIdentifier.php
index 0b551f3..92352bd 100644
--- a/Classes/Core/Domain/ValueObject/RedisInstanceIdentifier.php
+++ b/Classes/Core/Domain/ValueObject/RedisInstanceIdentifier.php
@@ -1,5 +1,7 @@
identifier;
}
-}
\ No newline at end of file
+}
diff --git a/Classes/Core/Infrastructure/ConsoleStatisticsEventOutput.php b/Classes/Core/Infrastructure/ConsoleStatisticsEventOutput.php
index 670332c..02d336e 100644
--- a/Classes/Core/Infrastructure/ConsoleStatisticsEventOutput.php
+++ b/Classes/Core/Infrastructure/ConsoleStatisticsEventOutput.php
@@ -1,4 +1,5 @@
output->writeln($prefix . 'STATISTICS EVENT ' . $event . ($additionalPayload ? ' ' . json_encode($additionalPayload) : ''));
+ public function writeEvent(
+ ContentReleaseIdentifier $contentReleaseIdentifier,
+ string $prefix,
+ string $event,
+ array $additionalPayload
+ ): void {
+ $this->output->writeln(
+ $prefix . 'STATISTICS EVENT ' . $event . ( $additionalPayload ? ' ' . json_encode($additionalPayload) : '' )
+ );
}
}
diff --git a/Classes/Core/Infrastructure/ContentReleaseLogger.php b/Classes/Core/Infrastructure/ContentReleaseLogger.php
index 164bdc5..f1f9a66 100644
--- a/Classes/Core/Infrastructure/ContentReleaseLogger.php
+++ b/Classes/Core/Infrastructure/ContentReleaseLogger.php
@@ -1,5 +1,7 @@
output = $output;
$this->contentReleaseIdentifier = $contentReleaseIdentifier;
$this->statisticsEventOutput = $statisticsEventOutput;
@@ -41,14 +47,19 @@ protected function __construct(OutputInterface $output, ContentReleaseIdentifier
}
}
-
- public static function fromConsoleOutput(ConsoleOutput $output, ContentReleaseIdentifier $contentReleaseIdentifier, StatisticsEventOutputInterface $statisticsEventOutput = new RedisStatisticsEventOutput()): self
- {
+ public static function fromConsoleOutput(
+ ConsoleOutput $output,
+ ContentReleaseIdentifier $contentReleaseIdentifier,
+ StatisticsEventOutputInterface $statisticsEventOutput = new RedisStatisticsEventOutput()
+ ): self {
return new static($output->getOutput(), $contentReleaseIdentifier, $statisticsEventOutput, null);
}
- public static function fromSymfonyOutput(OutputInterface $output, ContentReleaseIdentifier $contentReleaseIdentifier, StatisticsEventOutputInterface $statisticsEventOutput = new RedisStatisticsEventOutput()): self
- {
+ public static function fromSymfonyOutput(
+ OutputInterface $output,
+ ContentReleaseIdentifier $contentReleaseIdentifier,
+ StatisticsEventOutputInterface $statisticsEventOutput = new RedisStatisticsEventOutput()
+ ): self {
return new static($output, $contentReleaseIdentifier, $statisticsEventOutput, null);
}
@@ -80,7 +91,17 @@ protected function logToOutput(string $level, string $message, array $additional
public function logException(\Exception $exception, string $message, array $additionalPayload)
{
- $this->output->writeln($this->timePrefix() . $this->logPrefix . $message . "\n\n" . $exception->getMessage() . "\n\n" . $exception->getTraceAsString() . "\n\n" . json_encode($additionalPayload));
+ $this->output->writeln(
+ $this->timePrefix()
+ . $this->logPrefix
+ . $message
+ . "\n\n"
+ . $exception->getMessage()
+ . "\n\n"
+ . $exception->getTraceAsString()
+ . "\n\n"
+ . json_encode($additionalPayload)
+ );
}
/**
@@ -94,11 +115,21 @@ protected function timePrefix(): string
public function logStatisticsEvent(string $event, array $additionalPayload = [])
{
- $this->statisticsEventOutput->writeEvent($this->contentReleaseIdentifier, $this->logPrefix, $event, $additionalPayload);
+ $this->statisticsEventOutput->writeEvent(
+ $this->contentReleaseIdentifier,
+ $this->logPrefix,
+ $event,
+ $additionalPayload
+ );
}
public function withRenderer(RendererIdentifier $rendererIdentifier): self
{
- return new ContentReleaseLogger($this->output, $this->contentReleaseIdentifier, $this->statisticsEventOutput, $rendererIdentifier);
+ return new ContentReleaseLogger(
+ $this->output,
+ $this->contentReleaseIdentifier,
+ $this->statisticsEventOutput,
+ $rendererIdentifier
+ );
}
}
diff --git a/Classes/Core/Infrastructure/RedisClientManager.php b/Classes/Core/Infrastructure/RedisClientManager.php
index 6b8d1a5..3c7822b 100644
--- a/Classes/Core/Infrastructure/RedisClientManager.php
+++ b/Classes/Core/Infrastructure/RedisClientManager.php
@@ -1,5 +1,7 @@
configuration[$redisInstanceIdentifier->getIdentifier()];
$redis = new \Redis();
- $connected = false;
try {
- $connected = $redis->connect($instanceConfig['hostname'], $instanceConfig['port'] ?? 6379, $instanceConfig['timeout'] ?? 0) && $redis->select($instanceConfig['database'] ?? 0);
- } catch (\Exception $e) {
- throw new Exception(sprintf('Could not connect to Redis server %s:%d. Detailed reason: see nested exception.', $instanceConfig['hostname'], $instanceConfig['port']), 1630323312, $e);
+ $connected =
+ $redis->connect(
+ $instanceConfig['hostname'],
+ (int) ( $instanceConfig['port'] ?? 6379 ),
+ $instanceConfig['timeout'] ?? 0
+ ) && $redis->select($instanceConfig['database'] ?? 0);
+ } catch (\Exception $exception) {
+ throw new Exception(
+ sprintf(
+ 'Could not connect to Redis server %s:%d. Detailed reason: see nested exception.',
+ $instanceConfig['hostname'],
+ $instanceConfig['port']
+ ),
+ 1630323312,
+ $exception
+ );
}
if (!$connected) {
- throw new Exception(sprintf('Could not connect to Redis server %s:%d', $instanceConfig['hostname'], $instanceConfig['port']), 1467385687);
+ throw new Exception(
+ sprintf(
+ 'Could not connect to Redis server %s:%d',
+ $instanceConfig['hostname'],
+ $instanceConfig['port']
+ ),
+ 1467385687
+ );
}
return $redis;
@@ -57,10 +77,14 @@ public function getRedis(RedisInstanceIdentifier $redisInstanceIdentifier): \Red
try {
$pong = $redis->ping();
if ($pong === false) {
- $redis = $this->redisInstances[$redisInstanceIdentifier->getIdentifier()] = $this->connect($redisInstanceIdentifier);
+ $redis =
+ $this->redisInstances[$redisInstanceIdentifier->getIdentifier()] =
+ $this->connect($redisInstanceIdentifier);
}
} catch (\RedisException $e) {
- $redis = $this->redisInstances[$redisInstanceIdentifier->getIdentifier()] = $this->connect($redisInstanceIdentifier);
+ $redis =
+ $this->redisInstances[$redisInstanceIdentifier->getIdentifier()] =
+ $this->connect($redisInstanceIdentifier);
}
return $redis;
}
@@ -73,7 +97,10 @@ public function getPrimaryRedis(): \Redis
public function getRetentionCount(RedisInstanceIdentifier $redisInstanceIdentifier): int
{
if (!isset($this->configuration[$redisInstanceIdentifier->getIdentifier()]['contentReleaseRetentionCount'])) {
- throw new \RuntimeException('Did not find a configured contentReleaseRetentionCount for Redis ' . $redisInstanceIdentifier->getIdentifier());
+ throw new \RuntimeException(
+ 'Did not find a configured contentReleaseRetentionCount for Redis '
+ . $redisInstanceIdentifier->getIdentifier()
+ );
}
return $this->configuration[$redisInstanceIdentifier->getIdentifier()]['contentReleaseRetentionCount'];
}
diff --git a/Classes/Core/Infrastructure/RedisContentReleaseSizeService.php b/Classes/Core/Infrastructure/RedisContentReleaseSizeService.php
index 3d37cab..216dcde 100644
--- a/Classes/Core/Infrastructure/RedisContentReleaseSizeService.php
+++ b/Classes/Core/Infrastructure/RedisContentReleaseSizeService.php
@@ -28,8 +28,10 @@ class RedisContentReleaseSizeService
/**
* @return float size of the content release in megabytes
*/
- public function calculateReleaseSize(RedisInstanceIdentifier $redisInstanceIdentifier, ContentReleaseIdentifier $contentReleaseIdentifier): float
- {
+ public function calculateReleaseSize(
+ RedisInstanceIdentifier $redisInstanceIdentifier,
+ ContentReleaseIdentifier $contentReleaseIdentifier
+ ): float {
$redis = $this->redisClientManager->getRedis($redisInstanceIdentifier);
$allKeys = $redis->keys('contentStore:' . $contentReleaseIdentifier->getIdentifier() . ':*');
$size = 0;
diff --git a/Classes/Core/Infrastructure/RedisStatisticsEventOutput.php b/Classes/Core/Infrastructure/RedisStatisticsEventOutput.php
index ce8f2a0..7a0200d 100644
--- a/Classes/Core/Infrastructure/RedisStatisticsEventOutput.php
+++ b/Classes/Core/Infrastructure/RedisStatisticsEventOutput.php
@@ -1,4 +1,5 @@
redisStatisticsEventService->addEvent($contentReleaseIdentifier, $prefix, $event, $additionalPayload);
}
}
diff --git a/Classes/Core/Infrastructure/RedisStatisticsEventService.php b/Classes/Core/Infrastructure/RedisStatisticsEventService.php
index 23e7ec8..0e8557b 100644
--- a/Classes/Core/Infrastructure/RedisStatisticsEventService.php
+++ b/Classes/Core/Infrastructure/RedisStatisticsEventService.php
@@ -1,4 +1,5 @@
redisClientManager->getPrimaryRedis()->rPush($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'statisticsEvents'), json_encode([
- 'event' => $event,
- 'prefix' => $prefix,
- 'additionalPayload' => $additionalPayload,
- ]));
+ public function addEvent(
+ ContentReleaseIdentifier $contentReleaseIdentifier,
+ string $prefix,
+ string $event,
+ array $additionalPayload
+ ): void {
+ $this->redisClientManager->getPrimaryRedis()->rPush(
+ $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'statisticsEvents'),
+ json_encode([
+ 'event' => $event,
+ 'prefix' => $prefix,
+ 'additionalPayload' => $additionalPayload
+ ])
+ );
}
/**
@@ -32,13 +40,13 @@ public function addEvent(ContentReleaseIdentifier $contentReleaseIdentifier, str
* @param string[] $groupBy
* @return array<>
* @throws Exception
+ * @throws \JsonException
*/
public function countEvents(
ContentReleaseIdentifier $contentReleaseIdentifier,
- array $where,
- array $groupBy,
- ): array
- {
+ array $where,
+ array $groupBy
+ ): array {
$redis = $this->redisClientManager->getPrimaryRedis();
$key = $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'statisticsEvents');
$chunkSize = 1000;
@@ -51,9 +59,9 @@ public function countEvents(
foreach ($events as $eventJson) {
$event = $this->flatten(json_decode($eventJson, true));
- if($this->shouldCount($event, $where)) {
+ if ($this->shouldCount($event, $where)) {
$group = $this->groupValues($event, $groupBy);
- $eventKey = json_encode($group);
+ $eventKey = json_encode($group, JSON_THROW_ON_ERROR);
if (array_key_exists($eventKey, $countedEvents)) {
$countedEvents[$eventKey]['count'] += 1;
} else {
@@ -78,7 +86,7 @@ private function flatten(array $array): array
$results = [];
foreach ($array as $key => $value) {
- if (is_array($value) && ! empty($value)) {
+ if (is_array($value) && !empty($value)) {
foreach ($this->flatten($value) as $subKey => $subValue) {
$results[$key . '.' . $subKey] = $subValue;
}
@@ -97,7 +105,7 @@ private function flatten(array $array): array
*/
private function shouldCount(array $event, array $where): bool
{
- foreach ($where as $key=>$value) {
+ foreach ($where as $key => $value) {
if (!array_key_exists($key, $event) || $event[$key] !== $value) {
return false;
}
diff --git a/Classes/Core/Infrastructure/StatisticsEventOutputInterface.php b/Classes/Core/Infrastructure/StatisticsEventOutputInterface.php
index fb0cd6b..0a193d5 100644
--- a/Classes/Core/Infrastructure/StatisticsEventOutputInterface.php
+++ b/Classes/Core/Infrastructure/StatisticsEventOutputInterface.php
@@ -1,4 +1,5 @@
redisClientManager->getRedis($redisInstanceIdentifier)->eval(self::PRUNE_LUA_SCRIPT, []);
}
-
}
diff --git a/Classes/Eel/ModuleHelper.php b/Classes/Eel/ModuleHelper.php
index c808ce1..4d992f4 100644
--- a/Classes/Eel/ModuleHelper.php
+++ b/Classes/Eel/ModuleHelper.php
@@ -45,36 +45,51 @@ public function formatStdOutput(?string $stdOut): string
$lines = array_reverse(array_filter(preg_split("/\r\n|\n|\r/", $escapedString)));
$lineCount = count($lines);
- $formattedLines = array_map(static function (string $line, int $index) use ($lineCount) {
- // Extract additional JSON data
- preg_match("/\{.*}/", $line, $jsonMatches);
- $jsonData = array_filter(array_map(static function (string $match) {
- $matchData = json_decode($match, true);
- return $matchData['message'] ?? $matchData;
- }, $jsonMatches));
- if (count($jsonData) === 1) {
- $jsonData = array_shift($jsonData);
- }
-
- // Remove additional JSON data from log line
- $line = preg_replace("/\{.*}/", '', $line);
-
- // Add highlighting for log levels
- $line = preg_replace("/(DEBUG|WARNING|ERROR|INFO): (.*)/", "$1: $2", htmlSpecialChars($line));
-
- // Add line numbers. The lines are reversed (newest first), so the numbering counts down to 1.
- $line = ($lineCount - $index + 1) . ': ' . $line;
-
- // Insert formatted JSON data
- if ($jsonData) {
- $isDebug = strpos($line, 'DEBUG:') !== false;
- $jsonString = "\n" . json_encode($jsonData, JSON_PRETTY_PRINT) . "";
- $line = $isDebug ? '' . $line . '
' . $jsonString . '
tags - return '' . $line . ''; - }, $lines, range(1, $lineCount)); + $formattedLines = array_map( + static function (string $line, int $index) use ($lineCount) { + // Extract additional JSON data + preg_match("/\{.*}/", $line, $jsonMatches); + $jsonData = array_filter(array_map(static function (string $match) { + $matchData = json_decode($match, true); + return $matchData['message'] ?? $matchData; + }, $jsonMatches)); + if (count($jsonData) === 1) { + $jsonData = array_shift($jsonData); + } + + // Remove additional JSON data from log line + $line = preg_replace("/\{.*}/", '', $line); + + // Add highlighting for log levels + $line = preg_replace( + '/(DEBUG|WARNING|ERROR|INFO): (.*)/', + '$1: $2', + htmlSpecialChars($line) + ); + + // Add line numbers. The lines are reversed (newest first), so the numbering counts down to 1. + $line = ( $lineCount - $index + 1 ) . ': ' . $line; + + // Insert formatted JSON data + if ($jsonData) { + $isDebug = strpos($line, 'DEBUG:') !== false; + $jsonString = "\n" . json_encode($jsonData, JSON_PRETTY_PRINT) . ''; + $line = $isDebug + ? '' + . '' + : $line . $jsonString; + } + + // Wrap in' + . $line + . '
' + . $jsonString + . 'tags + return '' . $line . ''; + }, + $lines, + range(1, $lineCount) + ); return implode("\n", $formattedLines); } diff --git a/Classes/Exception.php b/Classes/Exception.php index c605895..d0b588f 100644 --- a/Classes/Exception.php +++ b/Classes/Exception.php @@ -1,7 +1,9 @@ node = $node; + public function __construct( + string $message, + NodeInterface $node, + $nodeUri, + int $code = 0, + ?Exception $previous = null + ) { $this->nodeUri = $nodeUri; parent::__construct($message, $code, $previous); } - /** - * @return string - */ - public function getNodeUri() + public function getNodeUri(): string { return $this->nodeUri; } -} \ No newline at end of file +} diff --git a/Classes/Fusion/ExceptionHandlers/PublishingAwareContextDependentHandler.php b/Classes/Fusion/ExceptionHandlers/PublishingAwareContextDependentHandler.php index 8df1e1b..ef43d34 100644 --- a/Classes/Fusion/ExceptionHandlers/PublishingAwareContextDependentHandler.php +++ b/Classes/Fusion/ExceptionHandlers/PublishingAwareContextDependentHandler.php @@ -1,11 +1,14 @@ contextPath = $contextPath; $this->nodeIdentifier = $nodeIdentifier; $this->nodeTypeName = $nodeTypeName; @@ -56,18 +62,30 @@ private function __construct(string $contextPath, string $nodeIdentifier, string $this->rendererId = $rendererId; } - static public function fromNode(NodeInterface $node, array $arguments = []): self + public static function fromNode(NodeInterface $node, array $arguments = []): self { - return new self($node->getContextPath(), $node->getIdentifier(), $node->getNodeType()->getName(), $arguments, ''); + return new self( + $node->getContextPath(), + $node->getIdentifier(), + $node->getNodeType()->getName(), + $arguments, + '' + ); } - static public function fromJsonString(string $enumeratedNodeString): self + public static function fromJsonString(string $enumeratedNodeString): self { $tmp = json_decode($enumeratedNodeString, true); if (!is_array($tmp)) { throw new \Exception('EnumeratedNode cannot be constructed from: ' . $enumeratedNodeString); } - return new self($tmp['contextPath'], $tmp['nodeIdentifier'], $tmp['nodeTypeName'] ?? '', $tmp['arguments'], $tmp['rendererId']); + return new self( + $tmp['contextPath'], + $tmp['nodeIdentifier'], + $tmp['nodeTypeName'] ?? '', + $tmp['arguments'], + $tmp['rendererId'] + ); } public function jsonSerialize(): array @@ -77,7 +95,7 @@ public function jsonSerialize(): array 'nodeIdentifier' => $this->nodeIdentifier, 'nodeTypeName' => $this->nodeTypeName, 'arguments' => $this->arguments, - 'rendererId' => $this->rendererId, + 'rendererId' => $this->rendererId ]; } @@ -86,7 +104,10 @@ public function getSiteNodeNameFromContextPath(): string if (preg_match('#^/sites/([^/@]*)#', $this->contextPath, $matches)) { return $matches[1]; } else { - throw new \Exception('Could not get site node name from context path "' . $this->contextPath . '"', 1495535171); + throw new \Exception( + 'Could not get site node name from context path "' . $this->contextPath . '"', + 1495535171 + ); } } @@ -119,7 +140,14 @@ public function getArguments(): array public function debugString(): string { - return sprintf('%s %s %s(%s) - %s', $this->nodeTypeName, $this->nodeIdentifier, $this->arguments ? http_build_query($this->arguments) . ' ' : '', $this->contextPath, $this->rendererId); + return sprintf( + '%s %s %s(%s) - %s', + $this->nodeTypeName, + $this->nodeIdentifier, + $this->arguments ? http_build_query($this->arguments) . ' ' : '', + $this->contextPath, + $this->rendererId + ); } public function withRendererId(string $rendererId): self diff --git a/Classes/NodeEnumeration/Domain/Repository/RedisEnumerationRepository.php b/Classes/NodeEnumeration/Domain/Repository/RedisEnumerationRepository.php index 13d3826..2addd4c 100644 --- a/Classes/NodeEnumeration/Domain/Repository/RedisEnumerationRepository.php +++ b/Classes/NodeEnumeration/Domain/Repository/RedisEnumerationRepository.php @@ -1,22 +1,23 @@ redisClientManager->getPrimaryRedis()->del($this->redisKeyService->getRedisKeyForPostfix($releaseIdentifier, 'enumeration:documentNodes')); + $this->redisClientManager->getPrimaryRedis()->del($this->redisKeyService->getRedisKeyForPostfix( + $releaseIdentifier, + 'enumeration:documentNodes' + )); } - public function addDocumentNodesToEnumeration(ContentReleaseIdentifier $releaseIdentifier, EnumeratedNode ...$enumeration) - { + public function addDocumentNodesToEnumeration( + ContentReleaseIdentifier $releaseIdentifier, + EnumeratedNode ...$enumeration + ) { $convertedEnumeration = array_map(function (EnumeratedNode $node) { return json_encode($node); }, $enumeration); - $this->redisClientManager->getPrimaryRedis()->rPush($this->redisKeyService->getRedisKeyForPostfix($releaseIdentifier, 'enumeration:documentNodes'), ...$convertedEnumeration); + $this->redisClientManager->getPrimaryRedis()->rPush( + $this->redisKeyService->getRedisKeyForPostfix($releaseIdentifier, 'enumeration:documentNodes'), + ...$convertedEnumeration + ); } /** @@ -47,7 +56,11 @@ public function addDocumentNodesToEnumeration(ContentReleaseIdentifier $releaseI */ public function findAll(ContentReleaseIdentifier $releaseIdentifier): iterable { - foreach ($this->redisClientManager->getPrimaryRedis()->lRange($this->redisKeyService->getRedisKeyForPostfix($releaseIdentifier, 'enumeration:documentNodes'), 0, -1) as $enumeratedNodeString) { + foreach ($this->redisClientManager->getPrimaryRedis()->lRange( + $this->redisKeyService->getRedisKeyForPostfix($releaseIdentifier, 'enumeration:documentNodes'), + 0, + -1 + ) as $enumeratedNodeString) { yield EnumeratedNode::fromJsonString($enumeratedNodeString); } } @@ -55,21 +68,29 @@ public function findAll(ContentReleaseIdentifier $releaseIdentifier): iterable public function count(ContentReleaseIdentifier $releaseIdentifier): int { $redis = $this->redisClientManager->getPrimaryRedis(); - $res = $redis->lLen($this->redisKeyService->getRedisKeyForPostfix($releaseIdentifier, 'enumeration:documentNodes')); + $res = $redis->lLen($this->redisKeyService->getRedisKeyForPostfix( + $releaseIdentifier, + 'enumeration:documentNodes' + )); if (is_int($res)) { return $res; } return 0; } - public function countMultiple(RedisInstanceIdentifier $redisInstanceIdentifier, ContentReleaseIdentifier ...$releaseIdentifiers): ContentReleaseBatchResult - { + public function countMultiple( + RedisInstanceIdentifier $redisInstanceIdentifier, + ContentReleaseIdentifier ...$releaseIdentifiers + ): ContentReleaseBatchResult { $result = []; // KEY == contentReleaseIdentifier. VALUE == enumerated count $redis = $this->redisClientManager->getRedis($redisInstanceIdentifier); foreach (GeneratorUtility::createArrayBatch($releaseIdentifiers, 50) as $batchedReleaseIdentifiers) { $redisPipeline = $redis->pipeline(); foreach ($batchedReleaseIdentifiers as $releaseIdentifier) { - $redisPipeline->lLen($this->redisKeyService->getRedisKeyForPostfix($releaseIdentifier, 'enumeration:documentNodes')); + $redisPipeline->lLen($this->redisKeyService->getRedisKeyForPostfix( + $releaseIdentifier, + 'enumeration:documentNodes' + )); } $res = $redisPipeline->exec(); foreach ($batchedReleaseIdentifiers as $i => $releaseIdentifier) { diff --git a/Classes/NodeEnumeration/Domain/Service/NodeContextCombinator.php b/Classes/NodeEnumeration/Domain/Service/NodeContextCombinator.php index 6f78ca6..1228b51 100644 --- a/Classes/NodeEnumeration/Domain/Service/NodeContextCombinator.php +++ b/Classes/NodeEnumeration/Domain/Service/NodeContextCombinator.php @@ -1,4 +1,5 @@ $workspaceName, 'dimensions' => $dimensionContextCombination, 'targetDimensions' => [], - 'invisibleContentShown' => $this->recurseHiddenContent, + 'invisibleContentShown' => $this->recurseHiddenContent )); $siteNode = $contentContext->getNode('/sites/' . $site->getNodeName()); @@ -117,5 +119,4 @@ public function recurseDocumentChildNodes(NodeInterface $node): \Generator yield from $this->recurseDocumentChildNodes($childNode); } } - } diff --git a/Classes/NodeEnumeration/NodeEnumerator.php b/Classes/NodeEnumeration/NodeEnumerator.php index c2d17af..3d0db8c 100644 --- a/Classes/NodeEnumeration/NodeEnumerator.php +++ b/Classes/NodeEnumeration/NodeEnumerator.php @@ -1,7 +1,8 @@ info( - 'Starting content release', - ['contentReleaseIdentifier' => $releaseIdentifier->jsonSerialize()] - ); + $contentReleaseLogger->info('Starting content release', [ + 'contentReleaseIdentifier' => $releaseIdentifier->jsonSerialize() + ]); // set content release status to running $currentMetadata = $this->redisContentReleaseService->fetchMetadataForContentRelease($releaseIdentifier); @@ -73,12 +77,10 @@ public function enumerateAndStoreInRedis( ); $this->redisEnumerationRepository->clearDocumentNodesEnumeration($releaseIdentifier); - foreach ( - GeneratorUtility::createArrayBatch( - $this->enumerateAll($site, $contentReleaseLogger, $newMetadata->getWorkspaceName()), - 100 - ) as $enumeration - ) { + foreach (GeneratorUtility::createArrayBatch( + $this->enumerateAll($site, $contentReleaseLogger, $newMetadata->getWorkspaceName()), + 100 + ) as $enumeration) { $this->concurrentBuildLockService->assertNoOtherContentReleaseWasStarted($releaseIdentifier); // $enumeration is an array of EnumeratedNode, with at most 100 elements in it. @@ -100,6 +102,9 @@ public function enumerateAndStoreInRedis( * "[!instanceof ...]" makes find() throw "find() needs an identifier, path or * instanceof filter for the first filter part" (exception 1436884196). For the same * reason, the positive "[instanceof ...]" filters are put first. + * + * If the whitelist configures exclusions only, the default node type is used as the + * positive filter - otherwise find() would run into the very same exception. */ private static function buildNodeTypeFilter(array $nodeTypeWhitelist): string { @@ -116,6 +121,9 @@ private static function buildNodeTypeFilter(array $nodeTypeWhitelist): string } $includes[] = '[instanceof ' . $nodeType . ']'; } + if ($includes === []) { + $includes[] = '[instanceof ' . self::DEFAULT_NODE_TYPE . ']'; + } return implode('', array_merge($includes, $excludes)); } @@ -130,14 +138,10 @@ private function enumerateAll( ): iterable { $combinator = new NodeContextCombinator(); - $nodeTypeFilter = self::buildNodeTypeFilter($this->nodeTypeWhitelist ?: ['Neos.Neos:Document']); + // an empty whitelist falls back to the default node type in buildNodeTypeFilter() + $nodeTypeFilter = self::buildNodeTypeFilter($this->nodeTypeWhitelist); - $queueSite = function (Site $site) use ( - $combinator, - $nodeTypeFilter, - $contentReleaseLogger, - $workspaceName - ) { + $queueSite = function (Site $site) use ($combinator, $nodeTypeFilter, $contentReleaseLogger, $workspaceName) { $contentReleaseLogger->debug('Publishing site', [ 'name' => $site->getName(), 'domain' => $site->getFirstActiveDomain() @@ -165,7 +169,7 @@ private function enumerateAll( while ($parentNode !== $siteNode) { if ($parentNode === null) { $contentReleaseLogger->debug('Skipping node from publishing, because it is orphaned', [ - 'node' => $contextPath, + 'node' => $contextPath ]); // Continue with the next document continue 2; @@ -176,26 +180,26 @@ private function enumerateAll( if ($nodeToEnumerate->isHidden()) { $contentReleaseLogger->debug('Skipping node from publishing, because it is hidden', [ - 'node' => $contextPath, + 'node' => $contextPath ]); } else { $contentReleaseLogger->debug('Registering node for publishing', [ 'node' => $contextPath ]); - foreach ( - $this->nodeRenderingExtensionManager->enumerateDocumentNode( - $nodeToEnumerate - ) as $enumeratedNode - ) { + foreach ($this->nodeRenderingExtensionManager->enumerateDocumentNode( + $nodeToEnumerate + ) as $enumeratedNode) { yield $enumeratedNode; } } } } - $contentReleaseLogger->debug( - sprintf('Finished enumerating site %s in %dms', $site->getName(), (microtime(true) - $startTime) * 1000) - ); + $contentReleaseLogger->debug(sprintf( + 'Finished enumerating site %s in %dms', + $site->getName(), + ( microtime(true) - $startTime ) * 1000 + )); }; if ($site === null) { @@ -226,5 +230,4 @@ protected function emitNodeEnumerated( ContentReleaseLogger $contentReleaseLogger ) { } - } diff --git a/Classes/NodeRendering/Dto/DocumentNodeCacheKey.php b/Classes/NodeRendering/Dto/DocumentNodeCacheKey.php index 2727b71..c5dfad2 100644 --- a/Classes/NodeRendering/Dto/DocumentNodeCacheKey.php +++ b/Classes/NodeRendering/Dto/DocumentNodeCacheKey.php @@ -47,21 +47,39 @@ private function __construct(string $nodeIdentifier, array $dimensions, string $ $this->arguments = $arguments; } - public static function fromNodeAndArguments(NodeInterface $node, array $arguments): self { - return new self($node->getIdentifier(), $node->getContext()->getDimensions(), $node->getWorkspace()->getName(), $arguments); + return new self( + $node->getIdentifier(), + $node->getContext()->getDimensions(), + $node->getWorkspace()->getName(), + $arguments + ); } public static function fromEnumeratedNode(EnumeratedNode $enumeratedNode) { - return new self($enumeratedNode->getNodeIdentifier(), $enumeratedNode->getDimensionsFromContextPath(), $enumeratedNode->getWorkspaceNameFromContextPath(), $enumeratedNode->getArguments()); + return new self( + $enumeratedNode->getNodeIdentifier(), + $enumeratedNode->getDimensionsFromContextPath(), + $enumeratedNode->getWorkspaceNameFromContextPath(), + $enumeratedNode->getArguments() + ); } public function redisKeyName(): string { // TODO: Add workspace name to cache entry to allow parallel releases, but `CacheUrlMappingAspect` has to provide node in correct workspace during rendering - return preg_replace('/[^a-zA-Z0-9-]/', '_', sprintf('doc--%s-%s-%s', $this->nodeIdentifier, json_encode($this->dimensions), json_encode($this->arguments))); + return preg_replace( + '/[^a-zA-Z0-9-]/', + '_', + sprintf( + 'doc--%s-%s-%s', + $this->nodeIdentifier, + json_encode($this->dimensions), + json_encode($this->arguments) + ) + ); } public function fullyQualifiedRedisKeyName(string $identifierPrefix): string diff --git a/Classes/NodeRendering/Dto/DocumentNodeCacheValues.php b/Classes/NodeRendering/Dto/DocumentNodeCacheValues.php index 82e1239..3a8a333 100644 --- a/Classes/NodeRendering/Dto/DocumentNodeCacheValues.php +++ b/Classes/NodeRendering/Dto/DocumentNodeCacheValues.php @@ -1,8 +1,10 @@ rootIdentifier; } - /** - * @return string - */ public function getUrl(): string { return $this->url; } - /** - * @return array - */ public function getMetadata(): array { return $this->metadata; } - - public function jsonSerialize() + public function jsonSerialize(): array { return ['rootIdentifier' => $this->rootIdentifier, 'url' => $this->url, 'metadata' => $this->metadata]; } /** - * add additional metadata - * - * @param string $key - * @param $value - * @return $this + * Add additional metadata. */ public function withMetadata(string $key, $value): self { @@ -100,6 +90,4 @@ public function withMetadata(string $key, $value): self $metadata[$key] = $value; return new self($this->rootIdentifier, $this->url, $metadata); } - - -} \ No newline at end of file +} diff --git a/Classes/NodeRendering/Dto/NodeRenderingCompletionStatus.php b/Classes/NodeRendering/Dto/NodeRenderingCompletionStatus.php index 749c18c..9b23b93 100644 --- a/Classes/NodeRendering/Dto/NodeRenderingCompletionStatus.php +++ b/Classes/NodeRendering/Dto/NodeRenderingCompletionStatus.php @@ -11,7 +11,6 @@ */ final class NodeRenderingCompletionStatus implements \JsonSerializable { - private const SCHEDULED = 'scheduled'; private const RUNNING = 'running'; private const SUCCESS = 'success'; @@ -75,9 +74,6 @@ public function isRunning(): bool return $this->status === self::RUNNING; } - /** - * @return string - */ public function getStatus(): string { return $this->status; @@ -93,9 +89,8 @@ public function hasCompleted(): bool return $this->isSuccessful() || $this->isFailed(); } - public function jsonSerialize() + public function jsonSerialize(): string { return $this->status; } - } diff --git a/Classes/NodeRendering/Dto/RenderedDocumentFromContentCache.php b/Classes/NodeRendering/Dto/RenderedDocumentFromContentCache.php index b1da88d..5d68ca2 100644 --- a/Classes/NodeRendering/Dto/RenderedDocumentFromContentCache.php +++ b/Classes/NodeRendering/Dto/RenderedDocumentFromContentCache.php @@ -1,4 +1,5 @@ fullContent = $fullContent; $this->documentNodeCacheValues = $documentNodeCacheValues; $this->isComplete = $isComplete; $this->incompleteReason = $incompleteReason; } - - static public function createIncomplete(string $reason): self + public static function createIncomplete(string $reason): self { return new self('', DocumentNodeCacheValues::empty(), false, $reason); } - static public function createWithFullContent(string $fullContent, DocumentNodeCacheValues $documentNodeCacheValues): self - { + public static function createWithFullContent( + string $fullContent, + DocumentNodeCacheValues $documentNodeCacheValues + ): self { return new self($fullContent, $documentNodeCacheValues, true, ''); } @@ -62,7 +67,7 @@ public function getUrl(): string public function getLegacyUrlKey(): string { - return "url--" . str_replace('.', '%2E', urlencode($this->documentNodeCacheValues->getUrl())); + return 'url--' . str_replace('.', '%2E', urlencode($this->documentNodeCacheValues->getUrl())); } public function getLegacyMetadataKey(): string @@ -75,9 +80,14 @@ public function getMetadata(): array return $this->documentNodeCacheValues->getMetadata(); } + /** + * @throws \JsonException + */ public function getLegacyMetadataString(): string { - return isset($this->documentNodeCacheValues->getMetadata()['louisMetadata']) ? json_encode($this->documentNodeCacheValues->getMetadata()['louisMetadata']) : ''; + return isset($this->documentNodeCacheValues->getMetadata()['louisMetadata']) + ? json_encode($this->documentNodeCacheValues->getMetadata()['louisMetadata'], JSON_THROW_ON_ERROR) + : ''; } public function isComplete(): bool diff --git a/Classes/NodeRendering/Dto/RendererIdentifier.php b/Classes/NodeRendering/Dto/RendererIdentifier.php index 30124e4..6fe46a9 100644 --- a/Classes/NodeRendering/Dto/RendererIdentifier.php +++ b/Classes/NodeRendering/Dto/RendererIdentifier.php @@ -11,7 +11,6 @@ */ final class RendererIdentifier { - /** * @var string */ @@ -31,4 +30,4 @@ public function string(): string { return $this->identifier; } -} \ No newline at end of file +} diff --git a/Classes/NodeRendering/Dto/RenderingStatistics.php b/Classes/NodeRendering/Dto/RenderingStatistics.php index 1fbe341..447c08c 100644 --- a/Classes/NodeRendering/Dto/RenderingStatistics.php +++ b/Classes/NodeRendering/Dto/RenderingStatistics.php @@ -12,7 +12,6 @@ */ final class RenderingStatistics implements \JsonSerializable { - /** * @var int */ @@ -62,9 +61,6 @@ public static function fromJsonString($jsonString): self return new self($tmp['remainingJobs'], $tmp['totalJobs'], $tmp['renderingsPerSecond']); } - /** - * @return int - */ public function getRemainingJobs(): int { return $this->remainingJobs; @@ -75,31 +71,22 @@ public function getRenderedJobs(): int return $this->totalJobs - $this->remainingJobs; } - /** - * @return int - */ public function getTotalJobs(): int { return $this->totalJobs; } - /** - * @return array - */ public function getRenderingsPerSecond(): array { return $this->renderingsPerSecond; } - /** - * @return string - */ public function getSvgSparkline(): string { return $this->svgSparkline; } - public function jsonSerialize() + public function jsonSerialize(): array { return [ 'remainingJobs' => $this->remainingJobs, @@ -108,6 +95,4 @@ public function jsonSerialize() 'svgSparkline' => $this->svgSparkline ]; } - - } diff --git a/Classes/NodeRendering/Extensibility/ContentReleaseWriterInterface.php b/Classes/NodeRendering/Extensibility/ContentReleaseWriterInterface.php index 797d39f..d7bb74e 100644 --- a/Classes/NodeRendering/Extensibility/ContentReleaseWriterInterface.php +++ b/Classes/NodeRendering/Extensibility/ContentReleaseWriterInterface.php @@ -1,4 +1,5 @@ getFullContent(), 9); $redis = $this->redisClientManager->getPrimaryRedis(); - $redis->hSet($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderedDocuments'), $renderedDocumentFromContentCache->getUrl(), $compressedContent); + $redis->hSet( + $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderedDocuments'), + $renderedDocumentFromContentCache->getUrl(), + $compressedContent + ); // Published URLs, lexicographically sorted // we use the same score "0" for all URLs, this way, they are lexicographically sorted // as explained in https://redis.io/topics/data-types-intro#lexicographical-scores - $redis->zAdd($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'meta:urls'), 0, $renderedDocumentFromContentCache->getUrl()); + $redis->zAdd( + $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'meta:urls'), + 0, + $renderedDocumentFromContentCache->getUrl() + ); } } diff --git a/Classes/NodeRendering/Extensibility/ContentReleaseWriters/LegacyWriter.php b/Classes/NodeRendering/Extensibility/ContentReleaseWriters/LegacyWriter.php index 72829de..c0e9eff 100644 --- a/Classes/NodeRendering/Extensibility/ContentReleaseWriters/LegacyWriter.php +++ b/Classes/NodeRendering/Extensibility/ContentReleaseWriters/LegacyWriter.php @@ -1,4 +1,5 @@ getLegacyUrlKey(); $metadataUrlKey = $renderedDocumentFromContentCache->getLegacyMetadataKey(); @@ -56,12 +59,13 @@ public function processRenderedDocument(ContentReleaseIdentifier $contentRelease $redis->hSet($redisDataKey, $metadataUrlKey, $rootMetadataKey); $redis->hSet($redisDataKey, $rootMetadataKey, $renderedDocumentFromContentCache->getLegacyMetadataString()); - // Published URLs, lexicographically sorted // we use the same score "0" for all URLs, this way, they are lexicographically sorted // as explained in https://redis.io/topics/data-types-intro#lexicographical-scores - $redis->zAdd($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'meta:urls'), 0, $renderedDocumentFromContentCache->getUrl()); - + $redis->zAdd( + $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'meta:urls'), + 0, + $renderedDocumentFromContentCache->getUrl() + ); } - } diff --git a/Classes/NodeRendering/Extensibility/DocumentEnumeratorInterface.php b/Classes/NodeRendering/Extensibility/DocumentEnumeratorInterface.php index 1f7e962..246763a 100644 --- a/Classes/NodeRendering/Extensibility/DocumentEnumeratorInterface.php +++ b/Classes/NodeRendering/Extensibility/DocumentEnumeratorInterface.php @@ -1,4 +1,5 @@ uriPathSegmentFilter = $options['uriPathSegmentFilter'] ?? null; $this->nodePathSegmentFilter = $options['nodePathSegmentFilter'] ?? null; } + public function enumerateDocumentNode(NodeInterface $documentNode): iterable { if ( @@ -49,7 +52,7 @@ public function enumerateDocumentNode(NodeInterface $documentNode): iterable } return [ - EnumeratedNode::fromNode($documentNode), + EnumeratedNode::fromNode($documentNode) ]; } } diff --git a/Classes/NodeRendering/Extensibility/DocumentMetadataGeneratorInterface.php b/Classes/NodeRendering/Extensibility/DocumentMetadataGeneratorInterface.php index e1c2198..c692b43 100644 --- a/Classes/NodeRendering/Extensibility/DocumentMetadataGeneratorInterface.php +++ b/Classes/NodeRendering/Extensibility/DocumentMetadataGeneratorInterface.php @@ -1,4 +1,5 @@ withMetadata('key', $value) inside this method. Be sure to return the modified cache values passed in. */ - public function generateMetadata(NodeInterface $node, array $arguments, ControllerContext $controllerContext, DocumentNodeCacheValues $cacheValues): DocumentNodeCacheValues; -} \ No newline at end of file + public function generateMetadata( + NodeInterface $node, + array $arguments, + ControllerContext $controllerContext, + DocumentNodeCacheValues $cacheValues + ): DocumentNodeCacheValues; +} diff --git a/Classes/NodeRendering/Extensibility/DocumentRendererInterface.php b/Classes/NodeRendering/Extensibility/DocumentRendererInterface.php index c283b8e..e81a974 100644 --- a/Classes/NodeRendering/Extensibility/DocumentRendererInterface.php +++ b/Classes/NodeRendering/Extensibility/DocumentRendererInterface.php @@ -1,4 +1,5 @@ redisContentCacheReader->tryToExtractRenderingForEnumeratedNodeFromContentCache(DocumentNodeCacheKey::fromEnumeratedNode($enumeratedNode)); + return $this->redisContentCacheReader->tryToExtractRenderingForEnumeratedNodeFromContentCache(DocumentNodeCacheKey::fromEnumeratedNode( + $enumeratedNode + )); } - public function renderDocumentNodeVariant(NodeInterface $node, EnumeratedNode $enumeratedNode, ContentReleaseLogger $contentReleaseLogger): void - { - $this->documentRenderer->renderDocumentNodeVariant($node, $enumeratedNode->getArguments(), $contentReleaseLogger); + public function renderDocumentNodeVariant( + NodeInterface $node, + EnumeratedNode $enumeratedNode, + ContentReleaseLogger $contentReleaseLogger + ): void { + $this->documentRenderer->renderDocumentNodeVariant( + $node, + $enumeratedNode->getArguments(), + $contentReleaseLogger + ); } } diff --git a/Classes/NodeRendering/Extensibility/NodeRenderingExtensionManager.php b/Classes/NodeRendering/Extensibility/NodeRenderingExtensionManager.php index 8d3a3b3..690a5df 100644 --- a/Classes/NodeRendering/Extensibility/NodeRenderingExtensionManager.php +++ b/Classes/NodeRendering/Extensibility/NodeRenderingExtensionManager.php @@ -1,4 +1,5 @@ documentEnumerators)) { - $this->documentEnumerators = self::instantiateExtensions($this->configuredDocumentRenderers, DocumentEnumeratorInterface::class, classNameKey: 'enumeratorClassName', optionsKey: 'enumeratorOptions', preserveKey: true); + $this->documentEnumerators = self::instantiateExtensions( + $this->configuredDocumentRenderers, + DocumentEnumeratorInterface::class, + 'enumeratorClassName', + 'enumeratorOptions', + true + ); } foreach ($this->documentEnumerators as $rendererId => $documentEnumerator) { foreach ($documentEnumerator->enumerateDocumentNode($documentNode) as $enumeratedNode) { @@ -73,23 +79,33 @@ public function enumerateDocumentNode(NodeInterface $documentNode): iterable public function tryToExtractRenderingForEnumeratedNodeFromContentCache(EnumeratedNode $enumeratedNode): RenderedDocumentFromContentCache { - return $this->rendererFor($enumeratedNode) - ->tryToExtractRenderingForEnumeratedNodeFromContentCache($enumeratedNode); + return $this->rendererFor($enumeratedNode)->tryToExtractRenderingForEnumeratedNodeFromContentCache( + $enumeratedNode + ); } - public function renderDocumentNodeVariant(NodeInterface $node, EnumeratedNode $enumeratedNode, ContentReleaseLogger $contentReleaseLogger): void - { - $this->rendererFor($enumeratedNode) - ->renderDocumentNodeVariant($node, $enumeratedNode, $contentReleaseLogger); + public function renderDocumentNodeVariant( + NodeInterface $node, + EnumeratedNode $enumeratedNode, + ContentReleaseLogger $contentReleaseLogger + ): void { + $this->rendererFor($enumeratedNode)->renderDocumentNodeVariant($node, $enumeratedNode, $contentReleaseLogger); } protected function rendererFor(EnumeratedNode $enumeratedNode): DocumentRendererInterface { if (!isset($this->documentEnumerators)) { - $this->documentRenderers = self::instantiateExtensions($this->configuredDocumentRenderers, DocumentRendererInterface::class, classNameKey: 'rendererClassName', preserveKey: true); + $this->documentRenderers = self::instantiateExtensions( + $this->configuredDocumentRenderers, + DocumentRendererInterface::class, + 'rendererClassName', + preserveKey: true + ); } if (!array_key_exists($enumeratedNode->rendererId, $this->documentRenderers)) { - throw new \RuntimeException('No renderer found for renderer ID ' . $enumeratedNode->rendererId . ' - should never happen!'); + throw new \RuntimeException( + 'No renderer found for renderer ID ' . $enumeratedNode->rendererId . ' - should never happen!' + ); } return $this->documentRenderers[$enumeratedNode->rendererId]; } @@ -103,14 +119,26 @@ protected function rendererFor(EnumeratedNode $enumeratedNode): DocumentRenderer * @param DocumentNodeCacheValues $cacheValues * @return DocumentNodeCacheValues */ - public function runDocumentMetadataGenerators(NodeInterface $node, array $arguments, ControllerContext $controllerContext, DocumentNodeCacheValues $cacheValues): DocumentNodeCacheValues - { + public function runDocumentMetadataGenerators( + NodeInterface $node, + array $arguments, + ControllerContext $controllerContext, + DocumentNodeCacheValues $cacheValues + ): DocumentNodeCacheValues { if (!isset($this->documentMetadataGenerators)) { - $this->documentMetadataGenerators = self::instantiateExtensions($this->configuredDocumentMetadataGenerators, DocumentMetadataGeneratorInterface::class); + $this->documentMetadataGenerators = self::instantiateExtensions( + $this->configuredDocumentMetadataGenerators, + DocumentMetadataGeneratorInterface::class + ); } foreach ($this->documentMetadataGenerators as $documentMetadataGenerator) { assert($documentMetadataGenerator instanceof DocumentMetadataGeneratorInterface); - $cacheValues = $documentMetadataGenerator->generateMetadata($node, $arguments, $controllerContext, $cacheValues); + $cacheValues = $documentMetadataGenerator->generateMetadata( + $node, + $arguments, + $controllerContext, + $cacheValues + ); } return $cacheValues; } @@ -121,19 +149,36 @@ public function runDocumentMetadataGenerators(NodeInterface $node, array $argume * @param ContentReleaseIdentifier $contentReleaseIdentifier * @param RenderedDocumentFromContentCache $renderedDocumentFromContentCache */ - public function addRenderedDocumentToContentRelease(ContentReleaseIdentifier $contentReleaseIdentifier, EnumeratedNode $enumeratedNode, RenderedDocumentFromContentCache $renderedDocumentFromContentCache, ContentReleaseLogger $logger): void - { + public function addRenderedDocumentToContentRelease( + ContentReleaseIdentifier $contentReleaseIdentifier, + EnumeratedNode $enumeratedNode, + RenderedDocumentFromContentCache $renderedDocumentFromContentCache, + ContentReleaseLogger $logger + ): void { if (!isset($this->contentReleaseWriters[$enumeratedNode->rendererId])) { - $this->contentReleaseWriters[$enumeratedNode->rendererId] = self::instantiateExtensions($this->configuredDocumentRenderers[$enumeratedNode->rendererId]['contentReleaseWriters'], ContentReleaseWriterInterface::class, optionsKey: 'options'); + $this->contentReleaseWriters[$enumeratedNode->rendererId] = self::instantiateExtensions( + $this->configuredDocumentRenderers[$enumeratedNode->rendererId]['contentReleaseWriters'], + ContentReleaseWriterInterface::class, + optionsKey: 'options' + ); } foreach ($this->contentReleaseWriters[$enumeratedNode->rendererId] as $contentReleaseWriter) { assert($contentReleaseWriter instanceof ContentReleaseWriterInterface); - $contentReleaseWriter->processRenderedDocument($contentReleaseIdentifier, $renderedDocumentFromContentCache, $logger); + $contentReleaseWriter->processRenderedDocument( + $contentReleaseIdentifier, + $renderedDocumentFromContentCache, + $logger + ); } } - private static function instantiateExtensions(array $configuration, string $extensionInterfaceName, string $classNameKey = 'className', string|null $optionsKey = null, bool $preserveKey = false): array - { + private static function instantiateExtensions( + array $configuration, + string $extensionInterfaceName, + string $classNameKey = 'className', + ?string $optionsKey = null, + bool $preserveKey = false + ): array { $instantiatedExtensions = []; foreach ($configuration as $k => $extensionConfig) { if (!is_array($extensionConfig)) { @@ -145,8 +190,10 @@ private static function instantiateExtensions(array $configuration, string $exte } else { $instance = new $className(); } - if (!($instance instanceof $extensionInterfaceName)) { - throw new \RuntimeException('Extension ' . get_class($instance) . ' does not implement ' . $extensionInterfaceName); + if (!$instance instanceof $extensionInterfaceName) { + throw new \RuntimeException( + 'Extension ' . get_class($instance) . ' does not implement ' . $extensionInterfaceName + ); } if ($preserveKey) { diff --git a/Classes/NodeRendering/Infrastructure/RedisContentCacheReader.php b/Classes/NodeRendering/Infrastructure/RedisContentCacheReader.php index 7674be8..a57881f 100644 --- a/Classes/NodeRendering/Infrastructure/RedisContentCacheReader.php +++ b/Classes/NodeRendering/Infrastructure/RedisContentCacheReader.php @@ -1,5 +1,7 @@ ${maxNestLevel} then - -- Return an error in this case - return '', 'Maximum Nesting Level Reached' - end + if depth > ${maxNestLevel} then + -- Return an error in this case + return '', 'Maximum Nesting Level Reached' + end - local content = redis.call('GET', identifierPrefix .. 'Neos_Fusion_Content:entry:' .. identifier) - if not content then - return '', identifierPrefix .. 'Neos_Fusion_Content:entry:' .. identifier .. ' not found' - end + local content = redis.call('GET', identifierPrefix .. 'Neos_Fusion_Content:entry:' .. identifier) + if not content then + return '', identifierPrefix .. 'Neos_Fusion_Content:entry:' .. identifier .. ' not found' + end + + local error = nil + content = string.gsub(content, '${contentCacheStartToken}${contentCacheMarker}([a-z0-9]+)${contentCacheEndToken}${contentCacheMarker}', function(id) + local str + local errMsg + str, errMsg = readContentCacheRecursively(id, depth + 1) - local error = nil - content = string.gsub(content, '${contentCacheStartToken}${contentCacheMarker}([a-z0-9]+)${contentCacheEndToken}${contentCacheMarker}', function(id) - local str - local errMsg - str, errMsg = readContentCacheRecursively(id, depth + 1) + if errMsg then + error = errMsg + end - if errMsg then - error = errMsg + return str end + ) - return str + if error then + return nil, error + else + return content, nil end - ) - - if error then - return nil, error - else - return content, nil end - end - local content, error = readContentCacheRecursively(rootIdentifier) - if not error then - error = '' - end + local content, error = readContentCacheRecursively(rootIdentifier) + if not error then + error = '' + end - if not content then - content = '' - end + if not content then + content = '' + end - return {content, error} - LUA; + return {content, error} + LUA; } /** @@ -179,7 +180,7 @@ protected function getRedis(): \Redis $packageManager = $this->objectManager->get(PackageManager::class); $flowPackage = $packageManager->getPackage('Neos.Flow'); preg_match('/^(\d+\.\d+)/', $flowPackage->getInstalledVersion(), $versionMatches); - $flowMajorVersion = (int)($versionMatches[1] ?? '0'); + $flowMajorVersion = (int) ( $versionMatches[1] ?? '0' ); $backend = $this->contentCache->getBackend(); @@ -201,9 +202,9 @@ protected function getRedis(): \Redis } throw new \RuntimeException( - 'The cache backend for "Neos_Fusion_Content" must be an OptimizedRedisCacheBackend, but is ' . get_class( - $backend - ), 1622570000 + 'The cache backend for "Neos_Fusion_Content" must be an OptimizedRedisCacheBackend, but is ' + . get_class($backend), + 1622570000 ); } } diff --git a/Classes/NodeRendering/Infrastructure/RedisRenderingErrorManager.php b/Classes/NodeRendering/Infrastructure/RedisRenderingErrorManager.php index e58d21e..0c1c9de 100644 --- a/Classes/NodeRendering/Infrastructure/RedisRenderingErrorManager.php +++ b/Classes/NodeRendering/Infrastructure/RedisRenderingErrorManager.php @@ -1,22 +1,22 @@ redisClientManager->getPrimaryRedis()->sAdd($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingErrors'), $exception->getMessage() . ' - ' . json_encode($additionalData)); + public function registerRenderingError( + ContentReleaseIdentifier $contentReleaseIdentifier, + array $additionalData, + \Exception $exception + ): void { + $this->redisClientManager->getPrimaryRedis()->sAdd( + $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingErrors'), + $exception->getMessage() . ' - ' . json_encode($additionalData) + ); } - public function getRenderingErrors(ContentReleaseIdentifier $contentReleaseIdentifier, ?RedisInstanceIdentifier $redisInstanceIdentifier = null): array - { + public function getRenderingErrors( + ContentReleaseIdentifier $contentReleaseIdentifier, + ?RedisInstanceIdentifier $redisInstanceIdentifier = null + ): array { $redisInstanceIdentifier = $redisInstanceIdentifier ?: RedisInstanceIdentifier::primary(); - return $this->redisClientManager->getRedis($redisInstanceIdentifier)->sMembers($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingErrors')); + return $this->redisClientManager + ->getRedis($redisInstanceIdentifier) + ->sMembers($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingErrors')); } public function flush(ContentReleaseIdentifier $contentReleaseIdentifier): void { - $this->redisClientManager->getPrimaryRedis()->del($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingErrors')); + $this->redisClientManager->getPrimaryRedis()->del($this->redisKeyService->getRedisKeyForPostfix( + $contentReleaseIdentifier, + 'renderingErrors' + )); } - public function countMultipleErrors(RedisInstanceIdentifier $redisInstanceIdentifier, ContentReleaseIdentifier ...$releaseIdentifiers): ContentReleaseBatchResult - { + public function countMultipleErrors( + RedisInstanceIdentifier $redisInstanceIdentifier, + ContentReleaseIdentifier ...$releaseIdentifiers + ): ContentReleaseBatchResult { $result = []; // KEY == contentReleaseIdentifier. VALUE == count of error entries $redis = $this->redisClientManager->getRedis($redisInstanceIdentifier); foreach (GeneratorUtility::createArrayBatch($releaseIdentifiers, 50) as $batchedReleaseIdentifiers) { $redisPipeline = $redis->pipeline(); foreach ($batchedReleaseIdentifiers as $releaseIdentifier) { - $redisPipeline->scard($this->redisKeyService->getRedisKeyForPostfix($releaseIdentifier, 'renderingErrors')); + $redisPipeline->scard($this->redisKeyService->getRedisKeyForPostfix( + $releaseIdentifier, + 'renderingErrors' + )); } $res = $redisPipeline->exec(); foreach ($batchedReleaseIdentifiers as $i => $releaseIdentifier) { @@ -61,5 +79,4 @@ public function countMultipleErrors(RedisInstanceIdentifier $redisInstanceIdenti } return ContentReleaseBatchResult::createFromArray($result); } - } diff --git a/Classes/NodeRendering/Infrastructure/RedisRenderingQueue.php b/Classes/NodeRendering/Infrastructure/RedisRenderingQueue.php index a4c83b2..627825e 100644 --- a/Classes/NodeRendering/Infrastructure/RedisRenderingQueue.php +++ b/Classes/NodeRendering/Infrastructure/RedisRenderingQueue.php @@ -1,21 +1,21 @@ redisClientManager->getPrimaryRedis()->rPush($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingJobQueue'), $encodedNode); + /** + * @throws \JsonException + */ + public function appendRenderingJob( + ContentReleaseIdentifier $contentReleaseIdentifier, + EnumeratedNode $enumeratedNode + ) { + $encodedNode = json_encode($enumeratedNode, JSON_THROW_ON_ERROR); + $this->redisClientManager->getPrimaryRedis()->rPush( + $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingJobQueue'), + $encodedNode + ); } public function numberOfQueuedJobs(ContentReleaseIdentifier $contentReleaseIdentifier): int { - return $this->redisClientManager->getPrimaryRedis()->lLen($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingJobQueue')) ?? 0; + return ( + $this->redisClientManager + ->getPrimaryRedis() + ->lLen($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingJobQueue')) + ?? 0 + ); } public function numberOfRenderingsInProgress(ContentReleaseIdentifier $contentReleaseIdentifier): int { - return $this->redisClientManager->getPrimaryRedis()->hLen($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'inProgressRenderings')) ?? 0; + return ( + $this->redisClientManager + ->getPrimaryRedis() + ->hLen($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'inProgressRenderings')) + ?? 0 + ); } - public function fetchAndReserveNextRenderingJob(ContentReleaseIdentifier $contentReleaseIdentifier, RendererIdentifier $rendererIdentifier): ?EnumeratedNode - { + public function fetchAndReserveNextRenderingJob( + ContentReleaseIdentifier $contentReleaseIdentifier, + RendererIdentifier $rendererIdentifier + ): ?EnumeratedNode { $redis = $this->redisClientManager->getPrimaryRedis(); // KEYS[1] is $renderingJobQueueKey @@ -64,7 +84,15 @@ public function fetchAndReserveNextRenderingJob(ContentReleaseIdentifier $conten return result "; - $nextEntry = $redis->eval($script, array($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingJobQueue'), $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'inProgressRenderings'), $rendererIdentifier->string()), 2); + $nextEntry = $redis->eval( + $script, + array( + $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingJobQueue'), + $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'inProgressRenderings'), + $rendererIdentifier->string() + ), + 2 + ); if ($nextEntry === false && $redis->getLastError() !== null) { throw new \Exception('Redis operation EVAL failed: ' . $redis->getLastError(), 1471442667); } @@ -82,12 +110,16 @@ public function fetchAndReserveNextRenderingJob(ContentReleaseIdentifier $conten * * A node which is handed out more than once means the previous rendering did not lead to a complete content * cache entry, {@see \Flowpack\DecoupledContentStore\NodeRendering\NodeRenderOrchestrator}. + * + * @throws \JsonException */ - public function registerRenderingAttempt(ContentReleaseIdentifier $contentReleaseIdentifier, EnumeratedNode $enumeratedNode): int - { - return (int)$this->redisClientManager->getPrimaryRedis()->hIncrBy( + public function registerRenderingAttempt( + ContentReleaseIdentifier $contentReleaseIdentifier, + EnumeratedNode $enumeratedNode + ): int { + return (int) $this->redisClientManager->getPrimaryRedis()->hIncrBy( $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderAttempts'), - json_encode($enumeratedNode), + json_encode($enumeratedNode, JSON_THROW_ON_ERROR), 1 ); } @@ -97,9 +129,13 @@ public function registerRenderingAttempt(ContentReleaseIdentifier $contentReleas * @param EnumeratedNode $enumeratedNode * @param RendererIdentifier $rendererIdentifier * @return bool TRUE if removal was successful or element was not found, FALSE if element was claimed by another renderer in the meantime (however this has happened) + * @throws \JsonException */ - public function removeRenderingJobFromReservedList(ContentReleaseIdentifier $contentReleaseIdentifier, EnumeratedNode $enumeratedNode, RendererIdentifier $rendererIdentifier): bool - { + public function removeRenderingJobFromReservedList( + ContentReleaseIdentifier $contentReleaseIdentifier, + EnumeratedNode $enumeratedNode, + RendererIdentifier $rendererIdentifier + ): bool { // Defensive Programming: It might be that the job has been claimed by another worker in the meantime (no clue how this might have happened though) $script = " local renderingReservedJobsKey = KEYS[1] @@ -119,12 +155,23 @@ public function removeRenderingJobFromReservedList(ContentReleaseIdentifier $con end "; - $removalSuccessful = $this->redisClientManager->getPrimaryRedis()->eval($script, array($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'inProgressRenderings'), json_encode($enumeratedNode), $rendererIdentifier->string()), 1); - return $removalSuccessful; + return (bool) $this->redisClientManager->getPrimaryRedis()->eval( + $script, + array( + $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'inProgressRenderings'), + json_encode($enumeratedNode, JSON_THROW_ON_ERROR), + $rendererIdentifier->string() + ), + 1 + ); } public function flush(ContentReleaseIdentifier $contentReleaseIdentifier) { - $this->redisClientManager->getPrimaryRedis()->del($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingJobQueue'), $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'inProgressRenderings'), $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderAttempts')); + $this->redisClientManager->getPrimaryRedis()->del( + $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingJobQueue'), + $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'inProgressRenderings'), + $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderAttempts') + ); } } diff --git a/Classes/NodeRendering/Infrastructure/RedisRenderingTimeStatisticsStore.php b/Classes/NodeRendering/Infrastructure/RedisRenderingTimeStatisticsStore.php index 8e897e2..b8c9b17 100644 --- a/Classes/NodeRendering/Infrastructure/RedisRenderingTimeStatisticsStore.php +++ b/Classes/NodeRendering/Infrastructure/RedisRenderingTimeStatisticsStore.php @@ -1,22 +1,23 @@ redisClientManager->getPrimaryRedis()->rPush($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingStatistics'), json_encode($renderingStatistics)); + public function addStatisticsIteration( + ContentReleaseIdentifier $contentReleaseIdentifier, + ?RenderingStatistics $renderingStatistics + ) { + $this->redisClientManager->getPrimaryRedis()->rPush( + $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingStatistics'), + json_encode($renderingStatistics) + ); } - public function replaceLastStatisticsIteration(ContentReleaseIdentifier $contentReleaseIdentifier, RenderingStatistics $renderingStatistics) - { - $this->redisClientManager->getPrimaryRedis()->rPop($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingStatistics')); + public function replaceLastStatisticsIteration( + ContentReleaseIdentifier $contentReleaseIdentifier, + RenderingStatistics $renderingStatistics + ) { + $this->redisClientManager + ->getPrimaryRedis() + ->rPop($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingStatistics')); $this->addStatisticsIteration($contentReleaseIdentifier, $renderingStatistics); } - public function getRenderingStatistics(ContentReleaseIdentifier $contentReleaseIdentifier, RedisInstanceIdentifier $redisInstanceIdentifier): array - { - return $this->redisClientManager->getRedis($redisInstanceIdentifier)->lRange($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingStatistics'), 0, -1); + public function getRenderingStatistics( + ContentReleaseIdentifier $contentReleaseIdentifier, + RedisInstanceIdentifier $redisInstanceIdentifier + ): array { + return $this->redisClientManager->getRedis($redisInstanceIdentifier)->lRange( + $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingStatistics'), + 0, + -1 + ); } public function flush(ContentReleaseIdentifier $contentReleaseIdentifier) { - $this->redisClientManager->getPrimaryRedis()->del($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'renderingStatistics')); + $this->redisClientManager->getPrimaryRedis()->del($this->redisKeyService->getRedisKeyForPostfix( + $contentReleaseIdentifier, + 'renderingStatistics' + )); } - public function countMultipleRenderingStatistics(RedisInstanceIdentifier $redisInstanceIdentifier, ContentReleaseIdentifier ...$releaseIdentifiers): ContentReleaseBatchResult - { + public function countMultipleRenderingStatistics( + RedisInstanceIdentifier $redisInstanceIdentifier, + ContentReleaseIdentifier ...$releaseIdentifiers + ): ContentReleaseBatchResult { $result = []; // KEY == contentReleaseIdentifier. VALUE == count of statistics entries (= count of iterations) $redis = $this->redisClientManager->getRedis($redisInstanceIdentifier); foreach (GeneratorUtility::createArrayBatch($releaseIdentifiers, 50) as $batchedReleaseIdentifiers) { $redisPipeline = $redis->pipeline(); foreach ($batchedReleaseIdentifiers as $releaseIdentifier) { - $redisPipeline->llen($this->redisKeyService->getRedisKeyForPostfix($releaseIdentifier, 'renderingStatistics')); + $redisPipeline->llen($this->redisKeyService->getRedisKeyForPostfix( + $releaseIdentifier, + 'renderingStatistics' + )); } $res = $redisPipeline->exec(); foreach ($batchedReleaseIdentifiers as $i => $releaseIdentifier) { @@ -67,14 +91,19 @@ public function countMultipleRenderingStatistics(RedisInstanceIdentifier $redisI return ContentReleaseBatchResult::createFromArray($result); } - public function getLastRenderingStatisticsEntry(RedisInstanceIdentifier $redisInstanceIdentifier, ContentReleaseIdentifier ...$releaseIdentifiers): ContentReleaseBatchResult - { + public function getLastRenderingStatisticsEntry( + RedisInstanceIdentifier $redisInstanceIdentifier, + ContentReleaseIdentifier ...$releaseIdentifiers + ): ContentReleaseBatchResult { $result = []; // KEY == contentReleaseIdentifier. VALUE == last rendering statistics entry) $redis = $this->redisClientManager->getRedis($redisInstanceIdentifier); foreach (GeneratorUtility::createArrayBatch($releaseIdentifiers, 50) as $batchedReleaseIdentifiers) { $redisPipeline = $redis->pipeline(); foreach ($batchedReleaseIdentifiers as $releaseIdentifier) { - $redisPipeline->lindex($this->redisKeyService->getRedisKeyForPostfix($releaseIdentifier, 'renderingStatistics'), -1); + $redisPipeline->lindex( + $this->redisKeyService->getRedisKeyForPostfix($releaseIdentifier, 'renderingStatistics'), + -1 + ); } $res = $redisPipeline->exec(); foreach ($batchedReleaseIdentifiers as $i => $releaseIdentifier) { @@ -84,14 +113,19 @@ public function getLastRenderingStatisticsEntry(RedisInstanceIdentifier $redisIn return ContentReleaseBatchResult::createFromArray($result); } - public function getFirstRenderingStatisticsEntry(RedisInstanceIdentifier $redisInstanceIdentifier, ContentReleaseIdentifier ...$releaseIdentifiers): ContentReleaseBatchResult - { + public function getFirstRenderingStatisticsEntry( + RedisInstanceIdentifier $redisInstanceIdentifier, + ContentReleaseIdentifier ...$releaseIdentifiers + ): ContentReleaseBatchResult { $result = []; // KEY == contentReleaseIdentifier. VALUE == first rendering statistics entry) $redis = $this->redisClientManager->getRedis($redisInstanceIdentifier); foreach (GeneratorUtility::createArrayBatch($releaseIdentifiers, 50) as $batchedReleaseIdentifiers) { $redisPipeline = $redis->pipeline(); foreach ($batchedReleaseIdentifiers as $releaseIdentifier) { - $redisPipeline->lindex($this->redisKeyService->getRedisKeyForPostfix($releaseIdentifier, 'renderingStatistics'), 0); + $redisPipeline->lindex( + $this->redisKeyService->getRedisKeyForPostfix($releaseIdentifier, 'renderingStatistics'), + 0 + ); } $res = $redisPipeline->exec(); foreach ($batchedReleaseIdentifiers as $i => $releaseIdentifier) { diff --git a/Classes/NodeRendering/InterruptibleProcessRuntime.php b/Classes/NodeRendering/InterruptibleProcessRuntime.php index 7972828..8f0d03f 100644 --- a/Classes/NodeRendering/InterruptibleProcessRuntime.php +++ b/Classes/NodeRendering/InterruptibleProcessRuntime.php @@ -1,4 +1,5 @@ handleExitEvent($currentEvent); } - $shortName = (new \ReflectionClass($currentEvent))->getShortName(); + $shortName = new \ReflectionClass($currentEvent)->getShortName(); foreach ($eventClassNames as $eventClassName) { if ($eventClassName === $shortName || is_a($currentEvent, $eventClassName)) { // stop here, can be restarted lateron. We still need to continue to the next event here. @@ -123,12 +124,11 @@ public function runUntilEventEncountered(string ...$eventClassNames): ?Interrupt return null; } - protected function handleExitEvent(ExitEvent $event): InterruptibleProcessRuntimeEventInterface + private function handleExitEvent(ExitEvent $event): InterruptibleProcessRuntimeEventInterface { if ($this->inTestingMode === true) { return $event; } exit($event->getStatusCode()); } - -} \ No newline at end of file +} diff --git a/Classes/NodeRendering/InterruptibleProcessRuntimeEventInterface.php b/Classes/NodeRendering/InterruptibleProcessRuntimeEventInterface.php index 34c6388..b2e6fbc 100644 --- a/Classes/NodeRendering/InterruptibleProcessRuntimeEventInterface.php +++ b/Classes/NodeRendering/InterruptibleProcessRuntimeEventInterface.php @@ -1,13 +1,12 @@ redisContentReleaseService->fetchMetadataForContentRelease($contentReleaseIdentifier); $renderStatus = $releaseMetadata->getStatus(); if ($renderStatus->hasCompleted()) { - $contentReleaseLogger->error('Release has already completed with status ' . $renderStatus->getDisplayName() . ', so we cannot render again.'); + $contentReleaseLogger->error( + 'Release has already completed with status ' + . $renderStatus->getDisplayName() + . ', so we cannot render again.' + ); yield ExitEvent::createWithStatusCode(self::EXIT_ERRORSTATUSCODE_RELEASE_ALREADY_COMPLETED); return; } @@ -136,8 +141,14 @@ public function renderContentRelease(ContentReleaseIdentifier $contentReleaseIde $this->redisRenderingStatisticsStore->flush($contentReleaseIdentifier); if ($this->redisEnumerationRepository->count($contentReleaseIdentifier) === 0) { - $contentReleaseLogger->error('Content Enumeration is empty. This is dangerous; we never want this to go live. Exiting.'); - $this->redisContentReleaseService->setContentReleaseMetadata($contentReleaseIdentifier, $releaseMetadata->withStatus(NodeRenderingCompletionStatus::failed()), RedisInstanceIdentifier::primary()); + $contentReleaseLogger->error( + 'Content Enumeration is empty. This is dangerous; we never want this to go live. Exiting.' + ); + $this->redisContentReleaseService->setContentReleaseMetadata( + $contentReleaseIdentifier, + $releaseMetadata->withStatus(NodeRenderingCompletionStatus::failed()), + RedisInstanceIdentifier::primary() + ); yield ExitEvent::createWithStatusCode(self::EXIT_ERRORSTATUSCODE_EMPTY_ENUMERATION); return; } @@ -151,8 +162,14 @@ public function renderContentRelease(ContentReleaseIdentifier $contentReleaseIde do { $i++; if ($i > 10) { - $contentReleaseLogger->error('FAILED to build a complete content release after 10 rendering attempts. Exiting.'); - $this->redisContentReleaseService->setContentReleaseMetadata($contentReleaseIdentifier, $releaseMetadata->withStatus(NodeRenderingCompletionStatus::failed()), RedisInstanceIdentifier::primary()); + $contentReleaseLogger->error( + 'FAILED to build a complete content release after 10 rendering attempts. Exiting.' + ); + $this->redisContentReleaseService->setContentReleaseMetadata( + $contentReleaseIdentifier, + $releaseMetadata->withStatus(NodeRenderingCompletionStatus::failed()), + RedisInstanceIdentifier::primary() + ); yield ExitEvent::createWithStatusCode(self::EXIT_ERRORSTATUSCODE_RETRY_LIMIT_REACHED); return; } @@ -160,28 +177,40 @@ public function renderContentRelease(ContentReleaseIdentifier $contentReleaseIde $contentReleaseLogger->info('Starting iteration ' . $i); $this->concurrentBuildLockService->assertNoOtherContentReleaseWasStarted($contentReleaseIdentifier); - $this->redisRenderingStatisticsStore->addStatisticsIteration($contentReleaseIdentifier, RenderingStatistics::create(0, 0, [])); + $this->redisRenderingStatisticsStore->addStatisticsIteration($contentReleaseIdentifier, RenderingStatistics::create( + 0, + 0, + [] + )); // goTroughEnumeratedNodesFillContentReleaseAndCheckWhatStillNeedsToBeDone $nodesScheduledForRendering = []; foreach ($currentEnumeration as $enumeratedNode) { assert($enumeratedNode instanceof EnumeratedNode); - $renderedDocumentFromContentCache = $this->nodeRenderingExtensionManager->tryToExtractRenderingForEnumeratedNodeFromContentCache($enumeratedNode); - if ($renderedDocumentFromContentCache->isComplete()) { - $contentReleaseLogger->debug( - 'Node fully rendered, adding to content release', - ['url' => $renderedDocumentFromContentCache->getUrl(), 'node' => $enumeratedNode] + $renderedDocumentFromContentCache = + $this->nodeRenderingExtensionManager->tryToExtractRenderingForEnumeratedNodeFromContentCache( + $enumeratedNode ); + if ($renderedDocumentFromContentCache->isComplete()) { + $contentReleaseLogger->debug('Node fully rendered, adding to content release', [ + 'url' => $renderedDocumentFromContentCache->getUrl(), + 'node' => $enumeratedNode + ]); // NOTE: Eventually consistent (TODO describe) // If wanted more fully consistent, move to bottom.... - $this->nodeRenderingExtensionManager->addRenderedDocumentToContentRelease($contentReleaseIdentifier, $enumeratedNode, $renderedDocumentFromContentCache, $contentReleaseLogger); - } else { - $contentReleaseLogger->debug( - 'Scheduling rendering for Node, as it was not found or its content is incomplete: ' - . $renderedDocumentFromContentCache->getIncompleteReason(), - ['url' => $renderedDocumentFromContentCache->getUrl(), 'node' => $enumeratedNode] + $this->nodeRenderingExtensionManager->addRenderedDocumentToContentRelease( + $contentReleaseIdentifier, + $enumeratedNode, + $renderedDocumentFromContentCache, + $contentReleaseLogger ); + } else { + $contentReleaseLogger->debug('Scheduling rendering for Node, as it was not found or its content is incomplete: ' + . $renderedDocumentFromContentCache->getIncompleteReason(), [ + 'url' => $renderedDocumentFromContentCache->getUrl(), + 'node' => $enumeratedNode + ]); // the rendered document was not found, or has holes. so we need to re-render. $nodesScheduledForRendering[] = $enumeratedNode; $this->redisRenderingQueue->appendRenderingJob($contentReleaseIdentifier, $enumeratedNode); @@ -190,15 +219,28 @@ public function renderContentRelease(ContentReleaseIdentifier $contentReleaseIde if (empty($nodesScheduledForRendering)) { // we have NO nodes scheduled for rendering anymore, so that means we FINISHED successfully. - $contentReleaseLogger->info(sprintf('Everything rendered completely in %d seconds. Finishing RenderOrchestrator', time() - $startTime)); + $contentReleaseLogger->info(sprintf( + 'Everything rendered completely in %d seconds. Finishing RenderOrchestrator', + time() - $startTime + )); // The release is complete now, so this is the point where we can determine its size once. Calculating // it is expensive, which is why the Backend UI relies on this stored value instead of re-calculating it. - $contentReleaseSize = $this->redisContentReleaseSizeService->calculateReleaseSize(RedisInstanceIdentifier::primary(), $contentReleaseIdentifier); + $contentReleaseSize = $this->redisContentReleaseSizeService->calculateReleaseSize( + RedisInstanceIdentifier::primary(), + $contentReleaseIdentifier + ); $contentReleaseLogger->info(sprintf('Content release size: %.2f MB', $contentReleaseSize)); // info to all renderers that we finished, and they should terminate themselves gracefully. - $this->redisContentReleaseService->setContentReleaseMetadata($contentReleaseIdentifier, $releaseMetadata->withStatus(NodeRenderingCompletionStatus::success())->withEndTime(new \DateTimeImmutable())->withContentReleaseSize($contentReleaseSize), RedisInstanceIdentifier::primary()); + $this->redisContentReleaseService->setContentReleaseMetadata( + $contentReleaseIdentifier, + $releaseMetadata + ->withStatus(NodeRenderingCompletionStatus::success()) + ->withEndTime(new \DateTimeImmutable()) + ->withContentReleaseSize($contentReleaseSize), + RedisInstanceIdentifier::primary() + ); // Exit successfully. yield ExitEvent::createWithStatusCode(0); @@ -209,8 +251,9 @@ public function renderContentRelease(ContentReleaseIdentifier $contentReleaseIde // closer to a complete content release. Retrying this until the retry limit is reached only wastes time - // and (because no exception happened) leaves no trace anywhere. So we register a rendering error naming // these nodes, which makes them visible in the Backend UI. - $scheduledNodes = array_map(fn(EnumeratedNode $enumeratedNode) => json_encode($enumeratedNode), - $nodesScheduledForRendering); + $scheduledNodes = array_map(fn(EnumeratedNode $enumeratedNode) => json_encode( + $enumeratedNode + ), $nodesScheduledForRendering); sort($scheduledNodes); $identicalIterationCount = $scheduledNodes === $previouslyScheduledNodes ? $identicalIterationCount + 1 : 1; $previouslyScheduledNodes = $scheduledNodes; @@ -220,12 +263,10 @@ public function renderContentRelease(ContentReleaseIdentifier $contentReleaseIde $this->redisRenderingErrorManager->registerRenderingError( $contentReleaseIdentifier, ['node' => $enumeratedNode->debugString()], - new \Exception( - sprintf( - 'This node was scheduled for rendering %d times in a row without ever becoming complete in the content cache. Check the render worker logs for this node - most likely no "doc--..." mapping entry is written for it.', - self::MAX_ITERATIONS_WITHOUT_PROGRESS - ) - ) + new \Exception(sprintf( + 'This node was scheduled for rendering %d times in a row without ever becoming complete in the content cache. Check the render worker logs for this node - most likely no "doc--..." mapping entry is written for it.', + self::MAX_ITERATIONS_WITHOUT_PROGRESS + )) ); } $this->redisContentReleaseService->setContentReleaseMetadata( @@ -233,13 +274,11 @@ public function renderContentRelease(ContentReleaseIdentifier $contentReleaseIde $releaseMetadata->withStatus(NodeRenderingCompletionStatus::failed()), RedisInstanceIdentifier::primary() ); - $contentReleaseLogger->error( - sprintf( - 'The same %d nodes were scheduled for rendering %d iterations in a row without any progress. EXITING now.', - count($nodesScheduledForRendering), - self::MAX_ITERATIONS_WITHOUT_PROGRESS - ) - ); + $contentReleaseLogger->error(sprintf( + 'The same %d nodes were scheduled for rendering %d iterations in a row without any progress. EXITING now.', + count($nodesScheduledForRendering), + self::MAX_ITERATIONS_WITHOUT_PROGRESS + )); yield ExitEvent::createWithStatusCode(self::EXIT_ERRORSTATUSCODE_RENDERING_ERRORS); return; } @@ -259,12 +298,19 @@ public function renderContentRelease(ContentReleaseIdentifier $contentReleaseIde $contentReleaseLogger->info('Waiting for renderings to complete...'); $waitTimer = 0; - while ($this->redisRenderingQueue->numberOfQueuedJobs($contentReleaseIdentifier) > 0 || $this->redisRenderingQueue->numberOfRenderingsInProgress($contentReleaseIdentifier) > 0) { - $this->redisRenderingStatisticsStore->replaceLastStatisticsIteration($contentReleaseIdentifier, RenderingStatistics::create($remainingJobsCount, $totalJobsCount, $renderingsPerSecondDataPoints)); + while ( + $this->redisRenderingQueue->numberOfQueuedJobs($contentReleaseIdentifier) > 0 + || $this->redisRenderingQueue->numberOfRenderingsInProgress($contentReleaseIdentifier) > 0 + ) { + $this->redisRenderingStatisticsStore->replaceLastStatisticsIteration($contentReleaseIdentifier, RenderingStatistics::create( + $remainingJobsCount, + $totalJobsCount, + $renderingsPerSecondDataPoints + )); sleep(1); $waitTimer++; - if ($waitTimer % 10 === 0) { + if (( $waitTimer % 10 ) === 0) { $previousRemainingJobs = $remainingJobsCount; $remainingJobsCount = $this->redisRenderingQueue->numberOfQueuedJobs($contentReleaseIdentifier); $jobsWorkedThroughOverLastTenSeconds = $previousRemainingJobs - $remainingJobsCount; @@ -272,14 +318,14 @@ public function renderContentRelease(ContentReleaseIdentifier $contentReleaseIde $contentReleaseLogger->debug('Waiting... ', [ 'numberOfQueuedJobs' => $remainingJobsCount, - 'numberOfRenderingsInProgress' => $this->redisRenderingQueue->numberOfRenderingsInProgress($contentReleaseIdentifier), + 'numberOfRenderingsInProgress' => + $this->redisRenderingQueue->numberOfRenderingsInProgress($contentReleaseIdentifier) ]); $this->concurrentBuildLockService->assertNoOtherContentReleaseWasStarted($contentReleaseIdentifier); } } - // NOTE: we do not abort rendering inside NodeRenderer when we encounter the first error, but we try to render // all pages in the full iteration until we stop the content release here. // This is to gain better visibility into all errors currently happening; and thus maybe being able to see @@ -288,14 +334,26 @@ public function renderContentRelease(ContentReleaseIdentifier $contentReleaseIde $renderingErrors = $this->redisRenderingErrorManager->getRenderingErrors($contentReleaseIdentifier); $amountOfRenderingErrors = count($renderingErrors); if ($amountOfRenderingErrors > 0) { - $this->redisContentReleaseService->setContentReleaseMetadata($contentReleaseIdentifier, $releaseMetadata->withStatus(NodeRenderingCompletionStatus::failed()), RedisInstanceIdentifier::primary()); - $contentReleaseLogger->error('In this iteration, there happened ' . $amountOfRenderingErrors . ' rendering errors. EXITING now, as there is no chance of completing the content release successfully.', [$renderingErrors]); + $this->redisContentReleaseService->setContentReleaseMetadata( + $contentReleaseIdentifier, + $releaseMetadata->withStatus(NodeRenderingCompletionStatus::failed()), + RedisInstanceIdentifier::primary() + ); + $contentReleaseLogger->error('In this iteration, there happened ' + . $amountOfRenderingErrors + . ' rendering errors. EXITING now, as there is no chance of completing the content release successfully.', [ + $renderingErrors + ]); yield ExitEvent::createWithStatusCode(self::EXIT_ERRORSTATUSCODE_RENDERING_ERRORS); return; } $remainingJobsCount = $this->redisRenderingQueue->numberOfQueuedJobs($contentReleaseIdentifier); - $this->redisRenderingStatisticsStore->replaceLastStatisticsIteration($contentReleaseIdentifier, RenderingStatistics::create($remainingJobsCount, $totalJobsCount, $renderingsPerSecondDataPoints)); + $this->redisRenderingStatisticsStore->replaceLastStatisticsIteration($contentReleaseIdentifier, RenderingStatistics::create( + $remainingJobsCount, + $totalJobsCount, + $renderingsPerSecondDataPoints + )); yield RenderingIterationCompletedEvent::create(); diff --git a/Classes/NodeRendering/NodeRenderer.php b/Classes/NodeRendering/NodeRenderer.php index 385c602..5990148 100644 --- a/Classes/NodeRendering/NodeRenderer.php +++ b/Classes/NodeRendering/NodeRenderer.php @@ -115,7 +115,6 @@ class NodeRenderer */ protected $contentReleaseManager; - /** * @Flow\Inject * @var PersistenceManagerInterface @@ -134,25 +133,35 @@ class NodeRenderer */ protected $nodeRenderingExtensionManager; - public function render(ContentReleaseIdentifier $contentReleaseIdentifier, ContentReleaseLogger $contentReleaseLogger, RendererIdentifier $rendererIdentifier) - { + public function render( + ContentReleaseIdentifier $contentReleaseIdentifier, + ContentReleaseLogger $contentReleaseLogger, + RendererIdentifier $rendererIdentifier + ) { $contentReleaseLogger = $contentReleaseLogger->withRenderer($rendererIdentifier); $i = 0; while (true) { - $renderStatus = $this->redisContentReleaseService->fetchMetadataForContentRelease($contentReleaseIdentifier)->getStatus(); + $renderStatus = $this->redisContentReleaseService + ->fetchMetadataForContentRelease($contentReleaseIdentifier) + ->getStatus(); if ($renderStatus->hasCompleted()) { $contentReleaseLogger->info('Content release completed; so we terminate ourselves gracefully.'); yield ExitEvent::createWithStatusCode(0); return; } - $enumeratedNode = $this->redisRenderingQueue->fetchAndReserveNextRenderingJob($contentReleaseIdentifier, $rendererIdentifier); + $enumeratedNode = $this->redisRenderingQueue->fetchAndReserveNextRenderingJob( + $contentReleaseIdentifier, + $rendererIdentifier + ); if ($enumeratedNode === null) { yield QueueEmptyEvent::create(); // the queue is currently empty, but this does not necessarily mean that rendering is finished. Maybe the NodeRenderOrchestrator is still // determining what needs to be done. We just need to wait a bit and retry. - $contentReleaseLogger->debug('Rendering queue currently empty; we wait a bit see if there is work for us.'); + $contentReleaseLogger->debug( + 'Rendering queue currently empty; we wait a bit see if there is work for us.' + ); sleep(2); $this->concurrentBuildLockService->assertNoOtherContentReleaseWasStarted($contentReleaseIdentifier); continue; @@ -176,23 +185,36 @@ public function render(ContentReleaseIdentifier $contentReleaseIdentifier, Conte // is fully deterministic). This happened 12/2022 to us. $this->persistenceManager->persistAll(); } finally { - $removalSuccess = $this->redisRenderingQueue->removeRenderingJobFromReservedList($contentReleaseIdentifier, $enumeratedNode, $rendererIdentifier); + $removalSuccess = $this->redisRenderingQueue->removeRenderingJobFromReservedList( + $contentReleaseIdentifier, + $enumeratedNode, + $rendererIdentifier + ); if ($removalSuccess === false) { - $contentReleaseLogger->warn('Node could not be removed from reserved-list, because it was claimed by some other worker in the meantime. We don not know yet how this case might happen.', [ - 'node' => $enumeratedNode->debugString(), - ]); + $contentReleaseLogger->warn( + 'Node could not be removed from reserved-list, because it was claimed by some other worker in the meantime. We don not know yet how this case might happen.', + [ + 'node' => $enumeratedNode->debugString() + ] + ); } } yield DocumentRenderedEvent::create(); $i++; - if (static::CHECK_FOR_CONCURRENT_RELEASES_RENDER_COUNT > 0 && $i % static::CHECK_FOR_CONCURRENT_RELEASES_RENDER_COUNT === 0) { + if ( + static::CHECK_FOR_CONCURRENT_RELEASES_RENDER_COUNT > 0 + && ( $i % static::CHECK_FOR_CONCURRENT_RELEASES_RENDER_COUNT ) === 0 + ) { $this->concurrentBuildLockService->assertNoOtherContentReleaseWasStarted($contentReleaseIdentifier); } - if ($i % static::RESTART_AFTER_RENDER_COUNT === 0) { - $contentReleaseLogger->info(sprintf('Restarting after %d renders.', static::RESTART_AFTER_RENDER_COUNT)); + if (( $i % static::RESTART_AFTER_RENDER_COUNT ) === 0) { + $contentReleaseLogger->info(sprintf( + 'Restarting after %d renders.', + static::RESTART_AFTER_RENDER_COUNT + )); yield ExitEvent::createWithStatusCode(193); return; } @@ -223,8 +245,7 @@ protected function renderDocumentNodeVariant( ContentReleaseIdentifier $contentReleaseIdentifier, ContentReleaseLogger $contentReleaseLogger, int $renderingAttempt = 1 - ) - { + ) { $nodeWasFound = false; try { $node = $this->fetchRenderableNode($enumeratedNode); @@ -244,34 +265,59 @@ protected function renderDocumentNodeVariant( 'nodeIdentifier' => $node->getIdentifier(), 'workspaceName' => $enumeratedNode->getWorkspaceNameFromContextPath(), 'dimensions' => $enumeratedNode->getDimensionsFromContextPath(), - 'arguments' => $enumeratedNode->getArguments(), + 'arguments' => $enumeratedNode->getArguments() ]); - $this->nodeRenderingExtensionManager->renderDocumentNodeVariant($node, $enumeratedNode, $contentReleaseLogger); + $this->nodeRenderingExtensionManager->renderDocumentNodeVariant( + $node, + $enumeratedNode, + $contentReleaseLogger + ); } + // NOTE: we do not abort rendering directly, when we encounter any error, but we try to render // all pages in the full iteration (and then, if errors exist, we stop). // This is to gain better visibility into all errors currently happening; and thus maybe being able to see // patterns among the errors. } catch (\Neos\Flow\Property\Exception $exception) { - $contentReleaseLogger->logException($exception->getPrevious(), 'Exception getting document node variant for rendering', array( - 'node' => $enumeratedNode->debugString(), - )); + $contentReleaseLogger->logException( + $exception->getPrevious(), + 'Exception getting document node variant for rendering', + array( + 'node' => $enumeratedNode->debugString() + ) + ); - $this->redisRenderingErrorManager->registerRenderingError($contentReleaseIdentifier, ['node' => $enumeratedNode->debugString()], $exception->getPrevious()); + $this->redisRenderingErrorManager->registerRenderingError( + $contentReleaseIdentifier, + ['node' => $enumeratedNode->debugString()], + $exception->getPrevious() + ); } catch (RenderingException $exception) { - $contentReleaseLogger->logException($exception->getPrevious(), 'Exception while rendering document node variant', array( - 'node' => $enumeratedNode->debugString(), - 'nodeUri' => $exception->getNodeUri() - )); + $contentReleaseLogger->logException( + $exception->getPrevious(), + 'Exception while rendering document node variant', + array( + 'node' => $enumeratedNode->debugString(), + 'nodeUri' => $exception->getNodeUri() + ) + ); - $this->redisRenderingErrorManager->registerRenderingError($contentReleaseIdentifier, ['node' => $enumeratedNode->debugString(), 'nodeUri' => $exception->getNodeUri()], $exception->getPrevious()); + $this->redisRenderingErrorManager->registerRenderingError( + $contentReleaseIdentifier, + ['node' => $enumeratedNode->debugString(), 'nodeUri' => $exception->getNodeUri()], + $exception->getPrevious() + ); } catch (\Exception $exception) { $contentReleaseLogger->logException($exception, 'Exception while rendering document node variant', array( 'node' => $enumeratedNode->debugString() )); - $this->redisRenderingErrorManager->registerRenderingError($contentReleaseIdentifier, ['node' => $enumeratedNode->debugString()], $exception); + $this->redisRenderingErrorManager->registerRenderingError( + $contentReleaseIdentifier, + ['node' => $enumeratedNode->debugString()], + $exception + ); } if (!$nodeWasFound) { @@ -281,7 +327,13 @@ protected function renderDocumentNodeVariant( // // NOTE: we do not directly abort (via exit(1)) the pipeline, as we want to get a list of all missing pages (and not just the first one). // Thus, we simply register a rendering error and ensure the next release will start after this one.s - $this->redisRenderingErrorManager->registerRenderingError($contentReleaseIdentifier, ['node' => $enumeratedNode->debugString()], new \Exception('We could not load a node which was part of the enumeration. At this point, the content release will definitely fail with no further possibility of recovery. Thus, we are exiting the rendering with an error')); + $this->redisRenderingErrorManager->registerRenderingError( + $contentReleaseIdentifier, + ['node' => $enumeratedNode->debugString()], + new \Exception( + 'We could not load a node which was part of the enumeration. At this point, the content release will definitely fail with no further possibility of recovery. Thus, we are exiting the rendering with an error' + ) + ); $this->contentReleaseManager->startIncrementalContentRelease(); } } @@ -319,7 +371,7 @@ private function flushContentCacheForNode( $flushedEntriesCount ), [ - 'node' => $enumeratedNode->debugString(), + 'node' => $enumeratedNode->debugString() ] ); } diff --git a/Classes/NodeRendering/NodeRenderingUriService.php b/Classes/NodeRendering/NodeRenderingUriService.php index 40563d7..9b50573 100644 --- a/Classes/NodeRendering/NodeRenderingUriService.php +++ b/Classes/NodeRendering/NodeRenderingUriService.php @@ -4,11 +4,11 @@ namespace Flowpack\DecoupledContentStore\NodeRendering; -use Neos\Flow\Annotations as Flow; use Flowpack\DecoupledContentStore\Exception; use Flowpack\DecoupledContentStore\NodeRendering\Extensibility\DocumentRendererInterface; use GuzzleHttp\Psr7\ServerRequest; use Neos\ContentRepository\Domain\Model\NodeInterface; +use Neos\Flow\Annotations as Flow; use Neos\Flow\Configuration\ConfigurationManager; use Neos\Flow\Http\BaseUriProvider; use Neos\Flow\Http\Helper\RequestInformationHelper; @@ -57,19 +57,41 @@ public function buildNodeUri(NodeInterface $node, array $arguments): string /** @var Site $currentSite */ $currentSite = $node->getContext()->getCurrentSite(); if (!$currentSite->hasActiveDomains()) { - throw new Exception(sprintf("Site %s has no active domain", $currentSite->getNodeName()), 1666684522); + throw new Exception(sprintf('Site %s has no active domain', $currentSite->getNodeName()), 1666684522); } $primaryDomain = $currentSite->getPrimaryDomain(); - if ((string)$primaryDomain->getScheme() === '') { - throw new Exception(sprintf("Domain %s for site %s has no scheme defined", $primaryDomain->getHostname(), $currentSite->getNodeName()), 1666684523); + if ((string) $primaryDomain->getScheme() === '') { + throw new Exception( + sprintf( + 'Domain %s for site %s has no scheme defined', + $primaryDomain->getHostname(), + $currentSite->getNodeName() + ), + 1666684523 + ); } // HINT: We cannot use a static URL here, but instead need to use an URL of the current site. // This is changed from the the old behavior, where we have changed the LinkingService in LinkingServiceAspect, // to properly generate the domain part of the routes - and this relies on the proper ControllerContext URI path. - $baseControllerContext = $this->buildControllerContextAndSetBaseUri($primaryDomain->__toString(), $node, $arguments); + $baseControllerContext = $this->buildControllerContextAndSetBaseUri( + $primaryDomain->__toString(), + $node, + $arguments + ); $format = $arguments['@format'] ?? 'html'; - $uri = $this->linkingService->createNodeUri($baseControllerContext, $node, null, $format, true, $arguments, '', false, [], false); + $uri = $this->linkingService->createNodeUri( + $baseControllerContext, + $node, + null, + $format, + true, + $arguments, + '', + false, + [], + false + ); return self::removeQueryPartFromUri($uri); } @@ -95,25 +117,18 @@ public function buildControllerContextAndSetBaseUri(string $uri, NodeInterface $ $this->securityContext->setRequest($request); $uriBuilder = $this->uriBuilderForRequest($request); - return new ControllerContext( - $request, - new ActionResponse(), - new Arguments([]), - $uriBuilder - ); + return new ControllerContext($request, new ActionResponse(), new Arguments([]), $uriBuilder); } - /** - * @param string $uri - * @param NodeInterface $node - * @return ActionRequest - */ - protected function buildFakeRequest($uri, NodeInterface $node): ActionRequest + private function buildFakeRequest(string $uri, NodeInterface $node): ActionRequest { $_SERVER['FLOW_REWRITEURLS'] = '1'; $httpRequest = new ServerRequest('GET', $uri); - $routingParameters = RouteParameters::createEmpty()->withParameter('requestUriHost', $httpRequest->getUri()->getHost()); + $routingParameters = RouteParameters::createEmpty()->withParameter( + 'requestUriHost', + $httpRequest->getUri()->getHost() + ); $httpRequest = $httpRequest->withAttribute(ServerRequestAttributes::ROUTING_PARAMETERS, $routingParameters); $request = ActionRequest::fromHttpRequest($httpRequest); @@ -125,13 +140,12 @@ protected function buildFakeRequest($uri, NodeInterface $node): ActionRequest return $request; } - /** * @param ActionRequest $request * @return UriBuilder * @throws \Neos\Utility\Exception\PropertyNotAccessibleException */ - protected function uriBuilderForRequest(ActionRequest $request): UriBuilder + private function uriBuilderForRequest(ActionRequest $request): UriBuilder { $uriBuilder = new UriBuilder(); $uriBuilder->setRequest($request); @@ -144,12 +158,7 @@ protected function uriBuilderForRequest(ActionRequest $request): UriBuilder return $uriBuilder; } - - /** - * @param string $uri - * @return string - */ - protected static function removeQueryPartFromUri($uri) + private static function removeQueryPartFromUri(string $uri): string { $uriData = explode('?', $uri); diff --git a/Classes/NodeRendering/ProcessEvents/DocumentRenderedEvent.php b/Classes/NodeRendering/ProcessEvents/DocumentRenderedEvent.php index 4acc988..0ada521 100644 --- a/Classes/NodeRendering/ProcessEvents/DocumentRenderedEvent.php +++ b/Classes/NodeRendering/ProcessEvents/DocumentRenderedEvent.php @@ -1,4 +1,5 @@ statusCode; } -} \ No newline at end of file +} diff --git a/Classes/NodeRendering/ProcessEvents/QueueEmptyEvent.php b/Classes/NodeRendering/ProcessEvents/QueueEmptyEvent.php index b1eb202..ade51ee 100644 --- a/Classes/NodeRendering/ProcessEvents/QueueEmptyEvent.php +++ b/Classes/NodeRendering/ProcessEvents/QueueEmptyEvent.php @@ -1,4 +1,5 @@ controllerContext passed in, // !!*WHICH CHANGES FOR EVERY DOCUMENT*!! - $this->fusionRuntimePerSiteNode[$currentSiteNodeContextPath] = new Runtime($fusionObjectTree, $this->controllerContext); + $this->fusionRuntimePerSiteNode[$currentSiteNodeContextPath] = new Runtime( + $fusionObjectTree, + $this->controllerContext + ); } $this->fusionRuntime = $this->fusionRuntimePerSiteNode[$currentSiteNodeContextPath]; @@ -151,7 +156,10 @@ protected function getFusionRuntime(TraversableNodeInterface $currentSiteNode) // technically, we do NOT need to replace the RuntimeContentCache (it works as well without the next line) // but I felt this would be an additional safeguard against problems with the cache (e.g. content leaking through dimensions or pages) - $this->runtimeContentCacheAccessor->setValue($this->fusionRuntime, new RuntimeContentCache($this->fusionRuntime)); + $this->runtimeContentCacheAccessor->setValue( + $this->fusionRuntime, + new RuntimeContentCache($this->fusionRuntime) + ); // after replacing the RuntimeContentCache, we again need to enable the content cache explicitly. // Otherwise, we do not get any contents into the content store. diff --git a/Classes/NodeRendering/Render/DocumentRenderer.php b/Classes/NodeRendering/Render/DocumentRenderer.php index 3a1513c..799b04d 100644 --- a/Classes/NodeRendering/Render/DocumentRenderer.php +++ b/Classes/NodeRendering/Render/DocumentRenderer.php @@ -1,31 +1,21 @@ cacheUrlMappingAspect->beforeDocumentRendering($contentReleaseLogger); $nodeUri = $this->nodeRenderingUriService->buildNodeUri($node, $arguments); @@ -93,13 +86,18 @@ public function renderDocumentNodeVariant(NodeInterface $node, array $arguments, $arguments['node'] = $node->getContextPath(); return $this->renderDocumentView($node, $nodeUri, $arguments, $contentReleaseLogger); } catch (\Exception $exception) { - throw new Exception\RenderingException('Error rendering document view', $node, $nodeUri, 1491378709, $exception); + throw new Exception\RenderingException( + 'Error rendering document view', + $node, + $nodeUri, + 1491378709, + $exception + ); } finally { $this->cacheUrlMappingAspect->afterDocumentRendering(); } } - /** * Render the view of a document node * @@ -112,8 +110,12 @@ public function renderDocumentNodeVariant(NodeInterface $node, array $arguments, * @return string the rendered output * @throws Exception\InvalidSiteConfigurationException */ - protected function renderDocumentView(NodeInterface $node, $uri, array $requestArguments, ContentReleaseLogger $contentReleaseLogger): string - { + protected function renderDocumentView( + NodeInterface $node, + $uri, + array $requestArguments, + ContentReleaseLogger $contentReleaseLogger + ): string { $this->isRendering = true; try { @@ -121,23 +123,31 @@ protected function renderDocumentView(NodeInterface $node, $uri, array $requestA $contentContext = $node->getContext(); $site = $contentContext->getCurrentSite(); $domain = $site->getFirstActiveDomain(); - $baseUri = (string)$domain; + $baseUri = (string) $domain; if ($baseUri === '') { throw new Exception\InvalidSiteConfigurationException( - 'Cannot render content without active domain for site "' . $site->getName() . '"', 1467289645 + 'Cannot render content without active domain for site "' . $site->getName() . '"', + 1467289645 ); } $contentReleaseLogger->info('Rendering document for URI ' . $uri, ['baseUri' => $baseUri]); - $controllerContext = $this->nodeRenderingUriService->buildControllerContextAndSetBaseUri($uri, $node, $requestArguments); + $controllerContext = $this->nodeRenderingUriService->buildControllerContextAndSetBaseUri( + $uri, + $node, + $requestArguments + ); /** @var ActionRequest $request */ $request = $controllerContext->getRequest(); $request->setArguments($requestArguments); $resourceBaseUri = $this->useRelativeResourceUris ? '' : $baseUri; - MultisiteFileSystemSymlinkTarget::injectBaseUriIntoRelevantResourcePublishingTargets($resourceBaseUri, $this->resourceManager); + MultisiteFileSystemSymlinkTarget::injectBaseUriIntoRelevantResourcePublishingTargets( + $resourceBaseUri, + $this->resourceManager + ); $this->fusionView->setFusionPath('documentRendering'); $this->fusionView->setControllerContext($controllerContext); @@ -146,7 +156,10 @@ protected function renderDocumentView(NodeInterface $node, $uri, array $requestA $output = $this->fusionView->render(); if ($this->addHttpMessage) { if ($output instanceof ResponseInterface) { - $output = implode("\r\n", ResponseInformationHelper::prepareHeaders($output)) . "\r\n" . $output->getBody()->getContents(); + $output = + implode("\r\n", ResponseInformationHelper::prepareHeaders($output)) + . "\r\n" + . $output->getBody()->getContents(); } else { $output = self::wrapInHttpMessage($output, $controllerContext->getResponse()); } @@ -173,12 +186,12 @@ private static function wrapInHttpMessage(string $output, ActionResponse $respon $headerLines = []; foreach ($response->buildHttpResponse()->getHeaders() as $name => $values) { foreach ($values as $value) { - $headerLines[] = $name . ": " . $value; + $headerLines[] = $name . ': ' . $value; } } // Finally, we build the HTTP response. - return "HTTP/1.1" . (empty($headerLines) ? "\r\n" : implode("\r\n", $headerLines)) . "\r\n" . $output; + return 'HTTP/1.1' . ( empty($headerLines) ? "\r\n" : implode("\r\n", $headerLines) ) . "\r\n" . $output; } /** diff --git a/Classes/NodeRendering/Render/ExtractedExceptionDto.php b/Classes/NodeRendering/Render/ExtractedExceptionDto.php index 0399352..b88ba49 100644 --- a/Classes/NodeRendering/Render/ExtractedExceptionDto.php +++ b/Classes/NodeRendering/Render/ExtractedExceptionDto.php @@ -1,9 +1,11 @@ getMessage() . (!empty($this->getStackTrace()) ? "\n{$this->getStackTrace()}" : '') . (!empty($this->getReferenceCode()) ? "\n(reference code {$this->getReferenceCode()})" : ''); + return ( + $this->getMessage() + . ( !empty($this->getStackTrace()) ? "\n{$this->getStackTrace()}" : '' ) + . ( !empty($this->getReferenceCode()) ? "\n(reference code {$this->getReferenceCode()})" : '' ) + ); } - } diff --git a/Classes/NodeRendering/Render/NodeContextCombinator.php b/Classes/NodeRendering/Render/NodeContextCombinator.php index e9a6646..a892324 100644 --- a/Classes/NodeRendering/Render/NodeContextCombinator.php +++ b/Classes/NodeRendering/Render/NodeContextCombinator.php @@ -1,14 +1,16 @@ dimensionPresetSource->getAllPresets(); if ($presets === []) { $contentContext = $this->contextFactory->create(array( - 'currentSite' => $site, - 'workspaceName' => 'live', - 'dimensions' => [], - 'targetDimensions' => [] - )); + 'currentSite' => $site, + 'workspaceName' => 'live', + 'dimensions' => [], + 'targetDimensions' => [] + )); $siteNode = $contentContext->getNode('/sites/' . $site->getNodeName()); @@ -127,5 +131,4 @@ public function recurseDocumentChildNodes(NodeInterface $node) } } } - -} \ No newline at end of file +} diff --git a/Classes/NodeRendering/Render/RenderExceptionExtractor.php b/Classes/NodeRendering/Render/RenderExceptionExtractor.php index 181d820..2627398 100644 --- a/Classes/NodeRendering/Render/RenderExceptionExtractor.php +++ b/Classes/NodeRendering/Render/RenderExceptionExtractor.php @@ -1,9 +1,11 @@ \s* @@ -44,12 +46,15 @@ class RenderExceptionExtractor public static function extractRenderingException($content) { if ( - preg_match(self::HTML_MESSAGE_HANDLER_PATTERN, $content, $matches) || - preg_match(self::XML_COMMENT_HANDLER_PATTERN, $content, $matches) + preg_match(self::HTML_MESSAGE_HANDLER_PATTERN, $content, $matches) + || preg_match(self::XML_COMMENT_HANDLER_PATTERN, $content, $matches) ) { - return new ExtractedExceptionDto($matches['message'], $matches['stackTrace'], $matches['referenceCode'] ?? ''); + return new ExtractedExceptionDto( + $matches['message'], + $matches['stackTrace'], + $matches['referenceCode'] ?? '' + ); } return null; } - } diff --git a/Classes/Package.php b/Classes/Package.php index 459c3ac..0860414 100644 --- a/Classes/Package.php +++ b/Classes/Package.php @@ -1,4 +1,7 @@ getSignalSlotDispatcher(); - $dispatcher->connect(Workspace::class, 'afterNodePublishing', - IncrementalContentReleaseHandler::class, 'nodePublished'); + $dispatcher->connect( + Workspace::class, + 'afterNodePublishing', + IncrementalContentReleaseHandler::class, + 'nodePublished' + ); // NASTY WORKAROUND - explanation follows. // @@ -29,9 +35,16 @@ public function boot(Bootstrap $bootstrap) // keeping the old behavior as before. // // In our case, we want to ONLY listen to web requests, ignoring CLI requests. Thus, we check for the type of the Controller, which is CommandControllerInterface for CLI; and ControllerInterface for web. - $dispatcher->connect('Neos\Flow\Mvc\Dispatcher', 'afterControllerInvocation', function($request, $response, $controller) use ($bootstrap) { + $dispatcher->connect('Neos\Flow\Mvc\Dispatcher', 'afterControllerInvocation', function ( + $request, + $response, + $controller + ) use ($bootstrap) { if ($controller instanceof ControllerInterface) { - $bootstrap->getObjectManager()->get(IncrementalContentReleaseHandler::class)->startContentReleaseIfNodesWerePublishedBefore(); + $bootstrap + ->getObjectManager() + ->get(IncrementalContentReleaseHandler::class) + ->startContentReleaseIfNodesWerePublishedBefore(); } }); } diff --git a/Classes/PrepareContentRelease/Dto/ContentReleaseMetadata.php b/Classes/PrepareContentRelease/Dto/ContentReleaseMetadata.php index 0561c76..6202af1 100644 --- a/Classes/PrepareContentRelease/Dto/ContentReleaseMetadata.php +++ b/Classes/PrepareContentRelease/Dto/ContentReleaseMetadata.php @@ -1,4 +1,5 @@ prunnerJobId = $prunnerJobId; $this->startTime = $startTime; $this->endTime = $endTime; @@ -71,9 +70,22 @@ private function __construct( $this->contentReleaseSize = $contentReleaseSize; } - public static function create(PrunnerJobId $prunnerJobId, \DateTimeInterface $startTime, string $workspace = 'live', string $accountId = 'cli'): self - { - return new self($prunnerJobId, $startTime, null, null, NodeRenderingCompletionStatus::scheduled(), [], $workspace, $accountId); + public static function create( + PrunnerJobId $prunnerJobId, + \DateTimeInterface $startTime, + string $workspace = 'live', + string $accountId = 'cli' + ): self { + return new self( + $prunnerJobId, + $startTime, + null, + null, + NodeRenderingCompletionStatus::scheduled(), + [], + $workspace, + $accountId + ); } public static function fromJsonString($metadataEncoded, ContentReleaseIdentifier $contentReleaseIdentifier): self @@ -88,20 +100,25 @@ public static function fromJsonString($metadataEncoded, ContentReleaseIdentifier return new self( PrunnerJobId::fromString($tmp['prunnerJobId']), - ($tmp['startTime'] !== null) ? \DateTimeImmutable::createFromFormat(\DateTime::RFC3339_EXTENDED, $tmp['startTime']) : null, - ($tmp['endTime'] !== null) ? \DateTimeImmutable::createFromFormat(\DateTime::RFC3339_EXTENDED, $tmp['endTime']) : null, - ($tmp['switchTime'] !== null) ? \DateTimeImmutable::createFromFormat(\DateTime::RFC3339_EXTENDED, $tmp['switchTime']) : null, + $tmp['startTime'] !== null + ? \DateTimeImmutable::createFromFormat(\DateTime::RFC3339_EXTENDED, $tmp['startTime']) + : null, + $tmp['endTime'] !== null + ? \DateTimeImmutable::createFromFormat(\DateTime::RFC3339_EXTENDED, $tmp['endTime']) + : null, + $tmp['switchTime'] !== null + ? \DateTimeImmutable::createFromFormat(\DateTime::RFC3339_EXTENDED, $tmp['switchTime']) + : null, NodeRenderingCompletionStatus::fromString($tmp['status']), isset($tmp['manualTransferJobIds']) ? array_map(function (string $item) { - return PrunnerJobId::fromString($item); - }, json_decode($tmp['manualTransferJobIds'])) : [], + return PrunnerJobId::fromString($item); + }, json_decode($tmp['manualTransferJobIds'])) : [], $tmp['workspaceName'] ?? 'live', key_exists('accountId', $tmp) ? $tmp['accountId'] : 'cli', - isset($tmp['contentReleaseSize']) ? (float)$tmp['contentReleaseSize'] : null, + isset($tmp['contentReleaseSize']) ? (float) $tmp['contentReleaseSize'] : null ); } - public function jsonSerialize(): array { return [ @@ -113,35 +130,85 @@ public function jsonSerialize(): array 'manualTransferJobIds' => json_encode($this->manualTransferJobIds), 'workspaceName' => $this->workspaceName, 'accountId' => $this->accountId, - 'contentReleaseSize' => $this->contentReleaseSize, + 'contentReleaseSize' => $this->contentReleaseSize ]; } public function withEndTime(\DateTimeInterface $endTime): self { - return new self($this->prunnerJobId, $this->startTime, $endTime, $this->switchTime, $this->status, $this->manualTransferJobIds, $this->workspaceName, $this->accountId, $this->contentReleaseSize); + return new self( + $this->prunnerJobId, + $this->startTime, + $endTime, + $this->switchTime, + $this->status, + $this->manualTransferJobIds, + $this->workspaceName, + $this->accountId, + $this->contentReleaseSize + ); } public function withSwitchTime(\DateTimeInterface $switchTime): self { - return new self($this->prunnerJobId, $this->startTime, $this->endTime, $switchTime, $this->status, $this->manualTransferJobIds, $this->workspaceName, $this->accountId, $this->contentReleaseSize); + return new self( + $this->prunnerJobId, + $this->startTime, + $this->endTime, + $switchTime, + $this->status, + $this->manualTransferJobIds, + $this->workspaceName, + $this->accountId, + $this->contentReleaseSize + ); } public function withStatus(NodeRenderingCompletionStatus $status): self { - return new self($this->prunnerJobId, $this->startTime, $this->endTime, $this->switchTime, $status, $this->manualTransferJobIds, $this->workspaceName, $this->accountId, $this->contentReleaseSize); + return new self( + $this->prunnerJobId, + $this->startTime, + $this->endTime, + $this->switchTime, + $status, + $this->manualTransferJobIds, + $this->workspaceName, + $this->accountId, + $this->contentReleaseSize + ); } public function withAdditionalManualTransferJobId(PrunnerJobId $prunnerJobId): self { $manualTransferIdArray = $this->getManualTransferJobIds(); $manualTransferIdArray[] = $prunnerJobId; - return new self($this->prunnerJobId, $this->startTime, $this->endTime, $this->switchTime, $this->status, $manualTransferIdArray, $this->workspaceName, $this->accountId, $this->contentReleaseSize); + return new self( + $this->prunnerJobId, + $this->startTime, + $this->endTime, + $this->switchTime, + $this->status, + $manualTransferIdArray, + $this->workspaceName, + $this->accountId, + $this->contentReleaseSize + ); } public function withContentReleaseSize(float $contentReleaseSize): self { - return new self($this->prunnerJobId, $this->startTime, $this->endTime, $this->switchTime, $this->status, $this->manualTransferJobIds, $this->workspaceName, $this->accountId, $contentReleaseSize); + return new self( + $this->prunnerJobId, + $this->startTime, + $this->endTime, + $this->switchTime, + $this->status, + $this->manualTransferJobIds, + $this->workspaceName, + $this->accountId, + $contentReleaseSize + ); } public function getPrunnerJobId(): PrunnerJobId @@ -194,5 +261,4 @@ public function getContentReleaseSize(): ?float { return $this->contentReleaseSize; } - } diff --git a/Classes/PrepareContentRelease/Infrastructure/RedisContentReleaseService.php b/Classes/PrepareContentRelease/Infrastructure/RedisContentReleaseService.php index fd023dc..2504b33 100644 --- a/Classes/PrepareContentRelease/Infrastructure/RedisContentReleaseService.php +++ b/Classes/PrepareContentRelease/Infrastructure/RedisContentReleaseService.php @@ -1,4 +1,5 @@ redisClientManager->getPrimaryRedis(); // Check there is no existing release with the same identifier - $existingRelease = $redis->get($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'meta:info')); + $existingRelease = $redis->get($this->redisKeyService->getRedisKeyForPostfix( + $contentReleaseIdentifier, + 'meta:info' + )); if ($existingRelease) { - $contentReleaseLogger->error(sprintf('Content Release "%s" already exists', $contentReleaseIdentifier->getIdentifier())); - throw new \RuntimeException(sprintf('Content Release "%s" already exists, cannot create a release with the same identifier', $contentReleaseIdentifier->getIdentifier()), 1689750292); + $contentReleaseLogger->error(sprintf( + 'Content Release "%s" already exists', + $contentReleaseIdentifier->getIdentifier() + )); + throw new \RuntimeException( + sprintf( + 'Content Release "%s" already exists, cannot create a release with the same identifier', + $contentReleaseIdentifier->getIdentifier() + ), + 1689750292 + ); } $metadata = ContentReleaseMetadata::create($prunnerJobId, new \DateTimeImmutable(), $workspaceName, $accountId); @@ -54,28 +71,50 @@ public function createContentRelease(ContentReleaseIdentifier $contentReleaseIde $redis->multi(); try { $redis->zAdd('contentStore:registeredReleases', 0, $contentReleaseIdentifier->getIdentifier()); - $redis->set($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'meta:info'), json_encode($metadata)); + $redis->set( + $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'meta:info'), + json_encode($metadata) + ); $redis->exec(); } catch (\Exception $e) { $redis->discard(); throw $e; } - $contentReleaseLogger->info(sprintf('Registered Content Release %s', $contentReleaseIdentifier->getIdentifier()), [ - 'metadata' => $metadata - ]); + $contentReleaseLogger->info( + sprintf('Registered Content Release %s', $contentReleaseIdentifier->getIdentifier()), + [ + 'metadata' => $metadata + ] + ); } - public function setContentReleaseMetadata(ContentReleaseIdentifier $contentReleaseIdentifier, ContentReleaseMetadata $metadata, RedisInstanceIdentifier $redisInstanceIdentifier): void - { - $this->redisClientManager->getRedis($redisInstanceIdentifier)->set($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'meta:info'), json_encode($metadata)); + public function setContentReleaseMetadata( + ContentReleaseIdentifier $contentReleaseIdentifier, + ContentReleaseMetadata $metadata, + RedisInstanceIdentifier $redisInstanceIdentifier + ): void { + $this->redisClientManager->getRedis($redisInstanceIdentifier)->set( + $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'meta:info'), + json_encode($metadata) + ); } - public function registerManualTransferJob(ContentReleaseIdentifier $contentReleaseIdentifier, PrunnerJobId $prunnerJobId, ContentReleaseLogger $contentReleaseLogger): void - { + public function registerManualTransferJob( + ContentReleaseIdentifier $contentReleaseIdentifier, + PrunnerJobId $prunnerJobId, + ContentReleaseLogger $contentReleaseLogger + ): void { $releaseMetadata = $this->fetchMetadataForContentRelease($contentReleaseIdentifier); - $this->setContentReleaseMetadata($contentReleaseIdentifier, $releaseMetadata->withAdditionalManualTransferJobId($prunnerJobId), RedisInstanceIdentifier::primary()); - - $contentReleaseLogger->info(sprintf('Register new pipeline for release %s', $contentReleaseIdentifier->getIdentifier())); + $this->setContentReleaseMetadata( + $contentReleaseIdentifier, + $releaseMetadata->withAdditionalManualTransferJobId($prunnerJobId), + RedisInstanceIdentifier::primary() + ); + + $contentReleaseLogger->info(sprintf( + 'Register new pipeline for release %s', + $contentReleaseIdentifier->getIdentifier() + )); } /** @@ -94,19 +133,26 @@ public function fetchAllReleaseIds(RedisInstanceIdentifier $redisInstanceIdentif return $result; } - public function fetchMetadataForContentRelease(ContentReleaseIdentifier $contentReleaseIdentifier, ?RedisInstanceIdentifier $redisInstanceIdentifier = null): ?ContentReleaseMetadata - { + public function fetchMetadataForContentRelease( + ContentReleaseIdentifier $contentReleaseIdentifier, + ?RedisInstanceIdentifier $redisInstanceIdentifier = null + ): ?ContentReleaseMetadata { $redisInstanceIdentifier = $redisInstanceIdentifier ?: RedisInstanceIdentifier::primary(); $redis = $this->redisClientManager->getRedis($redisInstanceIdentifier); - $metadataEncoded = $redis->get($this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, 'meta:info')); + $metadataEncoded = $redis->get($this->redisKeyService->getRedisKeyForPostfix( + $contentReleaseIdentifier, + 'meta:info' + )); if (!$metadataEncoded) { return null; } return ContentReleaseMetadata::fromJsonString($metadataEncoded, $contentReleaseIdentifier); } - public function fetchMetadataForContentReleases(RedisInstanceIdentifier $redisInstanceIdentifier, ContentReleaseIdentifier ...$releaseIdentifiers): ContentReleaseBatchResult - { + public function fetchMetadataForContentReleases( + RedisInstanceIdentifier $redisInstanceIdentifier, + ContentReleaseIdentifier ...$releaseIdentifiers + ): ContentReleaseBatchResult { $redis = $this->redisClientManager->getRedis($redisInstanceIdentifier); $result = []; // KEY == contentReleaseIdentifier. VALUE == enumerated count foreach (GeneratorUtility::createArrayBatch($releaseIdentifiers, 50) as $batchedReleaseIdentifiers) { @@ -116,10 +162,11 @@ public function fetchMetadataForContentReleases(RedisInstanceIdentifier $redisIn } $res = $redisPipeline->exec(); foreach ($batchedReleaseIdentifiers as $i => $releaseIdentifier) { - $result[(string)$releaseIdentifier] = $res[$i] ? ContentReleaseMetadata::fromJsonString($res[$i], $releaseIdentifier) : null; + $result[(string) $releaseIdentifier] = $res[$i] + ? ContentReleaseMetadata::fromJsonString($res[$i], $releaseIdentifier) + : null; } } return ContentReleaseBatchResult::createFromArray($result); } - } diff --git a/Classes/ReleaseSwitch/Infrastructure/RedisReleaseSwitchService.php b/Classes/ReleaseSwitch/Infrastructure/RedisReleaseSwitchService.php index 9035697..4b9d272 100644 --- a/Classes/ReleaseSwitch/Infrastructure/RedisReleaseSwitchService.php +++ b/Classes/ReleaseSwitch/Infrastructure/RedisReleaseSwitchService.php @@ -1,22 +1,23 @@ redisClient->getRedis($redisInstanceIdentifier); $current = $redis->get('contentStore:current'); // validation checks // we don't check for errors here (again) as we do not reach this stage if there were errors before - if (!in_array($contentReleaseIdentifier->getIdentifier(), $redis->zRevRangeByLex('contentStore:registeredReleases', '+', '-'))) { - $contentReleaseLogger->error('Content release identifier ' . $contentReleaseIdentifier->getIdentifier() . ' is not listed in current releases thus we do not switch.'); + if (!in_array($contentReleaseIdentifier->getIdentifier(), $redis->zRevRangeByLex( + 'contentStore:registeredReleases', + '+', + '-' + ))) { + $contentReleaseLogger->error( + 'Content release identifier ' + . $contentReleaseIdentifier->getIdentifier() + . ' is not listed in current releases thus we do not switch.' + ); return; } @@ -63,9 +75,14 @@ public function switchContentRelease(RedisInstanceIdentifier $redisInstanceIdent $hasError = false; foreach ($redisKeyPostfixesForEachRelease->getRequiredKeys() as $requiredPostfix) { if ($requiredPostfix->shouldTransfer($redisInstanceIdentifier)) { - $key = $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, $requiredPostfix->getRedisKeyPostfix()); + $key = $this->redisKeyService->getRedisKeyForPostfix( + $contentReleaseIdentifier, + $requiredPostfix->getRedisKeyPostfix() + ); if (!$redis->exists($key)) { - $contentReleaseLogger->error('Required redis key ' . $key . ' does not exist for release thus we do not switch.'); + $contentReleaseLogger->error( + 'Required redis key ' . $key . ' does not exist for release thus we do not switch.' + ); $hasError = true; } } @@ -79,9 +96,18 @@ public function switchContentRelease(RedisInstanceIdentifier $redisInstanceIdent $redis->set('contentStore:current', $contentReleaseIdentifier->getIdentifier()); $redis->set('contentStore:configEpoch', $this->configEpochSettings['current']); $releaseMetadata = $this->redisContentReleaseService->fetchMetadataForContentRelease($contentReleaseIdentifier); - $this->redisContentReleaseService->setContentReleaseMetadata($contentReleaseIdentifier, $releaseMetadata->withSwitchTime(new \DateTimeImmutable()), $redisInstanceIdentifier); + $this->redisContentReleaseService->setContentReleaseMetadata( + $contentReleaseIdentifier, + $releaseMetadata->withSwitchTime(new \DateTimeImmutable()), + $redisInstanceIdentifier + ); - $contentReleaseLogger->info(sprintf('Switched redis %s from content release %s to %s', $redisInstanceIdentifier->getIdentifier(), $current, $contentReleaseIdentifier->getIdentifier())); + $contentReleaseLogger->info(sprintf( + 'Switched redis %s from content release %s to %s', + $redisInstanceIdentifier->getIdentifier(), + $current, + $contentReleaseIdentifier->getIdentifier() + )); } public function getCurrentRelease(RedisInstanceIdentifier $redisInstanceIdentifier): ?ContentReleaseIdentifier diff --git a/Classes/Transfer/ContentReleaseCleaner.php b/Classes/Transfer/ContentReleaseCleaner.php index 8e2ee47..c97e01a 100644 --- a/Classes/Transfer/ContentReleaseCleaner.php +++ b/Classes/Transfer/ContentReleaseCleaner.php @@ -1,4 +1,5 @@ info('Removing old releases in Redis ' . $redisInstanceIdentifier->getIdentifier() . '. First, checking which releases to keep:'); + public function removeOldReleases( + RedisInstanceIdentifier $redisInstanceIdentifier, + ContentReleaseIdentifier $contentReleaseIdentifierOfUpcomingRelease, + ContentReleaseLogger $contentReleaseLogger + ): void { + $contentReleaseLogger->info( + 'Removing old releases in Redis ' + . $redisInstanceIdentifier->getIdentifier() + . '. First, checking which releases to keep:' + ); $currentRelease = $this->redisReleaseSwitchService->getCurrentRelease($redisInstanceIdentifier); if (!$currentRelease) { - $contentReleaseLogger->error('We did not find a current release in Content Store; so to be safe, we will NOT remove anything.'); + $contentReleaseLogger->error( + 'We did not find a current release in Content Store; so to be safe, we will NOT remove anything.' + ); return; } @@ -70,7 +80,8 @@ public function removeOldReleases(RedisInstanceIdentifier $redisInstanceIdentifi $contentReleasesToKeep = $this->redisClientManager->getRetentionCount($redisInstanceIdentifier); if ($contentReleasesToKeep < 2) { - throw new \RuntimeException('contentReleaseRetentionCount must be at least 2, found: ' . $contentReleasesToKeep); + throw new \RuntimeException('contentReleaseRetentionCount must be at least 2, found: ' + . $contentReleasesToKeep); } $healthyReleaseCounter = 0; @@ -90,14 +101,19 @@ public function removeOldReleases(RedisInstanceIdentifier $redisInstanceIdentifi // In case of errors, we want to keep more "good" content releases ("success" state and no errors), so that we can // more easily switch back to an older release. // -> We accept potential Redis out of memory errors in this case. - if (($this->redisContentReleaseService->fetchMetadataForContentRelease($id)->getStatus()->getStatus() === NodeRenderingCompletionStatus::success()->getStatus() && count($this->redisRenderingErrorManager->getRenderingErrors($id)) === 0)) { + if ( + $this->redisContentReleaseService->fetchMetadataForContentRelease( + $id + )->getStatus()->getStatus() === NodeRenderingCompletionStatus::success()->getStatus() + && count($this->redisRenderingErrorManager->getRenderingErrors($id)) === 0 + ) { $healthyReleaseCounter++; } } // we always want to keep $currentRelease and $contentReleaseIdentifierOfUpcomingRelease; thus // we need to remove 2 from $contentReleasesToKeep - $shouldRemoveRelease = ($healthyReleaseCounter > $contentReleasesToKeep - 2); + $shouldRemoveRelease = $healthyReleaseCounter > ( $contentReleasesToKeep - 2 ); if ($shouldRemoveRelease) { $releasesToRemove[] = $id; @@ -118,14 +134,18 @@ public function removeOldReleases(RedisInstanceIdentifier $redisInstanceIdentifi $contentReleaseLogger->info('Completed.'); } - - public function removeRelease(ContentReleaseIdentifier $contentReleaseIdentifierToRemove, RedisInstanceIdentifier $redisIdentifier, ContentReleaseLogger $contentReleaseLogger) - { + public function removeRelease( + ContentReleaseIdentifier $contentReleaseIdentifierToRemove, + RedisInstanceIdentifier $redisIdentifier, + ContentReleaseLogger $contentReleaseLogger + ) { $redis = $this->redisClientManager->getRedis($redisIdentifier); $currentRelease = $this->redisReleaseSwitchService->getCurrentRelease($redisIdentifier); if (!$currentRelease) { - $contentReleaseLogger->error('We did not find a current release in Content Store; so to be safe, we will NOT remove anything.'); + $contentReleaseLogger->error( + 'We did not find a current release in Content Store; so to be safe, we will NOT remove anything.' + ); return; } @@ -137,7 +157,10 @@ public function removeRelease(ContentReleaseIdentifier $contentReleaseIdentifier $redisKeyPostfixesForEachRelease = RedisKeyPostfixesForEachRelease::fromArray($this->redisKeyPostfixesForEachReleaseConfiguration); foreach ($redisKeyPostfixesForEachRelease->getRedisKeyPostfixes() as $redisKeyPostfix) { - $redisKey = $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifierToRemove, $redisKeyPostfix->getRedisKeyPostfix()); + $redisKey = $this->redisKeyService->getRedisKeyForPostfix( + $contentReleaseIdentifierToRemove, + $redisKeyPostfix->getRedisKeyPostfix() + ); $contentReleaseLogger->debug(' - Removing ' . $redisKey); $redis->del($redisKey); } diff --git a/Classes/Transfer/ContentReleaseSynchronizer.php b/Classes/Transfer/ContentReleaseSynchronizer.php index b0eb704..5e85160 100644 --- a/Classes/Transfer/ContentReleaseSynchronizer.php +++ b/Classes/Transfer/ContentReleaseSynchronizer.php @@ -1,4 +1,5 @@ info('Syncing Content Release ' . $contentReleaseIdentifier->getIdentifier() . ' to target ' . $targetRedisIdentifier->getIdentifier()); + public function syncToTarget( + RedisInstanceIdentifier $targetRedisIdentifier, + ContentReleaseIdentifier $contentReleaseIdentifier, + ContentReleaseLogger $contentReleaseLogger + ): void { + $contentReleaseLogger->info( + 'Syncing Content Release ' . $contentReleaseIdentifier->getIdentifier() . ' to target ' + . $targetRedisIdentifier->getIdentifier() + ); if ($targetRedisIdentifier->isPrimary()) { $contentReleaseLogger->error('Cannot sync to the primary redis (Content Release is already there).'); @@ -49,7 +56,10 @@ public function syncToTarget(RedisInstanceIdentifier $targetRedisIdentifier, Con $redisKeyPostfixesForEachRelease = RedisKeyPostfixesForEachRelease::fromArray($this->redisKeyPostfixesForEachReleaseConfiguration); foreach ($redisKeyPostfixesForEachRelease->getKeysToTransfer($targetRedisIdentifier) as $redisKeyPostfix) { - $redisKey = $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, $redisKeyPostfix->getRedisKeyPostfix()); + $redisKey = $this->redisKeyService->getRedisKeyForPostfix( + $contentReleaseIdentifier, + $redisKeyPostfix->getRedisKeyPostfix() + ); $contentReleaseLogger->info($redisKey); if ($redisKeyPostfix->isRequired() && !$sourceRedis->exists($redisKey)) { $contentReleaseLogger->error('Required key ' . $redisKey . ' does not exist.'); @@ -57,9 +67,25 @@ public function syncToTarget(RedisInstanceIdentifier $targetRedisIdentifier, Con } if ($redisKeyPostfix->hasTransferModeHashIncremental()) { - $this->transferHashKeyIncrementally($sourceRedis, $targetRedis, $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, $redisKeyPostfix->getRedisKeyPostfix()), $contentReleaseLogger); + $this->transferHashKeyIncrementally( + $sourceRedis, + $targetRedis, + $this->redisKeyService->getRedisKeyForPostfix( + $contentReleaseIdentifier, + $redisKeyPostfix->getRedisKeyPostfix() + ), + $contentReleaseLogger + ); } else { - $this->transferKey($sourceRedis, $targetRedis, $this->redisKeyService->getRedisKeyForPostfix($contentReleaseIdentifier, $redisKeyPostfix->getRedisKeyPostfix()), $contentReleaseLogger); + $this->transferKey( + $sourceRedis, + $targetRedis, + $this->redisKeyService->getRedisKeyForPostfix( + $contentReleaseIdentifier, + $redisKeyPostfix->getRedisKeyPostfix() + ), + $contentReleaseLogger + ); } } @@ -71,17 +97,25 @@ public function syncToTarget(RedisInstanceIdentifier $targetRedisIdentifier, Con * * @param string $keyToTransfer */ - protected function transferKey(\Redis $sourceRedis, \Redis $targetRedis, string $keyToTransfer, ContentReleaseLogger $contentReleaseLogger) - { + protected function transferKey( + \Redis $sourceRedis, + \Redis $targetRedis, + string $keyToTransfer, + ContentReleaseLogger $contentReleaseLogger + ) { $contentReleaseLogger->debug('SYNC: Attempting to transfer ' . $keyToTransfer); if (!$sourceRedis->exists($keyToTransfer)) { - $contentReleaseLogger->info('SYNC: Skipping ' . $keyToTransfer . ', as it does not exist on the source side'); + $contentReleaseLogger->info( + 'SYNC: Skipping ' . $keyToTransfer . ', as it does not exist on the source side' + ); return; } if ($targetRedis->exists($keyToTransfer)) { - $contentReleaseLogger->warn('SYNC: Skipping ' . $keyToTransfer . ', as it DOES exist on the target side (and we do not override!)'); + $contentReleaseLogger->warn( + 'SYNC: Skipping ' . $keyToTransfer . ', as it DOES exist on the target side (and we do not override!)' + ); return; } @@ -100,21 +134,41 @@ protected function transferKey(\Redis $sourceRedis, \Redis $targetRedis, string )); } - protected function transferHashKeyIncrementally(\Redis $sourceRedis, \Redis $targetRedis, string $keyToTransfer, ContentReleaseLogger $contentReleaseLogger) - { + protected function transferHashKeyIncrementally( + \Redis $sourceRedis, + \Redis $targetRedis, + string $keyToTransfer, + ContentReleaseLogger $contentReleaseLogger + ) { $contentReleaseLogger->debug('SYNC: (INCREMENTAL) Attempting to transfer ' . $keyToTransfer); if (!$sourceRedis->exists($keyToTransfer)) { - $contentReleaseLogger->info('SYNC: (INCREMENTAL) Skipping ' . $keyToTransfer . ', as it does not exist on the source side'); + $contentReleaseLogger->info( + 'SYNC: (INCREMENTAL) Skipping ' . $keyToTransfer . ', as it does not exist on the source side' + ); return; } if ($targetRedis->exists($keyToTransfer)) { - $contentReleaseLogger->warn('SYNC: (INCREMENTAL) WARNING: ' . $keyToTransfer . ', exists on the target side; we try to copy all values into it.'); + $contentReleaseLogger->warn( + 'SYNC: (INCREMENTAL) WARNING: ' + . $keyToTransfer + . ', exists on the target side; we try to copy all values into it.' + ); } if ($sourceRedis->type($keyToTransfer) !== \Redis::REDIS_HASH) { - $contentReleaseLogger->error('SYNC: (INCREMENTAL) !!! transferHashKeyIncrementally should only be used with hashes, but ' . $keyToTransfer . ' is of type ' . $sourceRedis->type($keyToTransfer)); - throw new \RuntimeException('!!! transferHashKeyIncrementally should only be used with hashes, but ' . $keyToTransfer . ' is of type ' . $sourceRedis->type($keyToTransfer)); + $contentReleaseLogger->error( + 'SYNC: (INCREMENTAL) !!! transferHashKeyIncrementally should only be used with hashes, but ' + . $keyToTransfer + . ' is of type ' + . $sourceRedis->type($keyToTransfer) + ); + throw new \RuntimeException( + '!!! transferHashKeyIncrementally should only be used with hashes, but ' + . $keyToTransfer + . ' is of type ' + . $sourceRedis->type($keyToTransfer) + ); } $expectedNumberOfHashItems = $sourceRedis->hLen($keyToTransfer); @@ -128,13 +182,13 @@ protected function transferHashKeyIncrementally(\Redis $sourceRedis, \Redis $tar // if we say a chunk should complete in 0.1s, we need < 400 chunks (at worst case for 40 seconds) // 80 000 records, divided by 400 chunks = 200 items per chunk. $startTime = microtime(true); - $it = NULL; + $it = null; $numberOfBatches = 0; while ($arr_keys = $sourceRedis->hScan($keyToTransfer, $it, null, 200)) { $numberOfBatches++; $targetPipeline = $targetRedis->pipeline(); // we don't care for the replies or for transactionality; so we use pipelining instead of MULTI foreach ($arr_keys as $hashKey => $hashValue) { - $targetPipeline->hSet($keyToTransfer, (string)$hashKey, $hashValue); + $targetPipeline->hSet($keyToTransfer, (string) $hashKey, $hashValue); } $targetPipeline->exec(); } @@ -143,8 +197,22 @@ protected function transferHashKeyIncrementally(\Redis $sourceRedis, \Redis $tar $actualNumberOfHashItems = $targetRedis->hLen($keyToTransfer); if ($expectedNumberOfHashItems !== $actualNumberOfHashItems) { - $contentReleaseLogger->error('SYNC: (INCREMENTAL) !!!! Number of hash items mismatch for key ' . $keyToTransfer . ' - expected ' . $expectedNumberOfHashItems . ', actual: ' . $actualNumberOfHashItems); - throw new \RuntimeException('!!!! Number of hash items mismatch for key ' . $keyToTransfer . ' - expected ' . $expectedNumberOfHashItems . ', actual: ' . $actualNumberOfHashItems); + $contentReleaseLogger->error( + 'SYNC: (INCREMENTAL) !!!! Number of hash items mismatch for key ' + . $keyToTransfer + . ' - expected ' + . $expectedNumberOfHashItems + . ', actual: ' + . $actualNumberOfHashItems + ); + throw new \RuntimeException( + '!!!! Number of hash items mismatch for key ' + . $keyToTransfer + . ' - expected ' + . $expectedNumberOfHashItems + . ', actual: ' + . $actualNumberOfHashItems + ); } $contentReleaseLogger->info(sprintf( @@ -152,7 +220,7 @@ protected function transferHashKeyIncrementally(\Redis $sourceRedis, \Redis $tar $keyToTransfer, $actualNumberOfHashItems, $numberOfBatches, - $endTime - $startTime, + $endTime - $startTime )); } } diff --git a/Classes/Transfer/Dto/RedisKeyPostfixForEachRelease.php b/Classes/Transfer/Dto/RedisKeyPostfixForEachRelease.php index fd64ef3..e412766 100644 --- a/Classes/Transfer/Dto/RedisKeyPostfixForEachRelease.php +++ b/Classes/Transfer/Dto/RedisKeyPostfixForEachRelease.php @@ -1,4 +1,5 @@ isRequired = $isRequired; } - public static function fromArray(array $in): self { - return new self( - $in['redisKeyPostfix'], - $in['transfer'], - $in['transferMode'], - $in['isRequired'] - ); + return new self($in['redisKeyPostfix'], $in['transfer'], $in['transferMode'], $in['isRequired']); } /** @@ -93,5 +87,4 @@ public function hasTransferModeHashIncremental(): bool { return $this->transferMode === self::TRANSFER_MODE_HASH_INCREMENTAL; } - } diff --git a/Classes/Transfer/Dto/RedisKeyPostfixesForEachRelease.php b/Classes/Transfer/Dto/RedisKeyPostfixesForEachRelease.php index 761d2ac..900c2ce 100644 --- a/Classes/Transfer/Dto/RedisKeyPostfixesForEachRelease.php +++ b/Classes/Transfer/Dto/RedisKeyPostfixesForEachRelease.php @@ -1,12 +1,13 @@ redisKeyPostfixes = $redisKeyPostfixes; } - public static function fromArray(array $in): self { $result = []; diff --git a/Classes/Transfer/Resource/RemoteResourceSynchronizer.php b/Classes/Transfer/Resource/RemoteResourceSynchronizer.php index 6078bca..dc62ab5 100644 --- a/Classes/Transfer/Resource/RemoteResourceSynchronizer.php +++ b/Classes/Transfer/Resource/RemoteResourceSynchronizer.php @@ -1,4 +1,7 @@ targets === array()) { $logger->debug('Skipping resource synchronization, no targets configured'); @@ -52,12 +55,9 @@ public function synchronize(ContentReleaseLogger $logger) if (!isset($targetConfiguration['user'])) { throw new Exception('Missing "user" for resource sync target', 1472126083); } - if (!isset($targetConfiguration['user'])) { - throw new Exception('Missing "user" for resource sync target', 1472126084); - } $port = 22; - if (isset($targetConfiguration['port']) && (string)$targetConfiguration['port'] !== '') { + if (isset($targetConfiguration['port']) && (string) $targetConfiguration['port'] !== '') { $port = intval($targetConfiguration['port']); } @@ -68,7 +68,12 @@ public function synchronize(ContentReleaseLogger $logger) ]); } - $target = $targetConfiguration['user'] . '@' . $targetConfiguration['host'] . ':' . $targetConfiguration['directory']; + $target = + $targetConfiguration['user'] + . '@' + . $targetConfiguration['host'] + . ':' + . $targetConfiguration['directory']; } // TODO This does not return errors yet, which we most probably need / want for production diff --git a/Classes/Transfer/Resource/Target/MultisiteFileSystemSymlinkTarget.php b/Classes/Transfer/Resource/Target/MultisiteFileSystemSymlinkTarget.php index aacdc58..e5ca375 100644 --- a/Classes/Transfer/Resource/Target/MultisiteFileSystemSymlinkTarget.php +++ b/Classes/Transfer/Resource/Target/MultisiteFileSystemSymlinkTarget.php @@ -1,5 +1,7 @@ overrideHttpBaseUri = $overrideHttpBaseUri; } - -} \ No newline at end of file +} diff --git a/Classes/Utility/GeneratorUtility.php b/Classes/Utility/GeneratorUtility.php index 9eda8dd..28b63a2 100644 --- a/Classes/Utility/GeneratorUtility.php +++ b/Classes/Utility/GeneratorUtility.php @@ -1,24 +1,25 @@ $iterable * @param int $chunkSize * @return iterable> */ - static public function createArrayBatch(iterable $iterable, int $chunkSize): iterable + public static function createArrayBatch(iterable $iterable, int $chunkSize): iterable { $accumulator = []; $i = 0; foreach ($iterable as $item) { $i++; $accumulator[] = $item; - if ($i % $chunkSize === 0) { + if (( $i % $chunkSize ) === 0) { yield $accumulator; $accumulator = []; diff --git a/Classes/Utility/Sparkline.php b/Classes/Utility/Sparkline.php index e7d8cde..e4ce553 100644 --- a/Classes/Utility/Sparkline.php +++ b/Classes/Utility/Sparkline.php @@ -1,6 +1,9 @@ 2, 'width' => 150, 'height' => 30,]; - $svg = '