-
Notifications
You must be signed in to change notification settings - Fork 2
Useful Implementation Patterns
mpyw edited this page Nov 22, 2023
·
5 revisions
When you are afraid to organize all into withLocking()
callback argument, whereas using lockOrFail()
instead makes it difficult to control the lock lifecycle...
In that case, you can keep locking only as long as the class instance is alive, and release it when it is destructed.
<?php
declare(strict_types=1);
namespace App\Services\Batch;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Facades\DB;
use Mpyw\LaravelDatabaseAdvisoryLock\Contracts\LockFailedException as DatabaseLockFailedException;
use Mpyw\LaravelDatabaseAdvisoryLock\Contracts\SessionLock;
use App\Services\Bank\Exceptions\LockFailedException;
class BatchExecutor
{
private readonly SessionLock $lock;
public function __construct(
private readonly ConnetcionInterface $db,
) {
if ($this->db->transactionLevel() > 0) {
throw new LogicException("Don't use transactions outside of this logic.");
}
try {
// Keep locking as long as the instance is alive.
$this->lock = $this->db->forSession()->lockOrFail(self::class);
} catch (DatabaseLockFailedException $e) {
// If necessary, wrap exceptions thrown by the library with your own domain-level exceptions.
throw new LockFailedException($e->getMessage(), previous: $e);
}
}
public function __destruct()
{
// The lock is released automatically on SessionLock::__destruct(), but written here for clarity.
$this->lock->release();
}
public function runFoo(): void
{
/* ... */
}
public function runBar(): void
{
/* ... */
}
public function runBaz(): void
{
/* ... */
}
}
function runBatchExclusively(): void
{
$batch = new BatchExecutor(DB::connection());
// Start Critical Section
$batch->runFoo();
$batch->runBar();
$batch->runBaz();
// End Critical Section
}