From 596cb2fc13def5e0a84fe5ad5d59e9d1c0948778 Mon Sep 17 00:00:00 2001 From: Denny Lubitz Date: Mon, 3 Jun 2024 22:48:17 +0200 Subject: [PATCH 1/6] BUGFIX: Flush node content caches without workspace name --- Neos.Neos/Classes/Fusion/Cache/CacheTag.php | 45 ++++++++++++++++--- .../Classes/Fusion/Cache/CacheTagSet.php | 35 +++++++++++++++ .../Fusion/Cache/ContentCacheFlusher.php | 20 ++++++--- .../Classes/Fusion/Helper/CachingHelper.php | 28 ++++++++---- 4 files changed, 105 insertions(+), 23 deletions(-) diff --git a/Neos.Neos/Classes/Fusion/Cache/CacheTag.php b/Neos.Neos/Classes/Fusion/Cache/CacheTag.php index 69814d621ef..98b8e1dcabf 100644 --- a/Neos.Neos/Classes/Fusion/Cache/CacheTag.php +++ b/Neos.Neos/Classes/Fusion/Cache/CacheTag.php @@ -32,7 +32,7 @@ private function __construct( final public static function forNodeAggregate( ContentRepositoryId $contentRepositoryId, - WorkspaceName $workspaceName, + ?WorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, ): self { return new self( @@ -51,9 +51,18 @@ final public static function forNodeAggregateFromNode(Node $node): self ); } + final public static function forNodeAggregateFromNodeWithoutWorkspace(Node $node): self + { + return self::forNodeAggregate( + $node->contentRepositoryId, + null, + $node->aggregateId + ); + } + final public static function forDescendantOfNode( ContentRepositoryId $contentRepositoryId, - WorkspaceName $workspaceName, + ?WorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, ): self { return new self( @@ -72,9 +81,18 @@ final public static function forDescendantOfNodeFromNode(Node $node): self ); } + final public static function forDescendantOfNodeFromNodeWithoutWorkspace(Node $node): self + { + return self::forDescendantOfNode( + $node->contentRepositoryId, + null, + $node->aggregateId + ); + } + final public static function forAncestorNode( ContentRepositoryId $contentRepositoryId, - WorkspaceName $workspaceName, + ?WorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, ): self { return new self( @@ -93,9 +111,18 @@ final public static function forAncestorNodeFromNode(Node $node): self ); } + final public static function forAncestorNodeFromNodeWithoutWorkspace(Node $node): self + { + return self::forAncestorNode( + $node->contentRepositoryId, + null, + $node->aggregateId + ); + } + final public static function forNodeTypeName( ContentRepositoryId $contentRepositoryId, - WorkspaceName $workspaceName, + ?WorkspaceName $workspaceName, NodeTypeName $nodeTypeName, ): self { return new self( @@ -107,7 +134,7 @@ final public static function forNodeTypeName( final public static function forDynamicNodeAggregate( ContentRepositoryId $contentRepositoryId, - WorkspaceName $workspaceName, + ?WorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, ): self { return new self( @@ -123,9 +150,13 @@ final public static function fromString(string $string): self } protected static function getHashForWorkspaceNameAndContentRepositoryId( - WorkspaceName $workspaceName, + ?WorkspaceName $workspaceName, ContentRepositoryId $contentRepositoryId, ): string { - return sha1($workspaceName->value . '@' . $contentRepositoryId->value); + if ($workspaceName) { + return sha1($workspaceName->value . '@' . $contentRepositoryId->value); + } + return sha1($contentRepositoryId->value); + } } diff --git a/Neos.Neos/Classes/Fusion/Cache/CacheTagSet.php b/Neos.Neos/Classes/Fusion/Cache/CacheTagSet.php index b620b062470..e8e3ec2a603 100644 --- a/Neos.Neos/Classes/Fusion/Cache/CacheTagSet.php +++ b/Neos.Neos/Classes/Fusion/Cache/CacheTagSet.php @@ -45,6 +45,17 @@ public static function forDescendantOfNodesFromNodes( )); } + public static function forDescendantOfNodesFromNodesWithoutWorkspace( + Nodes $nodes + ): self { + return new self(...array_map( + fn (Node $node): CacheTag => CacheTag::forDescendantOfNodeFromNodeWithoutWorkspace( + $node + ), + iterator_to_array($nodes) + )); + } + public static function forNodeAggregatesFromNodes( Nodes $nodes ): self { @@ -56,6 +67,16 @@ public static function forNodeAggregatesFromNodes( )); } + public static function forNodeAggregatesFromNodesWithoutWorkspace( + Nodes $nodes + ): self { + return new self(...array_map( + fn (Node $node): CacheTag => CacheTag::forNodeAggregateFromNodeWithoutWorkspace( + $node + ), + iterator_to_array($nodes) + )); + } public static function forNodeTypeNames( ContentRepositoryId $contentRepositoryId, @@ -72,6 +93,20 @@ public static function forNodeTypeNames( )); } + public static function forNodeTypeNamesWithoutWorkspace( + ContentRepositoryId $contentRepositoryId, + NodeTypeNames $nodeTypeNames + ): self { + return new self(...array_map( + fn (NodeTypeName $nodeTypeName): CacheTag => CacheTag::forNodeTypeName( + $contentRepositoryId, + null, + $nodeTypeName + ), + iterator_to_array($nodeTypeNames) + )); + } + public function add(CacheTag $cacheTag): self { $tags = $this->tags; diff --git a/Neos.Neos/Classes/Fusion/Cache/ContentCacheFlusher.php b/Neos.Neos/Classes/Fusion/Cache/ContentCacheFlusher.php index daea26cfeec..59e5908748f 100644 --- a/Neos.Neos/Classes/Fusion/Cache/ContentCacheFlusher.php +++ b/Neos.Neos/Classes/Fusion/Cache/ContentCacheFlusher.php @@ -72,7 +72,7 @@ public function flushNodeAggregate( $tagsToFlush[ContentCache::TAG_EVERYTHING] = 'which were tagged with "Everything".'; $tagsToFlush = array_merge( - $this->collectTagsForChangeOnNodeAggregate($contentRepository, $workspaceName, $nodeAggregateId), + $this->collectTagsForChangeOnNodeAggregate($contentRepository, $workspaceName, $nodeAggregateId, $workspaceName), $tagsToFlush ); @@ -80,12 +80,17 @@ public function flushNodeAggregate( } /** + * WorkspaceNameToFlush is nullable, so we can flush all workspaces as no specific workspaceName is provided. + * This is needed to flush nodes on asset changes, as the asset can get rendered in all workspaces, but lives + * usually only in live workspace. + * * @return array */ private function collectTagsForChangeOnNodeAggregate( ContentRepository $contentRepository, WorkspaceName $workspaceName, - NodeAggregateId $nodeAggregateId + NodeAggregateId $nodeAggregateId, + ?WorkspaceName $workspaceNameToFlush ): array { $contentGraph = $contentRepository->getContentGraph($workspaceName); @@ -96,12 +101,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, $workspaceName, $nodeAggregateId); + $tagsToFlush = $this->collectTagsForChangeOnNodeIdentifier($contentRepository->id, $workspaceNameToFlush, $nodeAggregateId); $tagsToFlush = array_merge($this->collectTagsForChangeOnNodeType( $nodeAggregate->nodeTypeName, $contentRepository->id, - $workspaceName, + $workspaceNameToFlush, $nodeAggregateId, $contentRepository ), $tagsToFlush); @@ -159,7 +164,7 @@ private function collectTagsForChangeOnNodeAggregate( */ private function collectTagsForChangeOnNodeIdentifier( ContentRepositoryId $contentRepositoryId, - WorkspaceName $workspaceName, + ?WorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, ): array { $tagsToFlush = []; @@ -192,7 +197,7 @@ private function collectTagsForChangeOnNodeIdentifier( private function collectTagsForChangeOnNodeType( NodeTypeName $nodeTypeName, ContentRepositoryId $contentRepositoryId, - WorkspaceName $workspaceName, + ?WorkspaceName $workspaceName, ?NodeAggregateId $referenceNodeIdentifier, ContentRepository $contentRepository ): array { @@ -305,7 +310,8 @@ public function registerAssetChange(AssetInterface $asset): void $this->collectTagsForChangeOnNodeAggregate( $contentRepository, $workspaceName, - $usage->nodeAggregateId + $usage->nodeAggregateId, + null ), $tagsToFlush ); diff --git a/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php b/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php index 5901939576b..c4d7485b7d0 100644 --- a/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php +++ b/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php @@ -53,7 +53,10 @@ public function nodeTag(iterable|Node $nodes): array $nodes = iterator_to_array($nodes); } - return CacheTagSet::forNodeAggregatesFromNodes(Nodes::fromArray($nodes))->toStringArray(); + return array_merge( + CacheTagSet::forNodeAggregatesFromNodes(Nodes::fromArray($nodes))->toStringArray(), + CacheTagSet::forNodeAggregatesFromNodesWithoutWorkspace(Nodes::fromArray($nodes))->toStringArray(), + ); } public function entryIdentifierForNode(Node $node): NodeCacheEntryIdentifier @@ -94,11 +97,17 @@ public function nodeTypeTag(string|iterable $nodeTypes, Node $contextNode): arra $nodeTypes = iterator_to_array($nodeTypes); } - return CacheTagSet::forNodeTypeNames( - $contextNode->contentRepositoryId, - $contextNode->workspaceName, - NodeTypeNames::fromStringArray($nodeTypes) - )->toStringArray(); + return array_merge( + CacheTagSet::forNodeTypeNames( + $contextNode->contentRepositoryId, + $contextNode->workspaceName, + NodeTypeNames::fromStringArray($nodeTypes) + )->toStringArray(), + CacheTagSet::forNodeTypeNamesWithoutWorkspace( + $contextNode->contentRepositoryId, + NodeTypeNames::fromStringArray($nodeTypes) + )->toStringArray(), + ); } /** @@ -118,9 +127,10 @@ public function descendantOfTag(iterable|Node $nodes): array $nodes = iterator_to_array($nodes); } - return CacheTagSet::forDescendantOfNodesFromNodes( - Nodes::fromArray($nodes) - )->toStringArray(); + return array_merge( + CacheTagSet::forDescendantOfNodesFromNodes(Nodes::fromArray($nodes))->toStringArray(), + CacheTagSet::forDescendantOfNodesFromNodesWithoutWorkspace(Nodes::fromArray($nodes))->toStringArray(), + ); } /** From 3614a5b84ab4e42d67855733cf5cace853a44cbe Mon Sep 17 00:00:00 2001 From: Denny Lubitz Date: Mon, 3 Jun 2024 22:57:05 +0200 Subject: [PATCH 2/6] BUGFIX: Flush node content caches without workspace name --- Neos.Neos/Classes/Fusion/Cache/CacheTag.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Neos.Neos/Classes/Fusion/Cache/CacheTag.php b/Neos.Neos/Classes/Fusion/Cache/CacheTag.php index 98b8e1dcabf..b471c458ade 100644 --- a/Neos.Neos/Classes/Fusion/Cache/CacheTag.php +++ b/Neos.Neos/Classes/Fusion/Cache/CacheTag.php @@ -157,6 +157,5 @@ protected static function getHashForWorkspaceNameAndContentRepositoryId( return sha1($workspaceName->value . '@' . $contentRepositoryId->value); } return sha1($contentRepositoryId->value); - } } From 36e8c505a0c2626b28cc6df91cf33477fcb4cff5 Mon Sep 17 00:00:00 2001 From: Denny Lubitz Date: Tue, 4 Jun 2024 00:26:17 +0200 Subject: [PATCH 3/6] BUGFIX: Flush node content caches without workspace name --- .../Unit/Fusion/Helper/CachingHelperTest.php | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/Neos.Neos/Tests/Unit/Fusion/Helper/CachingHelperTest.php b/Neos.Neos/Tests/Unit/Fusion/Helper/CachingHelperTest.php index 2f9916b6399..3b9992926f9 100644 --- a/Neos.Neos/Tests/Unit/Fusion/Helper/CachingHelperTest.php +++ b/Neos.Neos/Tests/Unit/Fusion/Helper/CachingHelperTest.php @@ -57,12 +57,20 @@ public function nodeTypeTagDataProvider() $nodeTypeName3 = 'Neos.Neos:Moo'; return [ - [$nodeTypeName1, ['NodeType_364cfc8e70b2baa23dbd14503d2bd00e063829e7_Neos_Neos-Foo']], + [$nodeTypeName1, + [ + 'NodeType_364cfc8e70b2baa23dbd14503d2bd00e063829e7_Neos_Neos-Foo', + 'NodeType_7505d64a54e061b7acd54ccd58b49dc43500b635_Neos_Neos-Foo', + ] + ], [[$nodeTypeName1, $nodeTypeName2, $nodeTypeName3], [ 'NodeType_364cfc8e70b2baa23dbd14503d2bd00e063829e7_Neos_Neos-Foo', 'NodeType_364cfc8e70b2baa23dbd14503d2bd00e063829e7_Neos_Neos-Bar', 'NodeType_364cfc8e70b2baa23dbd14503d2bd00e063829e7_Neos_Neos-Moo', + 'NodeType_7505d64a54e061b7acd54ccd58b49dc43500b635_Neos_Neos-Foo', + 'NodeType_7505d64a54e061b7acd54ccd58b49dc43500b635_Neos_Neos-Bar', + 'NodeType_7505d64a54e061b7acd54ccd58b49dc43500b635_Neos_Neos-Moo', ] ], [(new \ArrayObject([$nodeTypeName1, $nodeTypeName2, $nodeTypeName3])), @@ -70,6 +78,9 @@ public function nodeTypeTagDataProvider() 'NodeType_364cfc8e70b2baa23dbd14503d2bd00e063829e7_Neos_Neos-Foo', 'NodeType_364cfc8e70b2baa23dbd14503d2bd00e063829e7_Neos_Neos-Bar', 'NodeType_364cfc8e70b2baa23dbd14503d2bd00e063829e7_Neos_Neos-Moo', + 'NodeType_7505d64a54e061b7acd54ccd58b49dc43500b635_Neos_Neos-Foo', + 'NodeType_7505d64a54e061b7acd54ccd58b49dc43500b635_Neos_Neos-Bar', + 'NodeType_7505d64a54e061b7acd54ccd58b49dc43500b635_Neos_Neos-Moo', ] ], ]; @@ -103,15 +114,25 @@ public function nodeDataProvider() $node2 = $this->createNode(NodeAggregateId::fromString($nodeIdentifier2)); return [ - [$node1, ['Node_364cfc8e70b2baa23dbd14503d2bd00e063829e7_ca511a55-c5c0-f7d7-8d71-8edeffc75306']], - [[$node1], ['Node_364cfc8e70b2baa23dbd14503d2bd00e063829e7_ca511a55-c5c0-f7d7-8d71-8edeffc75306']], + [$node1, [ + 'Node_364cfc8e70b2baa23dbd14503d2bd00e063829e7_ca511a55-c5c0-f7d7-8d71-8edeffc75306', + 'Node_7505d64a54e061b7acd54ccd58b49dc43500b635_ca511a55-c5c0-f7d7-8d71-8edeffc75306', + ]], + [[$node1], [ + 'Node_364cfc8e70b2baa23dbd14503d2bd00e063829e7_ca511a55-c5c0-f7d7-8d71-8edeffc75306', + 'Node_7505d64a54e061b7acd54ccd58b49dc43500b635_ca511a55-c5c0-f7d7-8d71-8edeffc75306', + ]], [[$node1, $node2], [ 'Node_364cfc8e70b2baa23dbd14503d2bd00e063829e7_ca511a55-c5c0-f7d7-8d71-8edeffc75306', - 'Node_364cfc8e70b2baa23dbd14503d2bd00e063829e7_7005c7cf-4d19-ce36-0873-476b6cadb71a' + 'Node_364cfc8e70b2baa23dbd14503d2bd00e063829e7_7005c7cf-4d19-ce36-0873-476b6cadb71a', + 'Node_7505d64a54e061b7acd54ccd58b49dc43500b635_ca511a55-c5c0-f7d7-8d71-8edeffc75306', + 'Node_7505d64a54e061b7acd54ccd58b49dc43500b635_7005c7cf-4d19-ce36-0873-476b6cadb71a', ]], [(new \ArrayObject([$node1, $node2])), [ 'Node_364cfc8e70b2baa23dbd14503d2bd00e063829e7_ca511a55-c5c0-f7d7-8d71-8edeffc75306', - 'Node_364cfc8e70b2baa23dbd14503d2bd00e063829e7_7005c7cf-4d19-ce36-0873-476b6cadb71a' + 'Node_364cfc8e70b2baa23dbd14503d2bd00e063829e7_7005c7cf-4d19-ce36-0873-476b6cadb71a', + 'Node_7505d64a54e061b7acd54ccd58b49dc43500b635_ca511a55-c5c0-f7d7-8d71-8edeffc75306', + 'Node_7505d64a54e061b7acd54ccd58b49dc43500b635_7005c7cf-4d19-ce36-0873-476b6cadb71a', ]] ]; } @@ -169,15 +190,25 @@ public function descendantOfDataProvider() return [ - [$node1, ['DescendantOf_364cfc8e70b2baa23dbd14503d2bd00e063829e7_ca511a55-c5c0-f7d7-8d71-8edeffc75306']], - [[$node1], ['DescendantOf_364cfc8e70b2baa23dbd14503d2bd00e063829e7_ca511a55-c5c0-f7d7-8d71-8edeffc75306']], + [$node1, [ + 'DescendantOf_364cfc8e70b2baa23dbd14503d2bd00e063829e7_ca511a55-c5c0-f7d7-8d71-8edeffc75306', + 'DescendantOf_7505d64a54e061b7acd54ccd58b49dc43500b635_ca511a55-c5c0-f7d7-8d71-8edeffc75306', + ]], + [[$node1], [ + 'DescendantOf_364cfc8e70b2baa23dbd14503d2bd00e063829e7_ca511a55-c5c0-f7d7-8d71-8edeffc75306', + 'DescendantOf_7505d64a54e061b7acd54ccd58b49dc43500b635_ca511a55-c5c0-f7d7-8d71-8edeffc75306', + ]], [[$node1, $node2], [ 'DescendantOf_364cfc8e70b2baa23dbd14503d2bd00e063829e7_ca511a55-c5c0-f7d7-8d71-8edeffc75306', - 'DescendantOf_364cfc8e70b2baa23dbd14503d2bd00e063829e7_7005c7cf-4d19-ce36-0873-476b6cadb71a' + 'DescendantOf_364cfc8e70b2baa23dbd14503d2bd00e063829e7_7005c7cf-4d19-ce36-0873-476b6cadb71a', + 'DescendantOf_7505d64a54e061b7acd54ccd58b49dc43500b635_ca511a55-c5c0-f7d7-8d71-8edeffc75306', + 'DescendantOf_7505d64a54e061b7acd54ccd58b49dc43500b635_7005c7cf-4d19-ce36-0873-476b6cadb71a', ]], [(new \ArrayObject([$node1, $node2])), [ 'DescendantOf_364cfc8e70b2baa23dbd14503d2bd00e063829e7_ca511a55-c5c0-f7d7-8d71-8edeffc75306', - 'DescendantOf_364cfc8e70b2baa23dbd14503d2bd00e063829e7_7005c7cf-4d19-ce36-0873-476b6cadb71a' + 'DescendantOf_364cfc8e70b2baa23dbd14503d2bd00e063829e7_7005c7cf-4d19-ce36-0873-476b6cadb71a', + 'DescendantOf_7505d64a54e061b7acd54ccd58b49dc43500b635_ca511a55-c5c0-f7d7-8d71-8edeffc75306', + 'DescendantOf_7505d64a54e061b7acd54ccd58b49dc43500b635_7005c7cf-4d19-ce36-0873-476b6cadb71a', ]] ]; } From 8b8e4f4e3d60d2d5f77c100958bcab831ab94b10 Mon Sep 17 00:00:00 2001 From: Denny Lubitz Date: Tue, 4 Jun 2024 10:11:38 +0200 Subject: [PATCH 4/6] BUGFIX: Flush node content caches without workspace name --- Neos.Neos/Classes/Fusion/Cache/CacheTag.php | 41 +++++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/Neos.Neos/Classes/Fusion/Cache/CacheTag.php b/Neos.Neos/Classes/Fusion/Cache/CacheTag.php index b471c458ade..1c0eb7867f7 100644 --- a/Neos.Neos/Classes/Fusion/Cache/CacheTag.php +++ b/Neos.Neos/Classes/Fusion/Cache/CacheTag.php @@ -18,6 +18,8 @@ class CacheTag { protected const PATTERN = '/^[a-zA-Z0-9_%\-&]{1,250}$/'; + protected const NODE_PREFIX = 'Node'; + protected const PARTS_DELIMITER = '_'; private function __construct( public readonly string $value @@ -32,13 +34,28 @@ private function __construct( final public static function forNodeAggregate( ContentRepositoryId $contentRepositoryId, - ?WorkspaceName $workspaceName, + WorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, ): self { return new self( - 'Node_' + self::NODE_PREFIX + . self::PARTS_DELIMITER . self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId) - . '_' . $nodeAggregateId->value + . self::PARTS_DELIMITER + . $nodeAggregateId->value + ); + } + + final public static function forNodeAggregateWithoutWorkspaceName( + ContentRepositoryId $contentRepositoryId, + NodeAggregateId $nodeAggregateId, + ): self { + return new self( + self::NODE_PREFIX + . self::PARTS_DELIMITER + . self::getHashForContentRepositoryId($contentRepositoryId) + . self::PARTS_DELIMITER + . $nodeAggregateId->value ); } @@ -53,9 +70,8 @@ final public static function forNodeAggregateFromNode(Node $node): self final public static function forNodeAggregateFromNodeWithoutWorkspace(Node $node): self { - return self::forNodeAggregate( + return self::forNodeAggregateWithoutWorkspaceName( $node->contentRepositoryId, - null, $node->aggregateId ); } @@ -83,9 +99,8 @@ final public static function forDescendantOfNodeFromNode(Node $node): self final public static function forDescendantOfNodeFromNodeWithoutWorkspace(Node $node): self { - return self::forDescendantOfNode( + return self::forNodeAggregateWithoutWorkspaceName( $node->contentRepositoryId, - null, $node->aggregateId ); } @@ -113,9 +128,8 @@ final public static function forAncestorNodeFromNode(Node $node): self final public static function forAncestorNodeFromNodeWithoutWorkspace(Node $node): self { - return self::forAncestorNode( + return self::forNodeAggregateWithoutWorkspaceName( $node->contentRepositoryId, - null, $node->aggregateId ); } @@ -153,9 +167,12 @@ protected static function getHashForWorkspaceNameAndContentRepositoryId( ?WorkspaceName $workspaceName, ContentRepositoryId $contentRepositoryId, ): string { - if ($workspaceName) { - return sha1($workspaceName->value . '@' . $contentRepositoryId->value); - } + return sha1($workspaceName->value . '@' . $contentRepositoryId->value); + } + + protected static function getHashForContentRepositoryId( + ContentRepositoryId $contentRepositoryId, + ): string { return sha1($contentRepositoryId->value); } } From 2a6a8ccc81b704830efe5240205b118c1945a986 Mon Sep 17 00:00:00 2001 From: Bastian Waidelich Date: Tue, 4 Jun 2024 14:33:55 +0200 Subject: [PATCH 5/6] Adjust `CacheTag` to use special `CacheTagWorkspaceName::ANY` instead of `null` --- Neos.Neos/Classes/Fusion/Cache/CacheTag.php | 111 ++++++------------ .../Classes/Fusion/Cache/CacheTagSet.php | 46 +++----- .../Fusion/Cache/CacheTagWorkspaceName.php | 13 ++ .../Fusion/Cache/ContentCacheFlusher.php | 11 +- .../Classes/Fusion/Helper/CachingHelper.php | 4 +- 5 files changed, 76 insertions(+), 109 deletions(-) create mode 100644 Neos.Neos/Classes/Fusion/Cache/CacheTagWorkspaceName.php diff --git a/Neos.Neos/Classes/Fusion/Cache/CacheTag.php b/Neos.Neos/Classes/Fusion/Cache/CacheTag.php index 1c0eb7867f7..40510f233ba 100644 --- a/Neos.Neos/Classes/Fusion/Cache/CacheTag.php +++ b/Neos.Neos/Classes/Fusion/Cache/CacheTag.php @@ -18,8 +18,11 @@ class CacheTag { protected const PATTERN = '/^[a-zA-Z0-9_%\-&]{1,250}$/'; - protected const NODE_PREFIX = 'Node'; - protected const PARTS_DELIMITER = '_'; + protected const PREFIX_NODE = 'Node'; + protected const PREFIX_DESCENDANT_OF = 'DescendantOf'; + protected const PREFIX_ANCESTOR = 'Ancestor'; + protected const PREFIX_NODE_TYPE = 'NodeType'; + protected const PREFIX_DYNAMIC_NODE_TAG = 'DynamicNodeTag'; private function __construct( public readonly string $value @@ -32,30 +35,20 @@ private function __construct( } } - final public static function forNodeAggregate( - ContentRepositoryId $contentRepositoryId, - WorkspaceName $workspaceName, - NodeAggregateId $nodeAggregateId, - ): self { - return new self( - self::NODE_PREFIX - . self::PARTS_DELIMITER - . self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId) - . self::PARTS_DELIMITER - . $nodeAggregateId->value - ); + private static function fromSegments(string ...$segments): self + { + return new self(implode('_', $segments)); } - final public static function forNodeAggregateWithoutWorkspaceName( + final public static function forNodeAggregate( ContentRepositoryId $contentRepositoryId, + WorkspaceName|CacheTagWorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, ): self { - return new self( - self::NODE_PREFIX - . self::PARTS_DELIMITER - . self::getHashForContentRepositoryId($contentRepositoryId) - . self::PARTS_DELIMITER - . $nodeAggregateId->value + return self::fromSegments( + self::PREFIX_NODE, + self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId), + $nodeAggregateId->value, ); } @@ -68,23 +61,15 @@ final public static function forNodeAggregateFromNode(Node $node): self ); } - final public static function forNodeAggregateFromNodeWithoutWorkspace(Node $node): self - { - return self::forNodeAggregateWithoutWorkspaceName( - $node->contentRepositoryId, - $node->aggregateId - ); - } - final public static function forDescendantOfNode( ContentRepositoryId $contentRepositoryId, - ?WorkspaceName $workspaceName, + WorkspaceName|CacheTagWorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, ): self { - return new self( - 'DescendantOf_' - . self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId) - . '_' . $nodeAggregateId->value + return self::fromSegments( + self::PREFIX_DESCENDANT_OF, + self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId), + $nodeAggregateId->value, ); } @@ -97,23 +82,15 @@ final public static function forDescendantOfNodeFromNode(Node $node): self ); } - final public static function forDescendantOfNodeFromNodeWithoutWorkspace(Node $node): self - { - return self::forNodeAggregateWithoutWorkspaceName( - $node->contentRepositoryId, - $node->aggregateId - ); - } - final public static function forAncestorNode( ContentRepositoryId $contentRepositoryId, - ?WorkspaceName $workspaceName, + WorkspaceName|CacheTagWorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, ): self { - return new self( - 'Ancestor_' - . self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId) - . '_' . $nodeAggregateId->value + return self::fromSegments( + self::PREFIX_ANCESTOR, + self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId), + $nodeAggregateId->value, ); } @@ -126,35 +103,27 @@ final public static function forAncestorNodeFromNode(Node $node): self ); } - final public static function forAncestorNodeFromNodeWithoutWorkspace(Node $node): self - { - return self::forNodeAggregateWithoutWorkspaceName( - $node->contentRepositoryId, - $node->aggregateId - ); - } - final public static function forNodeTypeName( ContentRepositoryId $contentRepositoryId, - ?WorkspaceName $workspaceName, + WorkspaceName|CacheTagWorkspaceName $workspaceName, NodeTypeName $nodeTypeName, ): self { - return new self( - 'NodeType_' - . self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId) - . '_' . \strtr($nodeTypeName->value, '.:', '_-') + return self::fromSegments( + self::PREFIX_NODE_TYPE, + self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId), + \strtr($nodeTypeName->value, '.:', '_-'), ); } final public static function forDynamicNodeAggregate( ContentRepositoryId $contentRepositoryId, - ?WorkspaceName $workspaceName, + WorkspaceName|CacheTagWorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, ): self { - return new self( - 'DynamicNodeTag_' - . self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId) - . '_' . $nodeAggregateId->value + return self::fromSegments( + self::PREFIX_DYNAMIC_NODE_TAG, + self::getHashForWorkspaceNameAndContentRepositoryId($workspaceName, $contentRepositoryId), + $nodeAggregateId->value, ); } @@ -164,15 +133,11 @@ final public static function fromString(string $string): self } protected static function getHashForWorkspaceNameAndContentRepositoryId( - ?WorkspaceName $workspaceName, + WorkspaceName|CacheTagWorkspaceName $workspaceName, ContentRepositoryId $contentRepositoryId, ): string { - return sha1($workspaceName->value . '@' . $contentRepositoryId->value); - } - - protected static function getHashForContentRepositoryId( - ContentRepositoryId $contentRepositoryId, - ): string { - return sha1($contentRepositoryId->value); + return sha1( + $workspaceName === CacheTagWorkspaceName::ANY ? $contentRepositoryId->value : $workspaceName->value . '@' . $contentRepositoryId->value + ); } } diff --git a/Neos.Neos/Classes/Fusion/Cache/CacheTagSet.php b/Neos.Neos/Classes/Fusion/Cache/CacheTagSet.php index e8e3ec2a603..e96bfd769cb 100644 --- a/Neos.Neos/Classes/Fusion/Cache/CacheTagSet.php +++ b/Neos.Neos/Classes/Fusion/Cache/CacheTagSet.php @@ -38,19 +38,19 @@ public static function forDescendantOfNodesFromNodes( Nodes $nodes ): self { return new self(...array_map( - fn (Node $node): CacheTag => CacheTag::forDescendantOfNodeFromNode( - $node - ), - iterator_to_array($nodes) + CacheTag::forDescendantOfNodeFromNode(...), + iterator_to_array($nodes), )); } public static function forDescendantOfNodesFromNodesWithoutWorkspace( - Nodes $nodes + Nodes $nodes, ): self { return new self(...array_map( - fn (Node $node): CacheTag => CacheTag::forDescendantOfNodeFromNodeWithoutWorkspace( - $node + static fn (Node $node) => CacheTag::forDescendantOfNode( + $node->contentRepositoryId, + CacheTagWorkspaceName::ANY, + $node->aggregateId, ), iterator_to_array($nodes) )); @@ -60,9 +60,7 @@ public static function forNodeAggregatesFromNodes( Nodes $nodes ): self { return new self(...array_map( - fn (Node $node): CacheTag => CacheTag::forNodeAggregateFromNode( - $node - ), + CacheTag::forNodeAggregateFromNode(...), iterator_to_array($nodes) )); } @@ -71,20 +69,22 @@ public static function forNodeAggregatesFromNodesWithoutWorkspace( Nodes $nodes ): self { return new self(...array_map( - fn (Node $node): CacheTag => CacheTag::forNodeAggregateFromNodeWithoutWorkspace( - $node + static fn (Node $node) => CacheTag::forNodeAggregate( + $node->contentRepositoryId, + CacheTagWorkspaceName::ANY, + $node->aggregateId ), - iterator_to_array($nodes) + iterator_to_array($nodes), )); } public static function forNodeTypeNames( ContentRepositoryId $contentRepositoryId, - WorkspaceName $workspaceName, + WorkspaceName|CacheTagWorkspaceName $workspaceName, NodeTypeNames $nodeTypeNames ): self { return new self(...array_map( - fn (NodeTypeName $nodeTypeName): CacheTag => CacheTag::forNodeTypeName( + static fn (NodeTypeName $nodeTypeName): CacheTag => CacheTag::forNodeTypeName( $contentRepositoryId, $workspaceName, $nodeTypeName @@ -93,20 +93,6 @@ public static function forNodeTypeNames( )); } - public static function forNodeTypeNamesWithoutWorkspace( - ContentRepositoryId $contentRepositoryId, - NodeTypeNames $nodeTypeNames - ): self { - return new self(...array_map( - fn (NodeTypeName $nodeTypeName): CacheTag => CacheTag::forNodeTypeName( - $contentRepositoryId, - null, - $nodeTypeName - ), - iterator_to_array($nodeTypeNames) - )); - } - public function add(CacheTag $cacheTag): self { $tags = $this->tags; @@ -121,7 +107,7 @@ public function add(CacheTag $cacheTag): self public function toStringArray(): array { return array_map( - fn (CacheTag $tag): string => $tag->value, + static fn (CacheTag $tag): string => $tag->value, array_values($this->tags) ); } diff --git a/Neos.Neos/Classes/Fusion/Cache/CacheTagWorkspaceName.php b/Neos.Neos/Classes/Fusion/Cache/CacheTagWorkspaceName.php new file mode 100644 index 00000000000..2aaa79b476e --- /dev/null +++ b/Neos.Neos/Classes/Fusion/Cache/CacheTagWorkspaceName.php @@ -0,0 +1,13 @@ +collectTagsForChangeOnNodeAggregate($contentRepository, $workspaceName, $nodeAggregateId, $workspaceName), + $this->collectTagsForChangeOnNodeAggregate($contentRepository, $workspaceName, $nodeAggregateId, false), $tagsToFlush ); @@ -90,7 +90,7 @@ private function collectTagsForChangeOnNodeAggregate( ContentRepository $contentRepository, WorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, - ?WorkspaceName $workspaceNameToFlush + bool $anyWorkspace, ): array { $contentGraph = $contentRepository->getContentGraph($workspaceName); @@ -101,6 +101,7 @@ private function collectTagsForChangeOnNodeAggregate( // Node Aggregate was removed in the meantime, so no need to clear caches on this one anymore. return []; } + $workspaceNameToFlush = $anyWorkspace ? CacheTagWorkspaceName::ANY : $workspaceName; $tagsToFlush = $this->collectTagsForChangeOnNodeIdentifier($contentRepository->id, $workspaceNameToFlush, $nodeAggregateId); $tagsToFlush = array_merge($this->collectTagsForChangeOnNodeType( @@ -164,7 +165,7 @@ private function collectTagsForChangeOnNodeAggregate( */ private function collectTagsForChangeOnNodeIdentifier( ContentRepositoryId $contentRepositoryId, - ?WorkspaceName $workspaceName, + WorkspaceName|CacheTagWorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, ): array { $tagsToFlush = []; @@ -197,7 +198,7 @@ private function collectTagsForChangeOnNodeIdentifier( private function collectTagsForChangeOnNodeType( NodeTypeName $nodeTypeName, ContentRepositoryId $contentRepositoryId, - ?WorkspaceName $workspaceName, + WorkspaceName|CacheTagWorkspaceName $workspaceName, ?NodeAggregateId $referenceNodeIdentifier, ContentRepository $contentRepository ): array { @@ -311,7 +312,7 @@ public function registerAssetChange(AssetInterface $asset): void $contentRepository, $workspaceName, $usage->nodeAggregateId, - null + true ), $tagsToFlush ); diff --git a/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php b/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php index c4d7485b7d0..11e56aa3d26 100644 --- a/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php +++ b/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php @@ -25,6 +25,7 @@ use Neos\Neos\Domain\Model\NodeCacheEntryIdentifier; use Neos\Neos\Fusion\Cache\CacheTag; use Neos\Neos\Fusion\Cache\CacheTagSet; +use Neos\Neos\Fusion\Cache\CacheTagWorkspaceName; /** * Caching helper to make cache tag generation easier. @@ -103,8 +104,9 @@ public function nodeTypeTag(string|iterable $nodeTypes, Node $contextNode): arra $contextNode->workspaceName, NodeTypeNames::fromStringArray($nodeTypes) )->toStringArray(), - CacheTagSet::forNodeTypeNamesWithoutWorkspace( + CacheTagSet::forNodeTypeNames( $contextNode->contentRepositoryId, + CacheTagWorkspaceName::ANY, NodeTypeNames::fromStringArray($nodeTypes) )->toStringArray(), ); From 2b4890cb5596cab3e921bdc027b01376c4c11bb9 Mon Sep 17 00:00:00 2001 From: Denny Lubitz Date: Tue, 4 Jun 2024 14:51:20 +0200 Subject: [PATCH 6/6] BUGFIX: Flush node content caches without workspace name --- Neos.Neos/Classes/Fusion/Cache/ContentCacheFlusher.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Neos.Neos/Classes/Fusion/Cache/ContentCacheFlusher.php b/Neos.Neos/Classes/Fusion/Cache/ContentCacheFlusher.php index 19ddc3fffbf..992d776e12d 100644 --- a/Neos.Neos/Classes/Fusion/Cache/ContentCacheFlusher.php +++ b/Neos.Neos/Classes/Fusion/Cache/ContentCacheFlusher.php @@ -80,9 +80,8 @@ public function flushNodeAggregate( } /** - * WorkspaceNameToFlush is nullable, so we can flush all workspaces as no specific workspaceName is provided. - * This is needed to flush nodes on asset changes, as the asset can get rendered in all workspaces, but lives - * usually only in live workspace. + * @param bool $anyWorkspace This is needed to flush nodes on asset changes, as the asset can get rendered in all workspaces, but lives + * usually only in live workspace. * * @return array */