Skip to content

Commit

Permalink
Merge pull request #5055 from dlubitz/90/feature/workspace-aware-cont…
Browse files Browse the repository at this point in the history
…ent-cache

FEATURE: Workspace aware content cache
  • Loading branch information
mhsdesign authored May 16, 2024
2 parents d775216 + 420e7d6 commit 8c0c864
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 93 deletions.
44 changes: 22 additions & 22 deletions Neos.Neos/Classes/Fusion/Cache/CacheTag.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
use Neos\Flow\Annotations as Flow;

/**
Expand All @@ -32,87 +32,87 @@ private function __construct(

final public static function forNodeAggregate(
ContentRepositoryId $contentRepositoryId,
ContentStreamId $contentStreamId,
WorkspaceName $workspaceName,
NodeAggregateId $nodeAggregateId,
): self {
return new self(
'Node_'
. self::getHashForContentStreamIdAndContentRepositoryId($contentStreamId, $contentRepositoryId)
. self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId)
. '_' . $nodeAggregateId->value
);
}

final public static function forNodeAggregateFromNode(Node $node): self
{
return self::forNodeAggregate(
$node->subgraphIdentity->contentRepositoryId,
$node->subgraphIdentity->contentStreamId,
$node->contentRepositoryId,
$node->workspaceName,
$node->nodeAggregateId
);
}

final public static function forDescendantOfNode(
ContentRepositoryId $contentRepositoryId,
ContentStreamId $contentStreamId,
WorkspaceName $workspaceName,
NodeAggregateId $nodeAggregateId,
): self {
return new self(
'DescendantOf_'
. self::getHashForContentStreamIdAndContentRepositoryId($contentStreamId, $contentRepositoryId)
. self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId)
. '_' . $nodeAggregateId->value
);
}

final public static function forDescendantOfNodeFromNode(Node $node): self
{
return self::forDescendantOfNode(
$node->subgraphIdentity->contentRepositoryId,
$node->subgraphIdentity->contentStreamId,
$node->nodeAggregateId
$node->contentRepositoryId,
$node->workspaceName,
$node->aggregateId
);
}

final public static function forAncestorNode(
ContentRepositoryId $contentRepositoryId,
ContentStreamId $contentStreamId,
WorkspaceName $workspaceName,
NodeAggregateId $nodeAggregateId,
): self {
return new self(
'Ancestor_'
. self::getHashForContentStreamIdAndContentRepositoryId($contentStreamId, $contentRepositoryId)
. self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId)
. '_' . $nodeAggregateId->value
);
}

final public static function forAncestorNodeFromNode(Node $node): self
{
return self::forAncestorNode(
$node->subgraphIdentity->contentRepositoryId,
$node->subgraphIdentity->contentStreamId,
$node->nodeAggregateId
$node->contentRepositoryId,
$node->workspaceName,
$node->aggregateId
);
}

final public static function forNodeTypeName(
ContentRepositoryId $contentRepositoryId,
ContentStreamId $contentStreamId,
WorkspaceName $workspaceName,
NodeTypeName $nodeTypeName,
): self {
return new self(
'NodeType_'
. self::getHashForContentStreamIdAndContentRepositoryId($contentStreamId, $contentRepositoryId)
. self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId)
. '_' . \strtr($nodeTypeName->value, '.:', '_-')
);
}

final public static function forDynamicNodeAggregate(
ContentRepositoryId $contentRepositoryId,
ContentStreamId $contentStreamId,
WorkspaceName $workspaceName,
NodeAggregateId $nodeAggregateId,
): self {
return new self(
'DynamicNodeTag_'
. self::getHashForContentStreamIdAndContentRepositoryId($contentStreamId, $contentRepositoryId)
. self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId)
. '_' . $nodeAggregateId->value
);
}
Expand All @@ -122,10 +122,10 @@ final public static function fromString(string $string): self
return new self($string);
}

protected static function getHashForContentStreamIdAndContentRepositoryId(
ContentStreamId $contentStreamId,
protected static function getHashForWorkspaceNameAndContentRepositoryId(
WorkspaceName $workspaceName,
ContentRepositoryId $contentRepositoryId,
): string {
return sha1($contentStreamId->value . '@' . $contentRepositoryId->value);
return sha1($workspaceName->value . '@' . $contentRepositoryId->value);
}
}
14 changes: 7 additions & 7 deletions Neos.Neos/Classes/Fusion/Cache/CacheTagSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\ContentRepository\Core\Projection\ContentGraph\Nodes;
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
use Neos\Flow\Annotations as Flow;

/**
Expand Down Expand Up @@ -38,7 +38,7 @@ public static function forDescendantOfNodesFromNodes(
Nodes $nodes
): self {
return new self(...array_map(
fn(Node $node): CacheTag => CacheTag::forDescendantOfNodeFromNode(
fn (Node $node): CacheTag => CacheTag::forDescendantOfNodeFromNode(
$node
),
iterator_to_array($nodes)
Expand All @@ -49,7 +49,7 @@ public static function forNodeAggregatesFromNodes(
Nodes $nodes
): self {
return new self(...array_map(
fn(Node $node): CacheTag => CacheTag::forNodeAggregateFromNode(
fn (Node $node): CacheTag => CacheTag::forNodeAggregateFromNode(
$node
),
iterator_to_array($nodes)
Expand All @@ -59,13 +59,13 @@ public static function forNodeAggregatesFromNodes(

public static function forNodeTypeNames(
ContentRepositoryId $contentRepositoryId,
ContentStreamId $contentStreamId,
WorkspaceName $workspaceName,
NodeTypeNames $nodeTypeNames
): self {
return new self(...array_map(
fn(NodeTypeName $nodeTypeName): CacheTag => CacheTag::forNodeTypeName(
fn (NodeTypeName $nodeTypeName): CacheTag => CacheTag::forNodeTypeName(
$contentRepositoryId,
$contentStreamId,
$workspaceName,
$nodeTypeName
),
iterator_to_array($nodeTypeNames)
Expand All @@ -86,7 +86,7 @@ public function add(CacheTag $cacheTag): self
public function toStringArray(): array
{
return array_map(
fn(CacheTag $tag): string => $tag->value,
fn (CacheTag $tag): string => $tag->value,
array_values($this->tags)
);
}
Expand Down
57 changes: 31 additions & 26 deletions Neos.Neos/Classes/Fusion/Cache/ContentCacheFlusher.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@

namespace Neos\Neos\Fusion\Cache;

use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\ContentGraph;
use Neos\ContentRepository\Core\ContentRepository;
use Neos\ContentRepository\Core\NodeType\NodeType;
use Neos\ContentRepository\Core\NodeType\NodeTypeName;
use Neos\ContentRepository\Core\Projection\ContentGraph\NodeAggregate;
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
use Neos\ContentRepository\Core\SharedModel\Exception\NodeTypeNotFound;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Persistence\PersistenceManagerInterface;
Expand Down Expand Up @@ -64,20 +63,16 @@ public function __construct(

/**
* Main entry point to *directly* flush the caches of a given NodeAggregate
*
* @param ContentRepository $contentRepository
* @param ContentStreamId $contentStreamId
* @param NodeAggregateId $nodeAggregateId
*/
public function flushNodeAggregate(
ContentRepository $contentRepository,
ContentStreamId $contentStreamId,
WorkspaceName $workspaceName,
NodeAggregateId $nodeAggregateId
): void {
$tagsToFlush[ContentCache::TAG_EVERYTHING] = 'which were tagged with "Everything".';

$tagsToFlush = array_merge(
$this->collectTagsForChangeOnNodeAggregate($contentRepository, $contentStreamId, $nodeAggregateId),
$this->collectTagsForChangeOnNodeAggregate($contentRepository, $workspaceName, $nodeAggregateId),
$tagsToFlush
);

Expand All @@ -89,15 +84,10 @@ public function flushNodeAggregate(
*/
private function collectTagsForChangeOnNodeAggregate(
ContentRepository $contentRepository,
ContentStreamId $contentStreamId,
WorkspaceName $workspaceName,
NodeAggregateId $nodeAggregateId
): array {
$workspace = $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId($contentStreamId);
if (is_null($workspace)) {
return [];
}
$contentGraph = $contentRepository->getContentGraph($workspace->workspaceName);

$contentGraph = $contentRepository->getContentGraph($workspaceName);

$nodeAggregate = $contentGraph->findNodeAggregateById(
$nodeAggregateId
Expand All @@ -106,12 +96,12 @@ private function collectTagsForChangeOnNodeAggregate(
// Node Aggregate was removed in the meantime, so no need to clear caches on this one anymore.
return [];
}
$tagsToFlush = $this->collectTagsForChangeOnNodeIdentifier($contentRepository->id, $contentGraph->getContentStreamId(), $nodeAggregateId);
$tagsToFlush = $this->collectTagsForChangeOnNodeIdentifier($contentRepository->id, $workspaceName, $nodeAggregateId);

$tagsToFlush = array_merge($this->collectTagsForChangeOnNodeType(
$nodeAggregate->nodeTypeName,
$contentRepository->id,
$contentGraph->getContentStreamId(),
$workspaceName,
$nodeAggregateId,
$contentRepository
), $tagsToFlush);
Expand All @@ -125,7 +115,7 @@ private function collectTagsForChangeOnNodeAggregate(
$parentNodeAggregates[] = $parentNodeAggregate;
}
// we do not need these variables anymore here
unset($contentStreamId, $nodeAggregateId);
unset($nodeAggregateId);


// NOTE: Normally, the content graph cannot contain cycles. However, during the
Expand All @@ -144,7 +134,7 @@ private function collectTagsForChangeOnNodeAggregate(
}
$processedNodeAggregateIds[$nodeAggregate->nodeAggregateId->value] = true;

$tagName = CacheTag::forDescendantOfNode($contentRepository->id, $nodeAggregate->contentStreamId, $nodeAggregate->nodeAggregateId);
$tagName = CacheTag::forDescendantOfNode($contentRepository->id, $workspaceName, $nodeAggregate->nodeAggregateId);
$tagsToFlush[$tagName->value] = sprintf(
'which were tagged with "%s" because node "%s" has changed.',
$tagName->value,
Expand All @@ -169,24 +159,24 @@ private function collectTagsForChangeOnNodeAggregate(
*/
private function collectTagsForChangeOnNodeIdentifier(
ContentRepositoryId $contentRepositoryId,
ContentStreamId $contentStreamId,
WorkspaceName $workspaceName,
NodeAggregateId $nodeAggregateId,
): array {
$tagsToFlush = [];

$nodeCacheIdentifier = CacheTag::forNodeAggregate($contentRepositoryId, $contentStreamId, $nodeAggregateId);
$nodeCacheIdentifier = CacheTag::forNodeAggregate($contentRepositoryId, $workspaceName, $nodeAggregateId);
$tagsToFlush[$nodeCacheIdentifier->value] = sprintf(
'which were tagged with "%s" because that identifier has changed.',
$nodeCacheIdentifier->value
);

$dynamicNodeCacheIdentifier = CacheTag::forDynamicNodeAggregate($contentRepositoryId, $contentStreamId, $nodeAggregateId);
$dynamicNodeCacheIdentifier = CacheTag::forDynamicNodeAggregate($contentRepositoryId, $workspaceName, $nodeAggregateId);
$tagsToFlush[$dynamicNodeCacheIdentifier->value] = sprintf(
'which were tagged with "%s" because that identifier has changed.',
$dynamicNodeCacheIdentifier->value
);

$descendantOfNodeCacheIdentifier = CacheTag::forDescendantOfNode($contentRepositoryId, $contentStreamId, $nodeAggregateId);
$descendantOfNodeCacheIdentifier = CacheTag::forDescendantOfNode($contentRepositoryId, $workspaceName, $nodeAggregateId);
$tagsToFlush[$descendantOfNodeCacheIdentifier->value] = sprintf(
'which were tagged with "%s" because node "%s" has changed.',
$descendantOfNodeCacheIdentifier->value,
Expand All @@ -202,7 +192,7 @@ private function collectTagsForChangeOnNodeIdentifier(
private function collectTagsForChangeOnNodeType(
NodeTypeName $nodeTypeName,
ContentRepositoryId $contentRepositoryId,
ContentStreamId $contentStreamId,
WorkspaceName $workspaceName,
?NodeAggregateId $referenceNodeIdentifier,
ContentRepository $contentRepository
): array {
Expand All @@ -217,7 +207,7 @@ private function collectTagsForChangeOnNodeType(
}

foreach ($nodeTypesNamesToFlush as $nodeTypeNameToFlush) {
$nodeTypeNameCacheIdentifier = CacheTag::forNodeTypeName($contentRepositoryId, $contentStreamId, NodeTypeName::fromString($nodeTypeNameToFlush));
$nodeTypeNameCacheIdentifier = CacheTag::forNodeTypeName($contentRepositoryId, $workspaceName, NodeTypeName::fromString($nodeTypeNameToFlush));
$tagsToFlush[$nodeTypeNameCacheIdentifier->value] = sprintf(
'which were tagged with "%s" because node "%s" has changed and was of type "%s".',
$nodeTypeNameCacheIdentifier->value,
Expand Down Expand Up @@ -293,13 +283,28 @@ public function registerAssetChange(AssetInterface $asset): void
->withAsset($this->persistenceManager->getIdentifierByObject($asset))
->includeVariantsOfAsset();


$workspaceNamesByContentStreamId = [];
foreach ($this->globalAssetUsageService->findByFilter($filter) as $contentRepositoryId => $usages) {
foreach ($usages as $usage) {
// TODO: Remove when WorkspaceName is part of the AssetUsageProjection
$workspaceName = $workspaceNamesByContentStreamId[$contentRepositoryId][$usage->contentStreamId->value] ?? null;
if ($workspaceName === null) {
$contentRepository = $this->contentRepositoryRegistry->get(ContentRepositoryId::fromString($contentRepositoryId));
$workspace = $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId($usage->contentStreamId);
if ($workspace === null) {
continue;
}
$workspaceName = $workspace->workspaceName;
$workspaceNamesByContentStreamId[$contentRepositoryId][$usage->contentStreamId->value] = $workspaceName;
}
//

$contentRepository = $this->contentRepositoryRegistry->get(ContentRepositoryId::fromString($contentRepositoryId));
$tagsToFlush = array_merge(
$this->collectTagsForChangeOnNodeAggregate(
$contentRepository,
$usage->contentStreamId,
$workspaceName,
$usage->nodeAggregateId
),
$tagsToFlush
Expand Down
Loading

0 comments on commit 8c0c864

Please sign in to comment.