mirror of
https://github.com/shlinkio/shlink.git
synced 2026-02-28 04:03:12 +08:00
Decouple LocateVisitsCommand from AbstractLockedCommand
This commit is contained in:
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace Shlinkio\Shlink\CLI\Command\Db;
|
namespace Shlinkio\Shlink\CLI\Command\Db;
|
||||||
|
|
||||||
use Shlinkio\Shlink\CLI\Command\Util\AbstractLockedCommand;
|
use Shlinkio\Shlink\CLI\Command\Util\AbstractLockedCommand;
|
||||||
use Shlinkio\Shlink\CLI\Command\Util\LockedCommandConfig;
|
use Shlinkio\Shlink\CLI\Command\Util\LockConfig;
|
||||||
use Shlinkio\Shlink\CLI\Util\ProcessRunnerInterface;
|
use Shlinkio\Shlink\CLI\Util\ProcessRunnerInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Lock\LockFactory;
|
use Symfony\Component\Lock\LockFactory;
|
||||||
@@ -30,8 +30,8 @@ abstract class AbstractDatabaseCommand extends AbstractLockedCommand
|
|||||||
$this->processRunner->run($output, $command);
|
$this->processRunner->run($output, $command);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getLockConfig(): LockedCommandConfig
|
protected function getLockConfig(): LockConfig
|
||||||
{
|
{
|
||||||
return LockedCommandConfig::blocking($this->getName() ?? static::class);
|
return LockConfig::blocking($this->getName() ?? static::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,5 +39,5 @@ abstract class AbstractLockedCommand extends Command
|
|||||||
|
|
||||||
abstract protected function lockedExecute(InputInterface $input, OutputInterface $output): int;
|
abstract protected function lockedExecute(InputInterface $input, OutputInterface $output): int;
|
||||||
|
|
||||||
abstract protected function getLockConfig(): LockedCommandConfig;
|
abstract protected function getLockConfig(): LockConfig;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ namespace Shlinkio\Shlink\CLI\Command\Util;
|
|||||||
|
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
use Symfony\Component\Lock\LockFactory;
|
||||||
|
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
class CommandUtils
|
class CommandUtils
|
||||||
{
|
{
|
||||||
@@ -25,4 +28,31 @@ class CommandUtils
|
|||||||
|
|
||||||
return $callback();
|
return $callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs a callback with a lock, making sure the lock is released after running the callback, and the callback does
|
||||||
|
* not run if the lock is already acquired.
|
||||||
|
*
|
||||||
|
* @param callable(): int $callback
|
||||||
|
*/
|
||||||
|
public static function executeWithLock(
|
||||||
|
LockFactory $locker,
|
||||||
|
LockConfig $lockConfig,
|
||||||
|
SymfonyStyle $io,
|
||||||
|
callable $callback,
|
||||||
|
): int {
|
||||||
|
$lock = $locker->createLock($lockConfig->lockName, $lockConfig->ttl, $lockConfig->isBlocking);
|
||||||
|
if (! $lock->acquire($lockConfig->isBlocking)) {
|
||||||
|
$io->writeln(
|
||||||
|
sprintf('<comment>Command "%s" is already in progress. Skipping.</comment>', $lockConfig->lockName),
|
||||||
|
);
|
||||||
|
return Command::INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return $callback();
|
||||||
|
} finally {
|
||||||
|
$lock->release();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,24 +4,24 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Shlinkio\Shlink\CLI\Command\Util;
|
namespace Shlinkio\Shlink\CLI\Command\Util;
|
||||||
|
|
||||||
final class LockedCommandConfig
|
final readonly class LockConfig
|
||||||
{
|
{
|
||||||
public const float DEFAULT_TTL = 600.0; // 10 minutes
|
public const float DEFAULT_TTL = 600.0; // 10 minutes
|
||||||
|
|
||||||
private function __construct(
|
private function __construct(
|
||||||
public readonly string $lockName,
|
public string $lockName,
|
||||||
public readonly bool $isBlocking,
|
public bool $isBlocking,
|
||||||
public readonly float $ttl = self::DEFAULT_TTL,
|
public float $ttl = self::DEFAULT_TTL,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function blocking(string $lockName): self
|
public static function blocking(string $lockName): self
|
||||||
{
|
{
|
||||||
return new self($lockName, true);
|
return new self($lockName, isBlocking: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function nonBlocking(string $lockName): self
|
public static function nonBlocking(string $lockName): self
|
||||||
{
|
{
|
||||||
return new self($lockName, false);
|
return new self($lockName, isBlocking: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Shlinkio\Shlink\CLI\Command\Visit;
|
namespace Shlinkio\Shlink\CLI\Command\Visit;
|
||||||
|
|
||||||
use Shlinkio\Shlink\CLI\Command\Util\AbstractLockedCommand;
|
use Shlinkio\Shlink\CLI\Command\Util\CommandUtils;
|
||||||
use Shlinkio\Shlink\CLI\Command\Util\LockedCommandConfig;
|
use Shlinkio\Shlink\CLI\Command\Util\LockConfig;
|
||||||
use Shlinkio\Shlink\Common\Util\IpAddress;
|
use Shlinkio\Shlink\Common\Util\IpAddress;
|
||||||
use Shlinkio\Shlink\Core\Exception\IpCannotBeLocatedException;
|
use Shlinkio\Shlink\Core\Exception\IpCannotBeLocatedException;
|
||||||
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
|
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
|
||||||
@@ -15,6 +15,7 @@ use Shlinkio\Shlink\Core\Visit\Geolocation\VisitLocatorInterface;
|
|||||||
use Shlinkio\Shlink\Core\Visit\Geolocation\VisitToLocationHelperInterface;
|
use Shlinkio\Shlink\Core\Visit\Geolocation\VisitToLocationHelperInterface;
|
||||||
use Shlinkio\Shlink\Core\Visit\Model\UnlocatableIpType;
|
use Shlinkio\Shlink\Core\Visit\Model\UnlocatableIpType;
|
||||||
use Shlinkio\Shlink\IpGeolocation\Model\Location;
|
use Shlinkio\Shlink\IpGeolocation\Model\Location;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Exception\RuntimeException;
|
use Symfony\Component\Console\Exception\RuntimeException;
|
||||||
use Symfony\Component\Console\Input\ArrayInput;
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
@@ -26,7 +27,7 @@ use Throwable;
|
|||||||
|
|
||||||
use function sprintf;
|
use function sprintf;
|
||||||
|
|
||||||
class LocateVisitsCommand extends AbstractLockedCommand implements VisitGeolocationHelperInterface
|
class LocateVisitsCommand extends Command implements VisitGeolocationHelperInterface
|
||||||
{
|
{
|
||||||
public const string NAME = 'visit:locate';
|
public const string NAME = 'visit:locate';
|
||||||
|
|
||||||
@@ -35,9 +36,9 @@ class LocateVisitsCommand extends AbstractLockedCommand implements VisitGeolocat
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly VisitLocatorInterface $visitLocator,
|
private readonly VisitLocatorInterface $visitLocator,
|
||||||
private readonly VisitToLocationHelperInterface $visitToLocation,
|
private readonly VisitToLocationHelperInterface $visitToLocation,
|
||||||
LockFactory $locker,
|
private readonly LockFactory $locker,
|
||||||
) {
|
) {
|
||||||
parent::__construct($locker);
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function configure(): void
|
protected function configure(): void
|
||||||
@@ -97,7 +98,17 @@ class LocateVisitsCommand extends AbstractLockedCommand implements VisitGeolocat
|
|||||||
return $this->io->confirm('Do you want to proceed?', false);
|
return $this->io->confirm('Do you want to proceed?', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function lockedExecute(InputInterface $input, OutputInterface $output): int
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
return CommandUtils::executeWithLock(
|
||||||
|
$this->locker,
|
||||||
|
LockConfig::nonBlocking(self::NAME),
|
||||||
|
new SymfonyStyle($input, $output),
|
||||||
|
fn () => $this->runStuff($input),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function runStuff(InputInterface $input): int
|
||||||
{
|
{
|
||||||
$retry = $input->getOption('retry');
|
$retry = $input->getOption('retry');
|
||||||
$all = $retry && $input->getOption('all');
|
$all = $retry && $input->getOption('all');
|
||||||
@@ -174,9 +185,4 @@ class LocateVisitsCommand extends AbstractLockedCommand implements VisitGeolocat
|
|||||||
throw new RuntimeException('It is not possible to locate visits without a GeoLite2 db file.');
|
throw new RuntimeException('It is not possible to locate visits without a GeoLite2 db file.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getLockConfig(): LockedCommandConfig
|
|
||||||
{
|
|
||||||
return LockedCommandConfig::nonBlocking(self::NAME);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace Shlinkio\Shlink\CLI\Util;
|
namespace Shlinkio\Shlink\CLI\Util;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Shlinkio\Shlink\CLI\Command\Util\LockedCommandConfig;
|
use Shlinkio\Shlink\CLI\Command\Util\LockConfig;
|
||||||
use Symfony\Component\Console\Helper\DebugFormatterHelper;
|
use Symfony\Component\Console\Helper\DebugFormatterHelper;
|
||||||
use Symfony\Component\Console\Helper\ProcessHelper;
|
use Symfony\Component\Console\Helper\ProcessHelper;
|
||||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||||
@@ -24,7 +24,7 @@ class ProcessRunner implements ProcessRunnerInterface
|
|||||||
{
|
{
|
||||||
$this->createProcess = $createProcess !== null
|
$this->createProcess = $createProcess !== null
|
||||||
? $createProcess(...)
|
? $createProcess(...)
|
||||||
: static fn (array $cmd) => new Process($cmd, timeout: LockedCommandConfig::DEFAULT_TTL);
|
: static fn (array $cmd) => new Process($cmd, timeout: LockConfig::DEFAULT_TTL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(OutputInterface $output, array $cmd): void
|
public function run(OutputInterface $output, array $cmd): void
|
||||||
|
|||||||
Reference in New Issue
Block a user