Skip to content

Commit 4214c9a

Browse files
authored
Merge pull request #42 from Flowpack/task/release-2.0-beta
Feature: add changes from 1and1 branch, rebase + resolve conflicts for current main
2 parents b439d98 + 56e269a commit 4214c9a

33 files changed

Lines changed: 693 additions & 633 deletions

Classes/Aspects/CacheUrlMappingAspect.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ class CacheUrlMappingAspect
3737
*/
3838
protected $isActive = false;
3939

40+
/**
41+
* @var null | int
42+
*/
43+
protected $renderTimestamp = null;
44+
4045
/**
4146
* @var array
4247
*/
@@ -102,9 +107,6 @@ public function storeRootCacheIdentifier(JoinPointInterface $joinPoint)
102107

103108
/** @var NodeInterface $node */
104109
$node = $this->currentEvaluateContext['cacheIdentifierValues']['node'];
105-
if (!$node->getContext()->getWorkspace()->isPublicWorkspace()) {
106-
return;
107-
}
108110

109111
$url = $this->getCurrentUrl();
110112

@@ -144,8 +146,10 @@ public function storeRootCacheIdentifier(JoinPointInterface $joinPoint)
144146
$logger->debug('Mapping URL ' . $url . ' to ' . $rootIdentifier . ' with tags ' . implode(', ', $rootTags));
145147

