Skip to content

Commit

Permalink
Merge branch '9.0' into feature/4133-stricter-value-objects
Browse files Browse the repository at this point in the history
  • Loading branch information
bwaidelich committed Apr 2, 2023
2 parents 3fbf9fd + 75cb0c0 commit cfe3bdc
Show file tree
Hide file tree
Showing 21 changed files with 246 additions and 163 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Neos\ContentRepository\Core\EventStore\EventPersister;
use Neos\ContentRepository\Core\EventStore\Events;
use Neos\ContentRepository\Core\EventStore\EventsToPublish;
use Neos\ContentRepository\Core\Feature\ContentStreamRemoval\Command\RemoveContentStream;
use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdsToPublishOrDiscard;
use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\DiscardIndividualNodesFromWorkspace;
use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\DiscardWorkspace;
Expand Down Expand Up @@ -555,6 +556,12 @@ private function handlePublishIndividualNodesFromWorkspace(
),
);

// to avoid dangling content streams, we need to remove our temporary content stream (whose events
// have already been published)
$contentRepository->handle(new RemoveContentStream(
$matchingContentStream
));

// It is safe to only return the last command result,
// as the commands which were rebased are already executed "synchronously"
return new EventsToPublish(
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,84 @@
/**
* Internal - implementation detail of {@see ContentStreamPruner}
*
* This projection tracks whether content streams are currently in use or not. Each content stream
* is first CREATED or FORKED, and then moves to the IN_USE or REBASE_ERROR states; or is removed directly
* in case of temporary content streams.
*
* FORKING: Content streams are forked from a base content stream. It can happen that the base content
* stream is NO_LONGER_IN_USE, but the child content stream is still IN_USE_BY_WORKSPACE. In this case,
* the base content stream can go to removed=1 (removed from all projections), but needs to be retained
* in the event store: If we do a full replay, we need the events of the base content stream before the
* fork happened to rebuild the child content stream.
* This logic is done in {@see findUnusedAndRemovedContentStreams}.
*
* TEMPORARY content streams: Projections should take care to dispose their temporary content streams,
* by triggering a ContentStreamWasRemoved event after the content stream is no longer used.
*
* │ │
* │(for root │during
* │ content │rebase
* ▼ stream) ▼
* ┌──────────┐ ┌──────────┐ Temporary
* │ CREATED │ │ FORKED │────┐ states
* └──────────┘ └──────────┘ for
* │ │ temporary
* ├───────────────────────┤ content
* ▼ ▼ streams
* ┌───────────────────┐ ┌──────────────┐ │
* │IN_USE_BY_WORKSPACE│ │ REBASE_ERROR │ │
* └───────────────────┘ └──────────────┘ │ Persistent
* │ │ │ States
* ▼ │ │
* ┌───────────────────┐ │ │
* │ NO_LONGER_IN_USE │ │ │
* └───────────────────┘ │ │
* │ │ │
* └──────────┬────────────┘ │
* ▼ │
* ┌────────────────────────────────────────┐ │
* │ removed=1 │ │
* │ => removed from all other projections │◀─┘
* └────────────────────────────────────────┘ Cleanup
* │
* ▼
* ┌────────────────────────────────────────┐
* │ completely deleted from event stream │
* └────────────────────────────────────────┘
*
* @internal
*/
final class ContentStreamFinder implements ProjectionStateInterface
{
/**
* the content stream was created, but not yet assigned to a workspace.
*
* **temporary state** which should not appear if the system is idle (for content streams which are used with workspaces).
*/
public const STATE_CREATED = 'CREATED';

/**
* STATE_FORKED means the content stream was forked from an existing content stream, but not yet assigned
* to a workspace.
*
* **temporary state** which should not appear if the system is idle (for content streams which are used with workspaces).
*/
public const STATE_FORKED = 'FORKED';

/**
* the content stream is currently referenced as the "active" content stream by a workspace.
*/
public const STATE_IN_USE_BY_WORKSPACE = 'IN_USE_BY_WORKSPACE';
public const STATE_REBASING = 'REBASING';

/**
* a workspace was tried to be rebased, and during the rebase an error occured. This is the content stream
* which contains the errored state - so that we can recover content from it (probably manually)
*/
public const STATE_REBASE_ERROR = 'REBASE_ERROR';

/**
* the content stream is not used anymore, and can be removed.
*/
public const STATE_NO_LONGER_IN_USE = 'NO_LONGER_IN_USE';

public function __construct(
Expand All @@ -54,18 +124,29 @@ public function findAllIds(): iterable
)->fetchAllAssociative();

return array_map(
fn (array $databaseRow): ContentStreamId => ContentStreamId::fromString(
fn(array $databaseRow): ContentStreamId => ContentStreamId::fromString(
$databaseRow['contentStreamId']
),
$databaseRows
);
}

/**
* @param bool $findTemporaryContentStreams if TRUE, will find all content streams not bound to a workspace
* @return array<int,ContentStreamId>
*/
public function findUnusedContentStreams(): iterable
public function findUnusedContentStreams(bool $findTemporaryContentStreams): iterable
{
$states = [
self::STATE_NO_LONGER_IN_USE,
self::STATE_REBASE_ERROR,
];

if ($findTemporaryContentStreams === true) {
$states[] = self::STATE_CREATED;
$states[] = self::STATE_FORKED;
}

$connection = $this->client->getConnection();
$databaseRows = $connection->executeQuery(
'
Expand All @@ -74,10 +155,7 @@ public function findUnusedContentStreams(): iterable
AND state IN (:state)
',
[
'state' => [
self::STATE_NO_LONGER_IN_USE,
self::STATE_REBASE_ERROR,
]
'state' => $states
],
[
'state' => Connection::PARAM_STR_ARRAY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
use Neos\EventStore\Model\EventStream\EventStreamInterface;

/**
* See {@see ContentStreamFinder} for explanation.
*
* @internal
* @implements ProjectionInterface<ContentStreamFinder>
*/
Expand Down Expand Up @@ -84,6 +86,13 @@ private function setupTables(): void
throw new \RuntimeException('Failed to retrieve Schema Manager', 1625653914);
}

// MIGRATIONS
$currentSchema = $schemaManager->createSchema();
if ($currentSchema->hasTable($this->tableName)) {
// added 2023-04-01
$connection->executeStatement(sprintf("UPDATE %s SET state='FORKED' WHERE state='REBASING'; ", $this->tableName));
}

$schema = new Schema();
$contentStreamTable = $schema->createTable($this->tableName);
$contentStreamTable->addColumn('contentStreamId', Types::STRING)
Expand Down Expand Up @@ -219,7 +228,7 @@ private function whenContentStreamWasForked(ContentStreamWasForked $event, Event
'contentStreamId' => $event->newContentStreamId,
'version' => self::extractVersion($eventEnvelope),
'sourceContentStreamId' => $event->sourceContentStreamId,
'state' => ContentStreamFinder::STATE_REBASING, // TODO: FORKED?
'state' => ContentStreamFinder::STATE_FORKED,
]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
use Neos\ContentRepository\Core\ContentRepository;
use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceInterface;
use Neos\ContentRepository\Core\Feature\ContentStreamEventStreamName;
use Neos\ContentRepository\Core\SharedModel\User\UserId;
use Neos\ContentRepository\Core\Projection\ContentStream\ContentStreamFinder;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\ContentRepository\Core\Feature\ContentStreamRemoval\Command\RemoveContentStream;
use Neos\EventStore\EventStoreInterface;

/**
* For internal implementation details, see {@see ContentStreamFinder}.
*
* @api
*/
class ContentStreamPruner implements ContentRepositoryServiceInterface
Expand All @@ -35,11 +37,18 @@ public function __construct(
* To remove the deleted Content Streams,
* call {@see ContentStreamPruner::pruneRemovedFromEventStream()} afterwards.
*
* By default, only content streams in STATE_NO_LONGER_IN_USE and STATE_REBASE_ERROR will be removed.
* If you also call with $removeTemporary=true, will delete ALL content streams which are currently not assigned
* to a workspace (f.e. dangling ones in FORKED or CREATED.).
*
* @param bool $removeTemporary if TRUE, will delete ALL content streams not bound to a workspace
* @return iterable<int,ContentStreamId> the identifiers of the removed content streams
*/
public function prune(): iterable
public function prune(bool $removeTemporary = false): iterable
{
$unusedContentStreams = $this->contentRepository->getContentStreamFinder()->findUnusedContentStreams();
$unusedContentStreams = $this->contentRepository->getContentStreamFinder()->findUnusedContentStreams(
$removeTemporary
);

foreach ($unusedContentStreams as $contentStream) {
$this->lastCommandResult = $this->contentRepository->handle(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,17 @@ public function importCommand(string $contentStream, string $file = null): void
*
* To remove the deleted Content Streams, use `./flow contentStream:pruneRemovedFromEventStream` after running
* `./flow contentStream:prune`.
*
* By default, only content streams in STATE_NO_LONGER_IN_USE and STATE_REBASE_ERROR will be removed.
* If you also call with "--removeTemporary", will delete ALL content streams which are currently not assigned
* to a workspace (f.e. dangling ones in FORKED or CREATED.).
*/
public function pruneCommand(string $contentRepositoryIdentifier = 'default'): void
public function pruneCommand(string $contentRepositoryIdentifier = 'default', bool $removeTemporary = false): void
{
$contentRepositoryId = ContentRepositoryId::fromString($contentRepositoryIdentifier);
$contentStreamPruner = $this->contentRepositoryRegistry->getService($contentRepositoryId, new ContentStreamPrunerFactory());

$unusedContentStreams = $contentStreamPruner->prune();
$unusedContentStreams = $contentStreamPruner->prune($removeTemporary);
$unusedContentStreamsPresent = false;
foreach ($unusedContentStreams as $contentStream) {
$this->outputFormatted('Removed %s', [$contentStream]);
Expand Down

This file was deleted.

11 changes: 5 additions & 6 deletions Neos.Neos/Classes/Fusion/MenuItemsImplementation.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@

namespace Neos\Neos\Fusion;

use Neos\ContentRepository\Core\NodeType\NodeTypeName;
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindSubtreeFilter;
use Neos\ContentRepository\Core\Projection\ContentGraph\Subtree;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\ContentRepository\Core\Projection\ContentGraph\Subtrees;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds;
use Neos\ContentRepository\Core\Projection\ContentGraph\Nodes;
use Neos\ContentRepository\Core\Projection\ContentGraph\NodeTypeConstraints;
use Neos\ContentRepository\Core\Projection\ContentGraph\NodeTypeConstraintsWithSubNodeTypes;
use Neos\ContentRepository\Core\NodeType\NodeTypeName;
use Neos\ContentRepository\Core\Projection\ContentGraph\Subtree;
use Neos\Fusion\Exception as FusionException;

/**
Expand Down Expand Up @@ -137,9 +136,9 @@ public function getStartingPoint(): ?Node
}

/**
* @return array<int,Node>|null
* @return array<int,Node>|Nodes|null
*/
public function getItemCollection(): ?array
public function getItemCollection(): array|Nodes|null
{
return $this->fusionValue('itemCollection');
}
Expand Down
15 changes: 15 additions & 0 deletions Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,21 @@ private function setupTables(): void
throw new \RuntimeException('Failed to retrieve Schema Manager', 1625653914);
}

// MIGRATIONS
$currentSchema = $schemaManager->createSchema();
if ($currentSchema->hasTable($this->tableName)) {
$tableSchema = $currentSchema->getTable($this->tableName);
// added 2023-03-18
if ($tableSchema->hasColumn('nodeAggregateIdentifier')) {
// table in old format -> we migrate to new.
$connection->executeStatement(sprintf('ALTER TABLE %s RENAME COLUMN nodeAggregateIdentifier TO nodeAggregateId; ', $this->tableName));
}
// added 2023-03-18
if ($tableSchema->hasColumn('contentStreamIdentifier')) {
$connection->executeStatement(sprintf('ALTER TABLE %s RENAME COLUMN contentStreamIdentifier TO contentStreamId; ', $this->tableName));
}
}

$schema = new Schema();
$changeTable = $schema->createTable($this->tableName);
$changeTable->addColumn('contentStreamId', Types::STRING)
Expand Down
2 changes: 1 addition & 1 deletion Neos.Neos/Documentation/References/Signals/Flow.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Flow Signals Reference
======================

This reference was automatically generated from code on 2023-03-31
This reference was automatically generated from code on 2023-04-02


.. _`Flow Signals Reference: AbstractAdvice (``Neos\Flow\Aop\Advice\AbstractAdvice``)`:
Expand Down
2 changes: 1 addition & 1 deletion Neos.Neos/Documentation/References/Validators/Flow.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Flow Validator Reference
========================

This reference was automatically generated from code on 2023-03-31
This reference was automatically generated from code on 2023-04-02


.. _`Flow Validator Reference: AggregateBoundaryValidator`:
Expand Down
2 changes: 1 addition & 1 deletion Neos.Neos/Documentation/References/Validators/Media.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Media Validator Reference
=========================

This reference was automatically generated from code on 2023-03-31
This reference was automatically generated from code on 2023-04-02


.. _`Media Validator Reference: ImageOrientationValidator`:
Expand Down
2 changes: 1 addition & 1 deletion Neos.Neos/Documentation/References/Validators/Party.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Party Validator Reference
=========================

This reference was automatically generated from code on 2023-03-31
This reference was automatically generated from code on 2023-04-02


.. _`Party Validator Reference: AimAddressValidator`:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
Content Repository ViewHelper Reference
#######################################

This reference was automatically generated from code on 2023-03-31
This reference was automatically generated from code on 2023-04-02


Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
FluidAdaptor ViewHelper Reference
#################################

This reference was automatically generated from code on 2023-03-31
This reference was automatically generated from code on 2023-04-02


.. _`FluidAdaptor ViewHelper Reference: f:debug`:
Expand Down
2 changes: 1 addition & 1 deletion Neos.Neos/Documentation/References/ViewHelpers/Form.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Form ViewHelper Reference
#########################

This reference was automatically generated from code on 2023-03-31
This reference was automatically generated from code on 2023-04-02


.. _`Form ViewHelper Reference: neos.form:form`:
Expand Down
Loading

0 comments on commit cfe3bdc

Please sign in to comment.