Files
crowd-fund-api/src/Command/BenzinaPumpCommand.php
T

168 lines
5.2 KiB
PHP

<?php
namespace App\Command;
use App\Library\Benzina\Benzina;
use App\Library\Benzina\Pump\PumpInterface;
use App\Library\Benzina\Stream\PdoStream;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(
name: 'app:benzina:pump',
description: 'Pump records in a database into the v4 database.',
)]
class BenzinaPumpCommand extends Command
{
public function __construct(
private Benzina $benzina,
) {
parent::__construct();
}
protected function configure(): void
{
$this->addArgument('table', InputArgument::REQUIRED);
$this->addOption(
'database',
null,
InputOption::VALUE_OPTIONAL,
'The address of the database to read from',
'mysql://goteo:goteo@mariadb:3306/benzina'
);
$this->addOption(
'batch-size',
null,
InputOption::VALUE_OPTIONAL,
'The number of rows to process at once',
99
);
$this->addOption(
'skip-pumped',
null,
InputOption::VALUE_NEGATABLE,
'Skips feeding already pumped records in a batch',
true
);
$this->addUsage('app:benzina:pump --no-debug user');
$this->setHelp(
<<<'EOF'
The <info>%command.name%</info> processes the data in the database table and supplies it to the supporting pumps:
<info>%command.full_name%</info>
You can avoid possible memory leaks caused by the Symfony profiler with the <info>no-debug</info> flag:
<info>%command.full_name% --no-debug</info>
EOF
);
}
/**
* @param ConsoleOutputInterface $output
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$batchSize = $input->getOption('batch-size');
$stream = new PdoStream(
$input->getOption('database'),
$input->getArgument('table'),
$batchSize
);
$streamSize = $stream->size();
$streamBatches = $streamSize / $batchSize;
$streamSection = new SymfonyStyle($input, $output->section());
if ($streamSize < 1) {
$streamSection->writeln('No data found at the given source. Skipping execution.');
return Command::SUCCESS;
}
$streamSection->writeln(sprintf('Streaming %d records (%d batches).', $streamSize, $streamBatches));
$pumps = $this->benzina->getPumpsFor($stream);
$pumpsCount = \count($pumps);
$pumpsSection = new SymfonyStyle($input, $output->section());
if ($pumpsCount < 1) {
$pumpsSection->writeln('No pumps support the streamed sample. Skipping execution.');
return Command::SUCCESS;
}
$pumpsSection->writeln(sprintf('Streaming to %d pumps.', $pumpsCount));
$pumpsSection->listing(\array_map(function (PumpInterface $pump) {
return $pump::class;
}, $pumps));
$streamedBatchesSection = $output->section();
$streamedBatchesSection->writeln("\tStreamed batches:");
$streamedBatches = new ProgressBar($streamedBatchesSection);
$streamedBatches->start($streamBatches);
$streamedRecordsSection = $output->section();
$streamedRecordsSection->writeln("\tStreamed records:");
$streamedRecords = new ProgressBar($streamedRecordsSection);
$streamedRecords->start($streamSize);
$memUsageSection = $output->section();
$memUsageSection->writeln("\tMemory usage:");
$memUsage = new ProgressBar($memUsageSection);
$memUsage->start($this->getMemoryLimit());
while (!$stream->eof()) {
\memory_reset_peak_usage();
$batch = $stream->read();
$streamBatches = $streamBatches - 1;
$streamedBatches->advance();
foreach ($pumps as $key => $pump) {
$pump->setConfig([
'skip-pumped' => $input->getOption('skip-pumped'),
]);
$pump->pump($batch);
$memUsage->setProgress(\memory_get_peak_usage(false));
}
$streamed = $stream->tell();
$streamedRecords->setProgress($streamed);
}
$endSection = new SymfonyStyle($input, $output->section());
$endSection->success('Data processed successfully!');
return Command::SUCCESS;
}
private function getMemoryLimit(): int
{
$limit = \ini_get('memory_limit');
if (preg_match('/^(\d+)(.)$/', $limit, $matches)) {
if ($matches[2] == 'M') {
$limit = $matches[1] * 1024 * 1024; // nnnM -> nnn MB
} elseif ($matches[2] == 'K') {
$limit = $matches[1] * 1024; // nnnK -> nnn KB
}
}
return $limit;
}
}