146148
$arguments = $this->getCurrentArguments($node);
149+
// 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`.
147150
$rootKey = DocumentNodeCacheKey::fromNodeAndArguments($node, $arguments);
148-
$rootCacheValues = DocumentNodeCacheValues::create($rootIdentifier, $url);
151+
$rootCacheValues = DocumentNodeCacheValues::create($rootIdentifier, $url)
152+
->withMetadata('renderTime', (int)(microtime(true) * 1000) - $this->renderTimestamp);
149153
// allow other document metadata generators here
150154
$rootCacheValues = $this->nodeRenderingExtensionManager->runDocumentMetadataGenerators($node, $arguments, $this->controllerContext, $rootCacheValues);
151155
$this->contentCacheFrontend->set($rootKey->redisKeyName(), json_encode($rootCacheValues), $rootTags);
@@ -202,6 +206,7 @@ public function beforeDocumentRendering(ContentReleaseLogger $contentReleaseLogg
202206
{
203207
$this->isActive = true;
204208
$this->contentReleaseLogger = $contentReleaseLogger;
209+
$this->renderTimestamp = (int)(microtime(true) * 1000);
205210
}
206211

207212
public function afterDocumentRendering(): void

Classes/BackendUi/BackendUiDataService.php

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Flowpack\DecoupledContentStore\NodeRendering\Dto\RenderingStatistics;
1515
use Flowpack\DecoupledContentStore\NodeRendering\Infrastructure\RedisRenderingErrorManager;
1616
use Flowpack\DecoupledContentStore\NodeRendering\Infrastructure\RedisRenderingStatisticsStore;
17+
use Flowpack\DecoupledContentStore\PrepareContentRelease\Dto\ContentReleaseMetadata;
1718
use Flowpack\DecoupledContentStore\PrepareContentRelease\Infrastructure\RedisContentReleaseService;
1819
use Flowpack\DecoupledContentStore\ReleaseSwitch\Infrastructure\RedisReleaseSwitchService;
1920
use Neos\Flow\Annotations as Flow;
@@ -81,12 +82,17 @@ public function loadBackendOverviewData(RedisInstanceIdentifier $redisInstanceId
8182
$lastRendering = RenderingStatistics::fromJsonString($lastRenderingStatisticsEntries->getResultForContentRelease($contentReleaseId));
8283
$firstRendering = RenderingStatistics::fromJsonString($firstRenderingStatisticsEntries->getResultForContentRelease($contentReleaseId));
8384

85+
$metadataForContentRelease = $metadata->getResultForContentRelease($contentReleaseId);
86+
$countForContentRelease = $counts->getResultForContentRelease($contentReleaseId);
87+
$iterationsCountForContentRelease = $iterationsCounts->getResultForContentRelease($contentReleaseId);
88+
$errorCountForContentRelease = $errorCounts->getResultForContentRelease($contentReleaseId);
89+
8490
$result[] = new ContentReleaseOverviewRow(
8591
$contentReleaseId,
86-
$metadata->getResultForContentRelease($contentReleaseId),
87-
$counts->getResultForContentRelease($contentReleaseId),
88-
$iterationsCounts->getResultForContentRelease($contentReleaseId),
89-
$errorCounts->getResultForContentRelease($contentReleaseId),
92+
$metadataForContentRelease instanceof ContentReleaseMetadata ? $metadataForContentRelease : null,
93+
is_int($countForContentRelease) ? $countForContentRelease : 0,
94+
is_int($iterationsCountForContentRelease) ? $iterationsCountForContentRelease : 0,
95+
is_int($errorCountForContentRelease) ? $errorCountForContentRelease : 0,
9096
$lastRendering->getTotalJobs() > 0 ? round($lastRendering->getRenderedJobs()
9197
/ $lastRendering->getTotalJobs() * 100) : 100,
9298
$firstRendering->getRenderedJobs(),
@@ -114,9 +120,14 @@ private function calculateReleaseSize(RedisInstanceIdentifier $redisInstanceIden
114120
return round($size / 1000000, 2);
115121
}
116122

117-
public function loadDetailsData(ContentReleaseIdentifier $contentReleaseIdentifier, RedisInstanceIdentifier $redisInstanceIdentifier): ContentReleaseDetails
123+
public function loadDetailsData(ContentReleaseIdentifier $contentReleaseIdentifier, RedisInstanceIdentifier $redisInstanceIdentifier): ?ContentReleaseDetails
118124
{
119125
$contentReleaseMetadata = $this->redisContentReleaseService->fetchMetadataForContentRelease($contentReleaseIdentifier, $redisInstanceIdentifier);
126+
127+
if (!$contentReleaseMetadata) {
128+
return null;
129+
}
130+
120131
$contentReleaseJob = $this->prunnerApiService->loadJobDetail($contentReleaseMetadata->getPrunnerJobId()->toJobId());
121132

122133
$manualTransferJobs = count($contentReleaseMetadata->getManualTransferJobIds()) ? array_map(function (PrunnerJobId $item) {

Classes/BackendUi/Dto/ContentReleaseOverviewRow.php

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
class ContentReleaseOverviewRow
1515
{
1616
private ContentReleaseIdentifier $contentReleaseIdentifier;
17-
private ContentReleaseMetadata $metadata;
17+
private ?ContentReleaseMetadata $metadata;
1818
private int $enumeratedDocumentNodesCount;
1919
private int $iterationsCount;
2020
private int $errorCount;
@@ -23,7 +23,7 @@ class ContentReleaseOverviewRow
2323
private bool $isActive;
2424
private float $releaseSize;
2525

26-
public function __construct(ContentReleaseIdentifier $contentReleaseIdentifier, ContentReleaseMetadata $metadata,
26+
public function __construct(ContentReleaseIdentifier $contentReleaseIdentifier, ?ContentReleaseMetadata $metadata,
2727
int $enumeratedDocumentNodesCount, int $iterationsCount, int $errorCount,
2828
float $progress, int $renderedUrlCount, bool $isActive, float $releaseSize)
2929
{
@@ -38,73 +38,46 @@ public function __construct(ContentReleaseIdentifier $contentReleaseIdentifier,
3838
$this->releaseSize = $releaseSize;
3939
}
4040

41-
/**
42-
* @return ContentReleaseMetadata
43-
*/
44-
public function getMetadata(): ContentReleaseMetadata
41+
public function getMetadata(): ?ContentReleaseMetadata
4542
{
4643
return $this->metadata;
4744
}
4845

49-
/**
50-
* @return ContentReleaseIdentifier
51-
*/
5246
public function getContentReleaseIdentifier(): ContentReleaseIdentifier
5347
{
5448
return $this->contentReleaseIdentifier;
5549
}
5650

57-
/**
58-
* @return int
59-
*/
6051
public function getEnumeratedDocumentNodesCount(): int
6152
{
6253
return $this->enumeratedDocumentNodesCount;
6354
}
6455

65-
/**
66-
* @return int
67-
*/
6856
public function getIterationsCount(): int
6957
{
7058
return $this->iterationsCount;
7159
}
7260

73-
/**
74-
* @return int
75-
*/
7661
public function getErrorCount(): int
7762
{
7863
return $this->errorCount;
7964
}
8065

81-
/**
82-
* @return float
83-
*/
8466
public function getProgress(): float
8567
{
8668
return $this->progress;
8769
}
8870

89-
/**
90-
* @return int
91-
*/
9271
public function getRenderedUrlCount(): int
9372
{
9473
return $this->renderedUrlCount;
9574
}
9675

97-
/**
98-
* @return bool
99-
*/
10076
public function isActive(): bool
10177
{
10278
return $this->isActive;
10379
}
10480

105-
/**
106-
* @return float
107-
*/
10881
public function getReleaseSize(): float
10982
{
11083
return $this->releaseSize;

Classes/Command/ContentReleasePrepareCommandController.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Flowpack\DecoupledContentStore\Core\Domain\ValueObject\ContentReleaseIdentifier;
1111
use Flowpack\DecoupledContentStore\Core\Infrastructure\ContentReleaseLogger;
1212
use Neos\Flow\Cli\CommandController;
13+
use Neos\Fusion\Core\Cache\ContentCache;
1314

1415
/**
1516
* Commands for the PREPARE stage in the pipeline. Not meant to be called manually.
@@ -28,6 +29,12 @@ class ContentReleasePrepareCommandController extends CommandController
2829
*/
2930
protected $concurrentBuildLock;
3031

32+
/**
33+
* @Flow\Inject
34+
* @var ContentCache
35+
*/
36+
protected $contentCache;
37+
3138
public function createContentReleaseCommand(string $contentReleaseIdentifier, string $prunnerJobId, string $workspaceName = 'live', string $accountId = 'cli'): void
3239
{
3340
$contentReleaseIdentifier = ContentReleaseIdentifier::fromString($contentReleaseIdentifier);
@@ -51,4 +58,15 @@ public function registerManualTransferJobCommand(string $contentReleaseIdentifie
5158

5259
$this->redisContentReleaseService->registerManualTransferJob($contentReleaseIdentifier, $prunnerJobId, $logger);
5360
}
61+
62+
public function flushContentCacheIfRequiredCommand(string $contentReleaseIdentifier, bool $flushContentCache = false): void
63+
{
64+
$logger = ContentReleaseLogger::fromConsoleOutput($this->output, ContentReleaseIdentifier::fromString($contentReleaseIdentifier));
65+
if (!$flushContentCache) {
66+
$logger->info('Not flushing content cache');
67+
return;
68+
}
69+
$logger->info('Flushing content cache');
70+
$this->contentCache->flush();
71+
}
5472
}

Classes/ContentReleaseManager.php

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,12 @@
1313
use Flowpack\Prunner\PrunnerApiService;
1414
use Flowpack\Prunner\ValueObject\PipelineName;
1515
use Neos\Flow\Security\Context;
16-
use Neos\Fusion\Core\Cache\ContentCache;
1716

1817
/**
1918
* @Flow\Scope("singleton")
2019
*/
2120
class ContentReleaseManager
2221
{
23-
/**
24-
* @Flow\Inject
25-
* @var ContentCache
26-
*/
27-
protected $contentCache;
2822

2923
/**
3024
* @Flow\Inject
@@ -63,6 +57,7 @@ public function startIncrementalContentRelease(string $currentContentReleaseId =
6357
'contentReleaseId' => $contentReleaseId,
6458
'currentContentReleaseId' => $this->resolveCurrentContentReleaseId($currentContentReleaseId),
6559
'validate' => true,
60+
'flushContentCache' => false,
6661
'workspaceName' => $workspace !== null ? $workspace->getName() : 'live',
6762
'accountId' => $this->getAccountId(),
6863
]));
@@ -74,13 +69,11 @@ public function startIncrementalContentRelease(string $currentContentReleaseId =
7469
public function startFullContentRelease(bool $validate = true, string $currentContentReleaseId = null, Workspace $workspace = null, array $additionalVariables = []): ContentReleaseIdentifier
7570
{
7671
$contentReleaseId = ContentReleaseIdentifier::create();
77-
78-
$this->contentCache->flush();
79-
8072
$this->prunnerApiService->schedulePipeline(PipelineName::create('do_content_release'), array_merge($additionalVariables, [
8173
'contentReleaseId' => $contentReleaseId,
8274
'currentContentReleaseId' => $this->resolveCurrentContentReleaseId($currentContentReleaseId),
8375
'validate' => $validate,
76+
'flushContentCache' => true,
8477
'workspaceName' => $workspace !== null ? $workspace->getName() : 'live',
8578
'accountId' => $this->getAccountId(),
8679
]));

Classes/Controller/BackendController.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ public function detailsAction(string $contentReleaseIdentifier, ?string $content
114114
$contentReleaseIdentifier = ContentReleaseIdentifier::fromString($contentReleaseIdentifier);
115115
$contentStore = $contentStore ? RedisInstanceIdentifier::fromString($contentStore) : RedisInstanceIdentifier::primary();
116116

117+
$this->view->assign('contentReleaseIdentifier', $contentReleaseIdentifier);
117118
$this->view->assign('contentStore', $contentStore->getIdentifier());
118119

119120
$detailsData = $this->backendUiDataService->loadDetailsData($contentReleaseIdentifier, $contentStore);

Classes/Core/ConcurrentBuildLockService.php

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44

55
use Flowpack\DecoupledContentStore\Core\Domain\ValueObject\ContentReleaseIdentifier;
66
use Flowpack\DecoupledContentStore\Core\Infrastructure\RedisClientManager;
7+
use Flowpack\DecoupledContentStore\PrepareContentRelease\Infrastructure\RedisContentReleaseService;
78
use Neos\Flow\Annotations as Flow;
89

910
/**
10-
* We usually rely on prunner to ensure that only one build is running at any given time.
11+
* We usually rely on prunner to ensure that only one build per workspace is running at any given time.
1112
*
1213
* However, when running in a cloud environment with no shared storage, the prunner data folder is not shared between
1314
* instances. In this case, during a deployment, two containers run concurrently, with two separate prunner instances
@@ -27,35 +28,43 @@
2728
*/
2829
class ConcurrentBuildLockService
2930
{
31+
private const CONTENT_STORE_CONCURRENT_BUILD_LOCK = 'contentStore:concurrentBuildLocks';
3032

3133
/**
3234
* @Flow\Inject
3335
* @var RedisClientManager
3436
*/
3537
protected $redisClientManager;
3638

37-
public function ensureAllOtherInProgressContentReleasesWillBeTerminated(ContentReleaseIdentifier $contentReleaseIdentifier)
39+
/**
40+
* @Flow\Inject
41+
* @var RedisContentReleaseService
42+
*/
43+
protected $redisContentReleaseService;
44+
45+
public function ensureAllOtherInProgressContentReleasesWillBeTerminated(ContentReleaseIdentifier $contentReleaseIdentifier): void
3846
{
39-
$this->redisClientManager->getPrimaryRedis()->set('contentStore:concurrentBuildLock', $contentReleaseIdentifier->getIdentifier());
47+
$metadata = $this->redisContentReleaseService->fetchMetadataForContentRelease($contentReleaseIdentifier);
48+
$this->redisClientManager->getPrimaryRedis()->hSet(self::CONTENT_STORE_CONCURRENT_BUILD_LOCK, $metadata->getWorkspaceName(), (string)$contentReleaseIdentifier);
4049
}
4150

42-
public function assertNoOtherContentReleaseWasStarted(ContentReleaseIdentifier $contentReleaseIdentifier)
51+
public function assertNoOtherContentReleaseWasStarted(ContentReleaseIdentifier $contentReleaseIdentifier): void
4352
{
44-
$concurrentBuildLockString = $this->redisClientManager->getPrimaryRedis()->get('contentStore:concurrentBuildLock');
53+
$metadata = $this->redisContentReleaseService->fetchMetadataForContentRelease($contentReleaseIdentifier);
54+
$concurrentBuildLockStrings = $this->redisClientManager->getPrimaryRedis()->hGetAll(self::CONTENT_STORE_CONCURRENT_BUILD_LOCK);
55+
$concurrentBuildLockStringForWorkspace = $concurrentBuildLockStrings[$metadata->getWorkspaceName()] ?? null;
4556

46-
if (empty($concurrentBuildLockString)) {
57+
if (!$concurrentBuildLockStringForWorkspace) {
4758
echo '!!!!! Hard-aborting the current job ' . $contentReleaseIdentifier->getIdentifier() . ' because the concurrentBuildLock does not exist.' . "\n\n";
4859
echo "This should never happen for correctly configured jobs (that run after prepare_finished).\n\n";
4960
exit(1);
5061
}
51-
52-
$concurrentBuildLock = ContentReleaseIdentifier::fromString($concurrentBuildLockString);
5362

63+
$concurrentBuildLock = ContentReleaseIdentifier::fromString($concurrentBuildLockStringForWorkspace);
5464
if (!$contentReleaseIdentifier->equals($concurrentBuildLock)) {
5565
// the concurrent build lock is different (i.e. newer) than our currently-running content release.
5666
// Thus, we abort the in-progress content release as quickly as we can - by DYING.
57-
58-
echo '!!!!! Hard-aborting the current job ' . $contentReleaseIdentifier->getIdentifier() . ' because the concurrentBuildLock contains ' . $concurrentBuildLock->getIdentifier() . "\n\n";
67+
echo '!!!!! Hard-aborting the current job ' . $contentReleaseIdentifier->getIdentifier() . ' because the concurrentBuildLock for workspace "' . $metadata->getWorkspaceName() . '" contains ' . $concurrentBuildLock->getIdentifier() . "\n\n";
5968
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";
6069
exit(1);
6170
}

Classes/Core/Domain/Dto/ContentReleaseBatchResult.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Flowpack\DecoupledContentStore\Core\Domain\Dto;
44

55
use Flowpack\DecoupledContentStore\Core\Domain\ValueObject\ContentReleaseIdentifier;
6+
use Flowpack\DecoupledContentStore\PrepareContentRelease\Dto\ContentReleaseMetadata;
67
use Neos\Flow\Annotations as Flow;
78

89
/**
@@ -20,15 +21,14 @@ private function __construct(array $results)
2021
$this->results = $results;
2122
}
2223

23-
2424
public static function createFromArray(array $in): self
2525
{
2626
return new self($in);
2727
}
2828

2929
public function getResultForContentRelease(ContentReleaseIdentifier $contentReleaseIdentifier)
3030
{
31-
return $this->results[$contentReleaseIdentifier->jsonSerialize()];
31+
return $this->results[(string)$contentReleaseIdentifier] ?? null;
3232
}
3333

34-
}
34+
}

0 commit comments

Comments
 (0)