diff --git a/module/CLI/config/dependencies.config.php b/module/CLI/config/dependencies.config.php index b56ff76e..0a90c1ff 100644 --- a/module/CLI/config/dependencies.config.php +++ b/module/CLI/config/dependencies.config.php @@ -48,7 +48,7 @@ return [ ], ConfigAbstractFactory::class => [ - GeolocationDbUpdater::class => [DbUpdater::class, Reader::class], + GeolocationDbUpdater::class => [DbUpdater::class, Reader::class, Lock\Factory::class], Command\ShortUrl\GenerateShortUrlCommand::class => [Service\UrlShortener::class, 'config.url_shortener.domain'], Command\ShortUrl\ResolveUrlCommand::class => [Service\UrlShortener::class], diff --git a/module/CLI/src/Util/GeolocationDbUpdater.php b/module/CLI/src/Util/GeolocationDbUpdater.php index 067a9561..0d5c4b3e 100644 --- a/module/CLI/src/Util/GeolocationDbUpdater.php +++ b/module/CLI/src/Util/GeolocationDbUpdater.php @@ -9,18 +9,24 @@ use InvalidArgumentException; use Shlinkio\Shlink\CLI\Exception\GeolocationDbUpdateFailedException; use Shlinkio\Shlink\Common\Exception\RuntimeException; use Shlinkio\Shlink\Common\IpGeolocation\GeoLite2\DbUpdaterInterface; +use Symfony\Component\Lock\Factory as Locker; class GeolocationDbUpdater implements GeolocationDbUpdaterInterface { + private const LOCK_NAME = 'geolocation-db-update'; + /** @var DbUpdaterInterface */ private $dbUpdater; /** @var Reader */ private $geoLiteDbReader; + /** @var Locker */ + private $locker; - public function __construct(DbUpdaterInterface $dbUpdater, Reader $geoLiteDbReader) + public function __construct(DbUpdaterInterface $dbUpdater, Reader $geoLiteDbReader, Locker $locker) { $this->dbUpdater = $dbUpdater; $this->geoLiteDbReader = $geoLiteDbReader; + $this->locker = $locker; } /** @@ -28,6 +34,11 @@ class GeolocationDbUpdater implements GeolocationDbUpdaterInterface */ public function checkDbUpdate(callable $mustBeUpdated = null, callable $handleProgress = null): void { + $lock = $this->locker->createLock(self::LOCK_NAME); + if (! $lock->acquire()) { + return; + } + try { $meta = $this->geoLiteDbReader->metadata(); if ($this->buildIsTooOld($meta->__get('buildEpoch'))) { @@ -36,6 +47,8 @@ class GeolocationDbUpdater implements GeolocationDbUpdaterInterface } catch (InvalidArgumentException $e) { // This is the exception thrown by the reader when the database file does not exist $this->downloadNewDb(false, $mustBeUpdated, $handleProgress); + } finally { + $lock->release(); } }