diff --git a/src/Exceptions/OptimisticTransactionException.php b/src/Exceptions/OptimisticException.php similarity index 50% rename from src/Exceptions/OptimisticTransactionException.php rename to src/Exceptions/OptimisticException.php index ed3a7dd..edb8275 100644 --- a/src/Exceptions/OptimisticTransactionException.php +++ b/src/Exceptions/OptimisticException.php @@ -2,6 +2,6 @@ namespace AnourValar\LaravelAtom\Exceptions; -class OptimisticTransactionException extends \Exception +class OptimisticException extends \Exception { } diff --git a/src/Providers/LaravelAtomServiceProvider.php b/src/Providers/LaravelAtomServiceProvider.php index c21f245..ae699b8 100644 --- a/src/Providers/LaravelAtomServiceProvider.php +++ b/src/Providers/LaravelAtomServiceProvider.php @@ -69,7 +69,8 @@ public function boot() $this->publishes([__DIR__.'/../resources/lang/' => lang_path('vendor/laravel-atom')]); // migrations - $this->loadMigrationsFrom(__DIR__.'/../database/migrations'); + //$this->loadMigrationsFrom(__DIR__.'/../database/migrations'); + $this->publishes([__DIR__.'/../database/migrations/' => database_path('migrations')], 'migrations'); // events \Event::listen([TransactionCommitted::class, TransactionRolledBack::class], function ($event) { diff --git a/src/Service.php b/src/Service.php index 37d9581..c71947a 100644 --- a/src/Service.php +++ b/src/Service.php @@ -100,15 +100,13 @@ public function lock(): void { $sha1 = sha1(serialize($this->canonizeArgs(func_get_args()))); $connection = \DB::connection($this->config['locks']['connection']); - $table = $this->config['locks']['table']; $class = $this->config['locks']['strategies'][$this->config['locks']['strategy']]; - (new $class())->lock($sha1, $connection, $table); + (new $class())->lock($sha1, $connection); if ($this->lockHook) { ($this->lockHook)($sha1, func_get_args()); } - $this->cleanUp($connection, $table); } /** @@ -240,21 +238,6 @@ protected function canonizeArgs($value) return $value; } - /** - * @param \Illuminate\Database\Connection $connection - * @param string $table - * @return void - */ - protected function cleanUp(\Illuminate\Database\Connection $connection, string $table): void - { - if (! mt_rand(0, 50)) { - $connection - ->table($table) - ->where('updated_at', '<=', date('Y-m-d H:i:s', strtotime('-5 days'))) - ->delete(); - } - } - /** * @param mixed $connection * @return bool diff --git a/src/Strategies/OptimisticAdvisoryStrategy.php b/src/Strategies/OptimisticAdvisoryStrategy.php new file mode 100644 index 0000000..e2844a6 --- /dev/null +++ b/src/Strategies/OptimisticAdvisoryStrategy.php @@ -0,0 +1,28 @@ +transactionLevel()) { + throw new \LogicException('Lock can be applied only inside transaction'); + } + + $id1 = crc32($sha1) - 2147483648; + $id2 = crc32(strrev($sha1)) - 2147483648; + + if (! $connection->select('SELECT pg_try_advisory_xact_lock(:id1, :id2)', ['id1' => $id1, 'id2' => $id2])[0]->pg_try_advisory_xact_lock) { + throw new \AnourValar\LaravelAtom\Exceptions\OptimisticException(); + } + } +} diff --git a/src/Strategies/OptimisticTransactionStrategy.php b/src/Strategies/OptimisticTransactionStrategy.php index 9cb6880..c01127b 100644 --- a/src/Strategies/OptimisticTransactionStrategy.php +++ b/src/Strategies/OptimisticTransactionStrategy.php @@ -7,18 +7,18 @@ class OptimisticTransactionStrategy extends PessimisticTransactionStrategy { /** - * @throws \AnourValar\LaravelAtom\Exceptions\OptimisticTransactionException + * @throws \AnourValar\LaravelAtom\Exceptions\OptimisticException * * {@inheritDoc} * @see \AnourValar\LaravelAtom\Strategies\PessimisticTransactionStrategy::lock() */ - public function lock(string $sha1, Connection $connection, string $table): void + public function lock(string $sha1, Connection $connection): void { try { - parent::lock($sha1, $connection, $table); + parent::lock($sha1, $connection); } catch (\Illuminate\Database\QueryException $e) { if ($this->isLockException($e->getMessage())) { - throw new \AnourValar\LaravelAtom\Exceptions\OptimisticTransactionException(); + throw new \AnourValar\LaravelAtom\Exceptions\OptimisticException(); } throw $e; @@ -28,24 +28,23 @@ public function lock(string $sha1, Connection $connection, string $table): void /** * @param string $sha1 * @param \Illuminate\Database\Connection $connection - * @param string $table * @param bool $reTry * @throws \Exception * @return void */ - protected function apply(string $sha1, Connection $connection, string $table, bool $reTry = true): void + protected function apply(string $sha1, Connection $connection, bool $reTry = true): void { $connection->beginTransaction(); try { - $record = $connection->table($table)->lock($this->getLock())->where('sha1', '=', $sha1)->first(); + $record = $connection->table('locks')->lock($this->getLock())->where('sha1', '=', $sha1)->first(); } catch (\Illuminate\Database\QueryException $e) { $connection->rollBack(); throw $e; } if ($record) { - $connection->table($table)->where('sha1', '=', $sha1)->update(['updated_at' => date('Y-m-d H:i:s')]); + $connection->table('locks')->where('sha1', '=', $sha1)->update(['updated_at' => date('Y-m-d H:i:s')]); $connection->commit(); return; @@ -57,7 +56,7 @@ protected function apply(string $sha1, Connection $connection, string $table, bo } try { - $connection->table($table)->insert(['sha1' => $sha1, 'updated_at' => date('Y-m-d H:i:s')]); + $connection->table('locks')->insert(['sha1' => $sha1, 'updated_at' => date('Y-m-d H:i:s')]); $connection->commit(); } catch (\Illuminate\Database\QueryException $e) { $connection->rollBack(); @@ -67,7 +66,7 @@ protected function apply(string $sha1, Connection $connection, string $table, bo throw $e; } - $this->apply($sha1, $connection, $table, false); + $this->apply($sha1, $connection, false); } /** diff --git a/src/Strategies/PessimisticAdvisoryStrategy.php b/src/Strategies/PessimisticAdvisoryStrategy.php new file mode 100644 index 0000000..7f73a3a --- /dev/null +++ b/src/Strategies/PessimisticAdvisoryStrategy.php @@ -0,0 +1,24 @@ +transactionLevel()) { + throw new \LogicException('Lock can be applied only inside transaction'); + } + + $id1 = crc32($sha1) - 2147483648; + $id2 = crc32(strrev($sha1)) - 2147483648; + + $connection->select('SELECT pg_advisory_xact_lock(:id1, :id2)', ['id1' => $id1, 'id2' => $id2]); + } +} diff --git a/src/Strategies/PessimisticTransactionStrategy.php b/src/Strategies/PessimisticTransactionStrategy.php index 5f90c49..1aa013a 100644 --- a/src/Strategies/PessimisticTransactionStrategy.php +++ b/src/Strategies/PessimisticTransactionStrategy.php @@ -10,29 +10,35 @@ class PessimisticTransactionStrategy implements StrategyInterface * {@inheritDoc} * @see \AnourValar\LaravelAtom\Strategies\StrategyInterface::lock() */ - public function lock(string $sha1, Connection $connection, string $table): void + public function lock(string $sha1, Connection $connection): void { if (! $connection->transactionLevel()) { throw new \LogicException('Lock can be applied only inside transaction'); } - $this->apply($sha1, $connection, $table); + $this->apply($sha1, $connection); + + if (! mt_rand(0, 50)) { + $connection + ->table('locks') + ->where('updated_at', '<=', date('Y-m-d H:i:s', strtotime('-5 days'))) + ->delete(); + } } /** * @param string $sha1 * @param \Illuminate\Database\Connection $connection - * @param string $table * @param bool $reTry * @throws \Exception * @return void */ - protected function apply(string $sha1, Connection $connection, string $table, bool $reTry = true): void + protected function apply(string $sha1, Connection $connection, bool $reTry = true): void { - $record = $connection->table($table)->lock($this->getLock())->where('sha1', '=', $sha1)->first(); + $record = $connection->table('locks')->lock($this->getLock())->where('sha1', '=', $sha1)->first(); if ($record) { - $connection->table($table)->where('sha1', '=', $sha1)->update(['updated_at' => date('Y-m-d H:i:s')]); + $connection->table('locks')->where('sha1', '=', $sha1)->update(['updated_at' => date('Y-m-d H:i:s')]); return; } @@ -44,7 +50,7 @@ protected function apply(string $sha1, Connection $connection, string $table, bo $connection->beginTransaction(); try { - $connection->table($table)->insert(['sha1' => $sha1, 'updated_at' => date('Y-m-d H:i:s')]); + $connection->table('locks')->insert(['sha1' => $sha1, 'updated_at' => date('Y-m-d H:i:s')]); $connection->commit(); } catch (\Illuminate\Database\QueryException $e) { $connection->rollBack(); @@ -54,7 +60,7 @@ protected function apply(string $sha1, Connection $connection, string $table, bo throw $e; } - $this->apply($sha1, $connection, $table, false); + $this->apply($sha1, $connection, false); } /** diff --git a/src/Strategies/StrategyInterface.php b/src/Strategies/StrategyInterface.php index 05f95fd..4fbe4f8 100644 --- a/src/Strategies/StrategyInterface.php +++ b/src/Strategies/StrategyInterface.php @@ -11,9 +11,8 @@ interface StrategyInterface * * @param string $sha1 * @param \Illuminate\Database\Connection $connection - * @param string $table * @throws \Exception * @return void */ - public function lock(string $sha1, Connection $connection, string $table): void; + public function lock(string $sha1, Connection $connection): void; } diff --git a/src/database/migrations/2020_03_29_000000_create_locks.php b/src/database/migrations/2020_03_29_000000_create_locks.php index 92df6a9..76f0ec1 100644 --- a/src/database/migrations/2020_03_29_000000_create_locks.php +++ b/src/database/migrations/2020_03_29_000000_create_locks.php @@ -13,7 +13,7 @@ */ public function up() { - Schema::connection(config('atom.locks.connection'))->create(config('atom.locks.table'), function (Blueprint $table) { + Schema::connection(config('atom.locks.connection'))->create('locks', function (Blueprint $table) { $table->string('sha1', 40)->unique(); $table->timestamp('updated_at')->nullable()->index(); }); @@ -26,6 +26,6 @@ public function up() */ public function down() { - Schema::connection(config('atom.locks.connection'))->dropIfExists(config('atom.locks.table')); + Schema::connection(config('atom.locks.connection'))->dropIfExists('locks'); } }; diff --git a/src/resources/config/atom.php b/src/resources/config/atom.php index 9399509..bd9bad8 100644 --- a/src/resources/config/atom.php +++ b/src/resources/config/atom.php @@ -3,13 +3,14 @@ return [ 'locks' => [ 'connection' => null, - 'strategy' => 'pessimistic_transaction', - - 'table' => 'locks', + 'strategy' => 'pessimistic_advisory', 'strategies' => [ - 'pessimistic_transaction' => AnourValar\LaravelAtom\Strategies\PessimisticTransactionStrategy::class, - 'optimistic_transaction' => AnourValar\LaravelAtom\Strategies\OptimisticTransactionStrategy::class, + 'pessimistic_advisory' => AnourValar\LaravelAtom\Strategies\PessimisticAdvisoryStrategy::class, + 'optimistic_advisory' => AnourValar\LaravelAtom\Strategies\OptimisticAdvisoryStrategy::class, + + 'pessimistic_transaction' => AnourValar\LaravelAtom\Strategies\PessimisticTransactionStrategy::class, // @deprecated + 'optimistic_transaction' => AnourValar\LaravelAtom\Strategies\OptimisticTransactionStrategy::class, // @deprecated ], ],