Skip to content

Commit

Permalink
fix: Code review
Browse files Browse the repository at this point in the history
  • Loading branch information
Nattfarinn committed Aug 26, 2024
1 parent 544fccd commit bdeae0f
Showing 1 changed file with 66 additions and 82 deletions.
148 changes: 66 additions & 82 deletions src/bundle/Core/Command/VirtualFieldDuplicateFixCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
namespace Ibexa\Bundle\Core\Command;

use Doctrine\DBAL\Connection;
use Exception;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
Expand All @@ -26,14 +25,17 @@ final class VirtualFieldDuplicateFixCommand extends Command

private const DEFAULT_SLEEP = 0;

protected static $defaultName = 'ibexa:content:remove-duplicate-fields';

protected static $defaultDescription = 'Removes duplicate fields created as a result of faulty IBX-5388 performance fix.';

/** @var \Doctrine\DBAL\Connection */
private $connection;

public function __construct(
Connection $connection
) {
parent::__construct('ibexa:content:remove-duplicate-fields');
$this->setDescription('Removes duplicate fields created as a result of faulty IBX-5388 performance fix.');
parent::__construct();

$this->connection = $connection;
}
Expand All @@ -43,33 +45,26 @@ public function configure(): void
$this->addOption(
'batch-size',
'b',
InputOption::VALUE_OPTIONAL,
InputOption::VALUE_REQUIRED,
'Number of attributes affected per iteration',
self::DEFAULT_BATCH_SIZE
);

$this->addOption(
'max-iterations',
'i',
InputOption::VALUE_OPTIONAL,
InputOption::VALUE_REQUIRED,
'Max iterations count (default or -1: unlimited)',
self::MAX_ITERATIONS_UNLIMITED
);

$this->addOption(
'sleep',
's',
InputOption::VALUE_OPTIONAL,
InputOption::VALUE_REQUIRED,
'Wait between iterations, in milliseconds',
self::DEFAULT_SLEEP
);

$this->addOption(
'force',
'f',
InputOption::VALUE_NONE,
'Force operation (implies non-interactive mode)',
);
}

protected function execute(InputInterface $input, OutputInterface $output): int
Expand All @@ -78,11 +73,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$stopwatch = new Stopwatch(true);
$stopwatch->start('total', 'command');

$force = $input->getOption('force');
if ($force) {
$input->setInteractive(false);
}

$batchSize = (int)$input->getOption('batch-size');
if ($batchSize === 0) {
$style->warning('Batch size is set to 0. Nothing to do.');
Expand All @@ -99,67 +89,65 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$sleep = (int)$input->getOption('sleep');

try {
$totalCount = $this->getDuplicatedAttributeTotalCount($style, $stopwatch);
$totalCount = $this->getDuplicatedAttributeTotalCount($style, $stopwatch);

if ($totalCount > 0) {
$confirmation = $this->askForConfirmation($style);
if (!$confirmation && !$force) {
$style->info('Confirmation rejected. Terminating.');
if ($totalCount === 0) {
$style->success('Database is clean of attribute duplicates. Nothing to do.');

return Command::FAILURE;
}
} else {
$style->success('Database is clean of attribute duplicates. Nothing to do.');
return Command::SUCCESS;
}

return Command::SUCCESS;
}
if ($input->isInteractive()) {
$confirmation = $this->askForConfirmation($style);
if (!$confirmation) {
$style->info('Confirmation rejected. Terminating.');

$iteration = 1;
$totalDeleted = 0;
do {
$deleted = 0;
$stopwatch->start('iteration', 'sql');

$attributes = $this->getDuplicatedAttributesBatch($batchSize);
foreach ($attributes as $attribute) {
$attributeIds = $this->getDuplicatedAttributeIds($attribute);

$deleted += $this->deleteAttributes($attributeIds);
$totalDeleted += $deleted;
}

$style->info(
sprintf(
'Iteration %d: Removed %d duplicates (total removed this execution: %d). [Debug %s]',
$iteration,
$deleted,
$totalDeleted,
$stopwatch->stop('iteration')
)
);

if ($maxIterations !== self::MAX_ITERATIONS_UNLIMITED && ++$iteration > $maxIterations) {
$style->warning('Max iterations count reached. Terminating.');

return self::FAILURE;
}

// Wait, if needed, before moving to next iteration
usleep($sleep * 1000);
} while ($batchSize === count($attributes));

$style->success(sprintf(
'Operation successful. Removed total of %d duplicates. [Debug %s]',
$totalDeleted,
$stopwatch->stop('total')
));
} catch (Exception $exception) {
$style->error($exception->getMessage());

return Command::FAILURE;
return Command::FAILURE;
}
}

$iteration = 1;
$totalDeleted = 0;
do {
$deleted = 0;
$stopwatch->start('iteration', 'sql');

$attributes = $this->getDuplicatedAttributesBatch($batchSize);
foreach ($attributes as $attribute) {
$attributeIds = $this->getDuplicatedAttributeIds($attribute);

$iterationDeleted = $this->deleteAttributes($attributeIds);

$deleted += $iterationDeleted;
$totalDeleted += $iterationDeleted;
}

$style->info(
sprintf(
'Iteration %d: Removed %d duplicate database rows (total removed this execution: %d). [Debug %s]',
$iteration,
$deleted,
$totalDeleted,
$stopwatch->stop('iteration')
)
);

if ($maxIterations !== self::MAX_ITERATIONS_UNLIMITED && ++$iteration > $maxIterations) {
$style->warning('Max iterations count reached. Terminating.');

return self::SUCCESS;
}

// Wait, if needed, before moving to next iteration
usleep($sleep * 1000);
} while ($batchSize === count($attributes));

$style->success(sprintf(
'Operation successful. Removed total of %d duplicate database rows. [Debug %s]',
$totalDeleted,
$stopwatch->stop('total')
));

return Command::SUCCESS;
}

Expand Down Expand Up @@ -229,22 +217,18 @@ private function getDuplicatedAttributeIds(array $attribute): array
$query
->select('id')
->from('ezcontentobject_attribute')
->where('version = :version')
->andWhere('version = :version')
->andWhere('contentclassattribute_id = :contentclassattribute_id')
->andWhere('contentobject_id = :contentobject_id')
->andWhere('language_id = :language_id')
->orderBy('id', 'ASC')
->setFirstResult(0);
// Keep the original attribute row, the very first one
->setFirstResult(1);

$query->setParameters($attribute);

$result = $query->execute()->fetchFirstColumn();
$attributeIds = array_map('intval', $result);

// Keep the original attribute row, the very first one
array_shift($attributeIds);

return $attributeIds;
return array_map('intval', $result);
}

private function askForConfirmation(SymfonyStyle $style): bool
Expand All @@ -265,7 +249,7 @@ private function deleteAttributes($ids): int

$query
->delete('ezcontentobject_attribute')
->where($query->expr()->in('id', $ids));
->andWhere($query->expr()->in('id', $ids));

return (int)$query->execute();
}
Expand Down

0 comments on commit bdeae0f

Please sign in to comment.