Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/9.0' into feature/node-reference…
Browse files Browse the repository at this point in the history
…-copy
  • Loading branch information
mhsdesign committed Oct 31, 2024
2 parents 5d64dd6 + 3e7ac78 commit 49541dd
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 27 deletions.
98 changes: 71 additions & 27 deletions Classes/Command/ContentStreamCommandController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,55 +19,99 @@ class ContentStreamCommandController extends CommandController
protected $contentRepositoryRegistry;

/**
* Remove all content streams which are not needed anymore from the projections.
* Detects if dangling content streams exists and which content streams could be pruned from the event stream
*
* NOTE: This still **keeps** the event stream as is; so it would be possible to re-construct the content stream
* at a later point in time (though we currently do not provide any API for it).
* Dangling content streams
* ------------------------
*
* To remove the deleted Content Streams, use `./flow contentStream:pruneRemovedFromEventStream` after running
* `./flow contentStream:prune`.
* Content streams that are not removed via the event ContentStreamWasRemoved and are not in use by a workspace
* (not a current's workspace content stream).
*
* 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.).
* Previously before Neos 9 beta 15 (#5301), dangling content streams were not removed during publishing, discard or rebase.
*
* ./flow contentStream:removeDangling
*
* Pruneable content streams
* -------------------------
*
* Content streams that were removed ContentStreamWasRemoved e.g. after publishing, and are not required for a full
* replay to reconstruct the current projections state. The ability to reconstitute a previous state will be lost.
*
* ./flow contentStream:pruneRemovedFromEventStream
*
* @param string $contentRepository Identifier of the content repository. (Default: 'default')
* @param boolean $removeTemporary Will delete all content streams which are currently not assigned (Default: false)
*/
public function pruneCommand(string $contentRepository = 'default', bool $removeTemporary = false): void
public function statusCommand(string $contentRepository = 'default'): void
{
$contentRepositoryId = ContentRepositoryId::fromString($contentRepository);
$contentStreamPruner = $this->contentRepositoryRegistry->buildService($contentRepositoryId, new ContentStreamPrunerFactory());

$unusedContentStreams = $contentStreamPruner->prune($removeTemporary);
$unusedContentStreamsPresent = false;
foreach ($unusedContentStreams as $contentStreamId) {
$this->outputFormatted('Removed %s', [$contentStreamId->value]);
$unusedContentStreamsPresent = true;
}
if (!$unusedContentStreamsPresent) {
$this->outputLine('There are no unused content streams.');
$status = $contentStreamPruner->outputStatus(
$this->outputLine(...)
);
if ($status === false) {
$this->quit(1);
}
}

/**
* Remove unused and deleted content streams from the event stream; effectively REMOVING information completely
* Removes all nodes, hierarchy relations and content stream entries which are not needed anymore from the projections.
*
* NOTE: This still **keeps** the event stream as is; so it would be possible to re-construct the content stream at a later point in time.
*
* HINT: ./flow contentStream:status gives information what is about to be removed
*
* To prune the removed content streams from the event stream, run ./flow contentStream:pruneRemovedFromEventStream afterwards.
*
* NOTE: To ensure that no temporary content streams of the *current* moment are removed, a time threshold is configurable via --remove-temporary-before
*
* @param string $contentRepository Identifier of the content repository. (Default: 'default')
* @param string $removeTemporaryBefore includes all temporary content streams like FORKED or CREATED older than that in the removal. To remove all use --remove-temporary-before=-1sec
*/
public function pruneRemovedFromEventStreamCommand(string $contentRepository = 'default'): void
public function removeDanglingCommand(string $contentRepository = 'default', string $removeTemporaryBefore = '-1day'): void
{
$contentRepositoryId = ContentRepositoryId::fromString($contentRepository);
$contentStreamPruner = $this->contentRepositoryRegistry->buildService($contentRepositoryId, new ContentStreamPrunerFactory());

$unusedContentStreams = $contentStreamPruner->pruneRemovedFromEventStream();
$unusedContentStreamsPresent = false;
foreach ($unusedContentStreams as $contentStreamId) {
$this->outputFormatted('Removed events for %s', [$contentStreamId->id->value]);
$unusedContentStreamsPresent = true;
try {
$removeTemporaryBeforeDate = new \DateTimeImmutable($removeTemporaryBefore);
} catch (\Exception $exception) {
$this->outputLine(sprintf('<error>--remove-temporary-before=%s is not a valid date</error>: %s', $removeTemporaryBefore, $exception->getMessage()));
$this->quit(1);
}
if (!$unusedContentStreamsPresent) {
$this->outputLine('There are no unused content streams.');

$now = new \DateTimeImmutable('now');
if ($removeTemporaryBeforeDate > $now) {
$this->outputLine(sprintf('<error>--remove-temporary-before=%s must be in the past</error>', $removeTemporaryBefore));
$this->quit(1);
}

$contentStreamPruner->removeDanglingContentStreams(
$this->outputLine(...),
$removeTemporaryBeforeDate
);
}

/**
* Prune removed content streams that are unused from the event stream; effectively REMOVING information completely
*
* HINT: ./flow contentStream:status gives information what is about to be pruned
*
* @param string $contentRepository Identifier of the content repository. (Default: 'default')
* @param bool $force Prune the unused content streams without confirmation. This cannot be reverted!
*/
public function pruneRemovedFromEventStreamCommand(string $contentRepository = 'default', bool $force = false): void
{
if (!$force && !$this->output->askConfirmation(sprintf('> This will prune removed content streams that are unused from the event stream in content repository "%s" (see flow contentStream:status). Are you sure to proceed? (y/n) ', $contentRepository), false)) {
$this->outputLine('<comment>Abort.</comment>');
return;
}

$contentRepositoryId = ContentRepositoryId::fromString($contentRepository);
$contentStreamPruner = $this->contentRepositoryRegistry->buildService($contentRepositoryId, new ContentStreamPrunerFactory());

$contentStreamPruner->pruneRemovedFromEventStream(
$this->outputLine(...)
);
}
}
12 changes: 12 additions & 0 deletions Classes/Command/MigrateEventsCommandController.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ public function __construct(
parent::__construct();
}

/**
* Temporary low level backup to ensure the prune migration https://github.com/neos/neos-development-collection/pull/5297 is safe
*
* @param string $contentRepository Identifier of the Content Repository to backup
*/
public function backupCommand(string $contentRepository = 'default'): void
{
$contentRepositoryId = ContentRepositoryId::fromString($contentRepository);
$eventMigrationService = $this->contentRepositoryRegistry->buildService($contentRepositoryId, $this->eventMigrationServiceFactory);
$eventMigrationService->backup($this->outputLine(...));
}

/**
* Migrates initial metadata & roles from the CR core workspaces to the corresponding Neos database tables
*
Expand Down
8 changes: 8 additions & 0 deletions Classes/Service/EventMigrationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ public function __construct(
) {
}

public function backup(\Closure $outputFn): void
{
$backupEventTableName = DoctrineEventStoreFactory::databaseTableName($this->contentRepositoryId)
. '_bkp_' . date('Y_m_d_H_i_s');
$this->copyEventTable($backupEventTableName);
$outputFn(sprintf('Backup. Copied events table to %s', $backupEventTableName));
}

/**
* The following things have to be migrated:
*
Expand Down

0 comments on commit 49541dd

Please sign in to comment.