| 12345678910111213141516171819202122232425262728293031 |
- <?php
- namespace App\Support;
- use Illuminate\Support\Facades\DB;
- use RuntimeException;
- /** A command-wide lock also covers direct cron invocations and separate deployments sharing MySQL. */
- class SyncCommandLock
- {
- public function run(string $command, callable $work): ?int
- {
- $connection = DB::connection();
- $name = 'bot-sync:' . substr(hash('sha256', $connection->getDatabaseName() . ':' . $connection->getTablePrefix() . ':' . $command), 0, 48);
- if (in_array($connection->getDriverName(), ['mysql', 'mariadb'], true)) {
- $acquired = $connection->selectOne('SELECT GET_LOCK(?, 0) AS acquired', [$name], false)->acquired;
- if ($acquired === null) throw new RuntimeException('Unable to acquire synchronization lock');
- if ((int) $acquired !== 1) return null;
- try { return $work(); }
- finally { $connection->selectOne('SELECT RELEASE_LOCK(?) AS released', [$name], false); }
- }
- $handle = fopen(sys_get_temp_dir() . '/' . $name . '.lock', 'c');
- if ($handle === false) throw new RuntimeException('Unable to open synchronization lock');
- try {
- if (!flock($handle, LOCK_EX | LOCK_NB)) return null;
- try { return $work(); }
- finally { flock($handle, LOCK_UN); }
- } finally { fclose($handle); }
- }
- }
|