Browse Source

Increased timeout on db commands to 10 minutes

Alejandro Celaya 3 years ago
parent
commit
d932f0a204

+ 5 - 2
module/CLI/config/dependencies.config.php

@@ -34,6 +34,8 @@ return [
             PhpExecutableFinder::class => InvokableFactory::class,
 
             Util\GeolocationDbUpdater::class => ConfigAbstractFactory::class,
+            Util\ProcessRunner::class => ConfigAbstractFactory::class,
+
             ApiKey\RoleResolver::class => ConfigAbstractFactory::class,
 
             Command\ShortUrl\GenerateShortUrlCommand::class => ConfigAbstractFactory::class,
@@ -62,6 +64,7 @@ return [
 
     ConfigAbstractFactory::class => [
         Util\GeolocationDbUpdater::class => [DbUpdater::class, Reader::class, LOCAL_LOCK_FACTORY],
+        Util\ProcessRunner::class => [SymfonyCli\Helper\ProcessHelper::class],
         ApiKey\RoleResolver::class => [DomainService::class],
 
         Command\ShortUrl\GenerateShortUrlCommand::class => [
@@ -97,14 +100,14 @@ return [
 
         Command\Db\CreateDatabaseCommand::class => [
             LockFactory::class,
-            SymfonyCli\Helper\ProcessHelper::class,
+            Util\ProcessRunner::class,
             PhpExecutableFinder::class,
             Connection::class,
             NoDbNameConnectionFactory::SERVICE_NAME,
         ],
         Command\Db\MigrateDatabaseCommand::class => [
             LockFactory::class,
-            SymfonyCli\Helper\ProcessHelper::class,
+            Util\ProcessRunner::class,
             PhpExecutableFinder::class,
         ],
     ],

+ 10 - 7
module/CLI/src/Command/Db/AbstractDatabaseCommand.php

@@ -6,31 +6,34 @@ namespace Shlinkio\Shlink\CLI\Command\Db;
 
 use Shlinkio\Shlink\CLI\Command\Util\AbstractLockedCommand;
 use Shlinkio\Shlink\CLI\Command\Util\LockedCommandConfig;
-use Symfony\Component\Console\Helper\ProcessHelper;
+use Shlinkio\Shlink\CLI\Util\ProcessRunnerInterface;
 use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Lock\LockFactory;
 use Symfony\Component\Process\PhpExecutableFinder;
 
 abstract class AbstractDatabaseCommand extends AbstractLockedCommand
 {
-    private ProcessHelper $processHelper;
+    private ProcessRunnerInterface $processRunner;
     private string $phpBinary;
 
-    public function __construct(LockFactory $locker, ProcessHelper $processHelper, PhpExecutableFinder $phpFinder)
-    {
+    public function __construct(
+        LockFactory $locker,
+        ProcessRunnerInterface $processRunner,
+        PhpExecutableFinder $phpFinder
+    ) {
         parent::__construct($locker);
-        $this->processHelper = $processHelper;
+        $this->processRunner = $processRunner;
         $this->phpBinary = $phpFinder->find(false) ?: 'php';
     }
 
     protected function runPhpCommand(OutputInterface $output, array $command): void
     {
         $command = [$this->phpBinary, ...$command, '--no-interaction'];
-        $this->processHelper->mustRun($output, $command);
+        $this->processRunner->run($output, $command);
     }
 
     protected function getLockConfig(): LockedCommandConfig
     {
-        return new LockedCommandConfig($this->getName(), true);
+        return LockedCommandConfig::blocking($this->getName());
     }
 }

+ 3 - 3
module/CLI/src/Command/Db/CreateDatabaseCommand.php

@@ -6,7 +6,7 @@ namespace Shlinkio\Shlink\CLI\Command\Db;
 
 use Doctrine\DBAL\Connection;
 use Shlinkio\Shlink\CLI\Util\ExitCodes;
-use Symfony\Component\Console\Helper\ProcessHelper;
+use Shlinkio\Shlink\CLI\Util\ProcessRunnerInterface;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Console\Style\SymfonyStyle;
@@ -26,12 +26,12 @@ class CreateDatabaseCommand extends AbstractDatabaseCommand
 
     public function __construct(
         LockFactory $locker,
-        ProcessHelper $processHelper,
+        ProcessRunnerInterface $processRunner,
         PhpExecutableFinder $phpFinder,
         Connection $conn,
         Connection $noDbNameConn
     ) {
-        parent::__construct($locker, $processHelper, $phpFinder);
+        parent::__construct($locker, $processRunner, $phpFinder);
         $this->regularConn = $conn;
         $this->noDbNameConn = $noDbNameConn;
     }

+ 12 - 2
module/CLI/src/Command/Util/LockedCommandConfig.php

@@ -6,19 +6,29 @@ namespace Shlinkio\Shlink\CLI\Command\Util;
 
 final class LockedCommandConfig
 {
-    private const DEFAULT_TTL = 90.0; // 1.5 minutes
+    public const DEFAULT_TTL = 600.0; // 10 minutes
 
     private string $lockName;
     private bool $isBlocking;
     private float $ttl;
 
-    public function __construct(string $lockName, bool $isBlocking = false, float $ttl = self::DEFAULT_TTL)
+    private function __construct(string $lockName, bool $isBlocking, float $ttl = self::DEFAULT_TTL)
     {
         $this->lockName = $lockName;
         $this->isBlocking = $isBlocking;
         $this->ttl = $ttl;
     }
 
+    public static function blocking(string $lockName): self
+    {
+        return new self($lockName, true);
+    }
+
+    public static function nonBlocking(string $lockName): self
+    {
+        return new self($lockName, false);
+    }
+
     public function lockName(): string
     {
         return $this->lockName;

+ 1 - 1
module/CLI/src/Command/Visit/LocateVisitsCommand.php

@@ -208,6 +208,6 @@ class LocateVisitsCommand extends AbstractLockedCommand implements VisitGeolocat
 
     protected function getLockConfig(): LockedCommandConfig
     {
-        return new LockedCommandConfig($this->getName());
+        return LockedCommandConfig::nonBlocking($this->getName());
     }
 }

+ 54 - 0
module/CLI/src/Util/ProcessRunner.php

@@ -0,0 +1,54 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Shlinkio\Shlink\CLI\Util;
+
+use Shlinkio\Shlink\CLI\Command\Util\LockedCommandConfig;
+use Symfony\Component\Console\Helper\DebugFormatterHelper;
+use Symfony\Component\Console\Helper\ProcessHelper;
+use Symfony\Component\Console\Output\ConsoleOutputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Process\Process;
+
+use function spl_object_hash;
+use function sprintf;
+use function str_replace;
+
+class ProcessRunner implements ProcessRunnerInterface
+{
+    private ProcessHelper $helper;
+
+    public function __construct(ProcessHelper $helper)
+    {
+        $this->helper = $helper;
+    }
+
+    public function run(OutputInterface $output, array $cmd): void
+    {
+        if ($output instanceof ConsoleOutputInterface) {
+            $output = $output->getErrorOutput();
+        }
+
+        /** @var DebugFormatterHelper $formatter */
+        $formatter = $this->helper->getHelperSet()->get('debug_formatter');
+        $process = new Process($cmd, null, null, null, LockedCommandConfig::DEFAULT_TTL);
+
+        if ($output->isVeryVerbose()) {
+            $output->write(
+                $formatter->start(spl_object_hash($process), str_replace('<', '\\<', $process->getCommandLine())),
+            );
+        }
+
+        $callback = $output->isDebug() ? $this->helper->wrapCallback($output, $process) : null;
+        $process->mustRun($callback);
+
+        if ($output->isVeryVerbose()) {
+            $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf(
+                '%s Command did not run successfully',
+                $process->getExitCode(),
+            );
+            $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful()));
+        }
+    }
+}

+ 12 - 0
module/CLI/src/Util/ProcessRunnerInterface.php

@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Shlinkio\Shlink\CLI\Util;
+
+use Symfony\Component\Console\Output\OutputInterface;
+
+interface ProcessRunnerInterface
+{
+    public function run(OutputInterface $output, array $cmd): void;
+}

+ 4 - 5
module/CLI/test/Command/Db/CreateDatabaseCommandTest.php

@@ -12,14 +12,13 @@ use Prophecy\Argument;
 use Prophecy\PhpUnit\ProphecyTrait;
 use Prophecy\Prophecy\ObjectProphecy;
 use Shlinkio\Shlink\CLI\Command\Db\CreateDatabaseCommand;
+use Shlinkio\Shlink\CLI\Util\ProcessRunnerInterface;
 use Symfony\Component\Console\Application;
-use Symfony\Component\Console\Helper\ProcessHelper;
 use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Console\Tester\CommandTester;
 use Symfony\Component\Lock\LockFactory;
 use Symfony\Component\Lock\LockInterface;
 use Symfony\Component\Process\PhpExecutableFinder;
-use Symfony\Component\Process\Process;
 
 class CreateDatabaseCommandTest extends TestCase
 {
@@ -43,7 +42,7 @@ class CreateDatabaseCommandTest extends TestCase
         $phpExecutableFinder = $this->prophesize(PhpExecutableFinder::class);
         $phpExecutableFinder->find(false)->willReturn('/usr/local/bin/php');
 
-        $this->processHelper = $this->prophesize(ProcessHelper::class);
+        $this->processHelper = $this->prophesize(ProcessRunnerInterface::class);
         $this->schemaManager = $this->prophesize(AbstractSchemaManager::class);
         $this->databasePlatform = $this->prophesize(AbstractPlatform::class);
 
@@ -113,12 +112,12 @@ class CreateDatabaseCommandTest extends TestCase
         $createDatabase = $this->schemaManager->createDatabase($shlinkDatabase)->will(function (): void {
         });
         $listTables = $this->schemaManager->listTableNames()->willReturn([]);
-        $runCommand = $this->processHelper->mustRun(Argument::type(OutputInterface::class), [
+        $runCommand = $this->processHelper->run(Argument::type(OutputInterface::class), [
             '/usr/local/bin/php',
             CreateDatabaseCommand::DOCTRINE_SCRIPT,
             CreateDatabaseCommand::DOCTRINE_CREATE_SCHEMA_COMMAND,
             '--no-interaction',
-        ], Argument::cetera())->willReturn(new Process([]));
+        ]);
 
         $this->commandTester->execute([]);
         $output = $this->commandTester->getDisplay();

+ 4 - 5
module/CLI/test/Command/Db/MigrateDatabaseCommandTest.php

@@ -9,14 +9,13 @@ use Prophecy\Argument;
 use Prophecy\PhpUnit\ProphecyTrait;
 use Prophecy\Prophecy\ObjectProphecy;
 use Shlinkio\Shlink\CLI\Command\Db\MigrateDatabaseCommand;
+use Shlinkio\Shlink\CLI\Util\ProcessRunnerInterface;
 use Symfony\Component\Console\Application;
-use Symfony\Component\Console\Helper\ProcessHelper;
 use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Console\Tester\CommandTester;
 use Symfony\Component\Lock\LockFactory;
 use Symfony\Component\Lock\LockInterface;
 use Symfony\Component\Process\PhpExecutableFinder;
-use Symfony\Component\Process\Process;
 
 class MigrateDatabaseCommandTest extends TestCase
 {
@@ -37,7 +36,7 @@ class MigrateDatabaseCommandTest extends TestCase
         $phpExecutableFinder = $this->prophesize(PhpExecutableFinder::class);
         $phpExecutableFinder->find(false)->willReturn('/usr/local/bin/php');
 
-        $this->processHelper = $this->prophesize(ProcessHelper::class);
+        $this->processHelper = $this->prophesize(ProcessRunnerInterface::class);
 
         $command = new MigrateDatabaseCommand(
             $locker->reveal(),
@@ -53,12 +52,12 @@ class MigrateDatabaseCommandTest extends TestCase
     /** @test */
     public function migrationsCommandIsRunWithProperVerbosity(): void
     {
-        $runCommand = $this->processHelper->mustRun(Argument::type(OutputInterface::class), [
+        $runCommand = $this->processHelper->run(Argument::type(OutputInterface::class), [
             '/usr/local/bin/php',
             MigrateDatabaseCommand::DOCTRINE_MIGRATIONS_SCRIPT,
             MigrateDatabaseCommand::DOCTRINE_MIGRATE_COMMAND,
             '--no-interaction',
-        ], Argument::cetera())->willReturn(new Process([]));
+        ]);
 
         $this->commandTester->execute([]);
         $output = $this->commandTester->getDisplay();

+ 1 - 1
module/CLI/test/Command/Visit/LocateVisitsCommandTest.php

@@ -52,7 +52,7 @@ class LocateVisitsCommandTest extends TestCase
         $this->lock->acquire(false)->willReturn(true);
         $this->lock->release()->will(function (): void {
         });
-        $locker->createLock(Argument::type('string'), 90.0, false)->willReturn($this->lock->reveal());
+        $locker->createLock(Argument::type('string'), 600.0, false)->willReturn($this->lock->reveal());
 
         $command = new LocateVisitsCommand(
             $this->visitService->reveal(),

+ 0 - 1
phpstan.neon

@@ -2,5 +2,4 @@ parameters:
 	checkMissingIterableValueType: false
 	checkGenericClassInNonGenericObjectType: false
 	ignoreErrors:
-		- '#mustRun\(\)#'
 		- '#If condition is always false#'