From d06833943ba40940bd1a7e082d972dc4a5af4a5c Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:25:17 +0100 Subject: [PATCH] TASK: Remove `fixReplay` command we would have to delete too many events that were legit as well, like a move AFTER a deletion https://github.com/neos/neos-development-collection/issues/5364 --- .../NeosBetaMigrationCommandController.php | 113 ------------------ 1 file changed, 113 deletions(-) diff --git a/Neos.ContentRepositoryRegistry/Classes/Command/NeosBetaMigrationCommandController.php b/Neos.ContentRepositoryRegistry/Classes/Command/NeosBetaMigrationCommandController.php index b565b6734d..ac63222d7f 100644 --- a/Neos.ContentRepositoryRegistry/Classes/Command/NeosBetaMigrationCommandController.php +++ b/Neos.ContentRepositoryRegistry/Classes/Command/NeosBetaMigrationCommandController.php @@ -6,12 +6,10 @@ use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; -use Neos\ContentGraph\DoctrineDbalAdapter\DoctrineDbalContentGraphProjection; use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceFactoryDependencies; use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceFactoryInterface; use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceInterface; use Neos\ContentRepository\Core\Feature\ContentStreamEventStreamName; -use Neos\ContentRepository\Core\Projection\CatchUpOptions; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; @@ -20,14 +18,12 @@ use Neos\EventStore\Model\Event\EventMetadata; use Neos\EventStore\Model\Event\EventType; use Neos\EventStore\Model\Event\EventTypes; -use Neos\EventStore\Model\Event\SequenceNumber; use Neos\EventStore\Model\EventEnvelope; use Neos\EventStore\Model\Events; use Neos\EventStore\Model\EventStream\EventStreamFilter; use Neos\EventStore\Model\EventStream\ExpectedVersion; use Neos\Flow\Annotations as Flow; use Neos\Flow\Cli\CommandController; -use Symfony\Component\Console\Helper\ProgressBar; class NeosBetaMigrationCommandController extends CommandController { @@ -92,88 +88,6 @@ public function reorderNodeAggregateWasRemovedCommand(string $contentRepository $this->outputLine('Reordered %d removals. Please replay and rebase your other workspaces.', [count($eventsToReorder)]); } - public function fixReplayCommand(string $contentRepository = 'default', bool $resetProjection = true): void - { - $contentRepositoryId = ContentRepositoryId::fromString($contentRepository); - $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); - - $this->backup($contentRepositoryId); - - - $progressBar = new ProgressBar($this->output->getOutput()); - - $progressBar->start($this->highestSequenceNumber($contentRepositoryId)->value, 1); - $options = CatchUpOptions::create(progressCallback: fn () => $progressBar->advance()); - - if ($resetProjection) { - $contentRepository->resetProjectionState(DoctrineDbalContentGraphProjection::class); - } - - $automaticRemovedSequenceNumbers = []; - $manualRemovedSequenceNumbers = []; - - do { - try { - $contentRepository->catchUpProjection(DoctrineDbalContentGraphProjection::class, $options); - } catch (\Throwable $e) { - $this->outputLine(); - - if (preg_match('/^Exception while catching up to sequence number (\d+)/', $e->getMessage(), $matches) !== 1) { - $this->outputLine('Could not replay because of unexpected error'); - $this->outputLine('Removed %d other events: %s', [count($automaticRemovedSequenceNumbers), join(', ', $automaticRemovedSequenceNumbers)]); - throw $e; - } - $failedSequenceNumber = SequenceNumber::fromInteger((int)$matches[1]); - - $eventRow = $this->getEventEnvelopeData($failedSequenceNumber, $contentRepositoryId); - - if ($eventRow['metadata'] !== null) { - $this->outputLine('Did not delete event %s because it doesnt seem to be auto generated', [$failedSequenceNumber->value]); - $this->outputLine('The exception: %s', [$e->getMessage()]); - $this->outputLine(json_encode($eventRow, JSON_PRETTY_PRINT)); - $this->outputLine(); - if ($this->output->askConfirmation(sprintf('> still delete it %d? (y/n) ', $failedSequenceNumber->value), false)) { - $manualRemovedSequenceNumbers[] = $failedSequenceNumber->value; - $this->deleteEvent($failedSequenceNumber, $contentRepositoryId); - continue; - } - $this->outputLine('Removed %d other events: %s', [count($automaticRemovedSequenceNumbers), join(', ', $automaticRemovedSequenceNumbers)]); - throw $e; - } - - $this->outputLine('Deleted event %s because it seems to be invalid and auto generated', [$failedSequenceNumber->value]); - $this->outputLine(json_encode($eventRow)); - - $automaticRemovedSequenceNumbers[] = $failedSequenceNumber->value; - $this->deleteEvent($failedSequenceNumber, $contentRepositoryId); - - $this->outputLine(); - continue; - } - - $progressBar->finish(); - - $this->outputLine(); - $this->outputLine('Replay was successfully.'); - $this->outputLine('Removed %d automatic events: %s', [count($automaticRemovedSequenceNumbers), join(', ', $automaticRemovedSequenceNumbers)]); - if ($manualRemovedSequenceNumbers) { - $this->outputLine('Also removed %d events manually: %s', [count($manualRemovedSequenceNumbers), join(', ', $manualRemovedSequenceNumbers)]); - } - - return; - - } while (true); - } - - public function highestSequenceNumber(ContentRepositoryId $contentRepositoryId): SequenceNumber - { - $eventTableName = DoctrineEventStoreFactory::databaseTableName($contentRepositoryId); - return SequenceNumber::fromInteger((int)$this->connection->fetchOne( - 'SELECT sequencenumber FROM ' . $eventTableName . ' ORDER BY sequencenumber ASC' - )); - } - - private function backup(ContentRepositoryId $contentRepositoryId): void { $backupEventTableName = DoctrineEventStoreFactory::databaseTableName($contentRepositoryId) @@ -182,33 +96,6 @@ private function backup(ContentRepositoryId $contentRepositoryId): void $this->outputLine(sprintf('Copied events table to %s', $backupEventTableName)); } - /** - * @return array - */ - private function getEventEnvelopeData(SequenceNumber $sequenceNumber, ContentRepositoryId $contentRepositoryId): array - { - $eventTableName = DoctrineEventStoreFactory::databaseTableName($contentRepositoryId); - return $this->connection->fetchAssociative( - 'SELECT * FROM ' . $eventTableName . ' WHERE sequencenumber=:sequenceNumber', - [ - 'sequenceNumber' => $sequenceNumber->value, - ] - ); - } - - private function deleteEvent(SequenceNumber $sequenceNumber, ContentRepositoryId $contentRepositoryId): void - { - $eventTableName = DoctrineEventStoreFactory::databaseTableName($contentRepositoryId); - $this->connection->beginTransaction(); - $this->connection->executeStatement( - 'DELETE FROM ' . $eventTableName . ' WHERE sequencenumber=:sequenceNumber', - [ - 'sequenceNumber' => $sequenceNumber->value - ] - ); - $this->connection->commit(); - } - private function copyEventTable(string $backupEventTableName, ContentRepositoryId $contentRepositoryId): void { $eventTableName = DoctrineEventStoreFactory::databaseTableName($contentRepositoryId);