1. Go to this page and download the library: Download siberfx/laravel-mutex-lock library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
use Siberfx\MutexLock\Facades\Mutex;
$result = Mutex::synchronized('reports:monthly', function () {
// Only one process at a time runs this block.
return Report::generateMonthly();
});
use Siberfx\MutexLock\Facades\Mutex;
$invoice = Mutex::synchronized("invoice:{$order->id}", function () use ($order) {
return $order->invoice ?? $order->invoice()->create([
'number' => Invoice::nextNumber(),
'total' => $order->total,
]);
});
try {
Mutex::synchronized('import', fn () => $importer->run());
} catch (ImportFailed $e) {
// The lock has already been released at this point.
report($e);
}
Mutex::synchronized('report', $callback); // configured default (mutex-lock.timeout, 10s)
Mutex::synchronized('report', $callback, timeout: 30); // wait up to 30 seconds
Mutex::synchronized('report', $callback, timeout: 2.5); // fractional seconds are fine
Mutex::synchronized('report', $callback, timeout: 0); // try once, don't wait
Mutex::synchronized('report', $callback, timeout: INF); // wait as long as it takes
use Malkusch\Lock\Exception\LockAcquireTimeoutException;
use Siberfx\MutexLock\Facades\Mutex;
public function store(Request $request)
{
try {
$import = Mutex::synchronized(
"import:{$request->user()->id}",
fn () => Import::start($request->file('csv')),
timeout: 0,
);
} catch (LockAcquireTimeoutException) {
return back()->withErrors('An import is already running. Please wait for it to finish.');
}
return to_route('imports.show', $import);
}
use Siberfx\MutexLock\MutexManager;
final class TransferMoney
{
public function __construct(private readonly MutexManager $mutex) {}
public function __invoke(Account $from, Account $to, int $cents): void
{
// Lock both accounts in a stable order to avoid deadlocks.
[$first, $second] = collect([$from->id, $to->id])->sort()->values()->all();
$this->mutex->synchronized("account:{$first}", fn () =>
$this->mutex->synchronized("account:{$second}", fn () =>
DB::transaction(function () use ($from, $to, $cents) {
$from->refresh()->decrement('balance', $cents);
$to->refresh()->increment('balance', $cents);
})
)
);
}
}
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Siberfx\MutexLock\Jobs\Middleware\WithMutex;
class RecalculateInvoice implements ShouldQueue
{
use Queueable;
public int $tries = 10;
public function __construct(public Invoice $invoice) {}
public function middleware(): array
{
return [
(new WithMutex("invoice:{$this->invoice->id}"))->releaseAfter(15),
];
}
public function handle(): void
{
$this->invoice->recalculate();
}
}
public function withdraw(Wallet $wallet, int $cents): Transaction
{
return Mutex::synchronized("wallet:{$wallet->id}", function () use ($wallet, $cents) {
$wallet->refresh(); // always re-read state *inside* the lock
throw_if($wallet->balance < $cents, InsufficientFunds::class);
return DB::transaction(function () use ($wallet, $cents) {
$wallet->decrement('balance', $cents);
return $wallet->transactions()->create(['amount' => -$cents]);
});
});
}
public function __invoke(Request $request)
{
$eventId = $request->input('id');
Mutex::check(
"webhook:{$eventId}",
fn () => ! ProcessedWebhook::whereKey($eventId)->exists(),
)->then(function () use ($eventId, $request) {
DB::transaction(function () use ($eventId, $request) {
HandleStripeEvent::dispatchSync($request->all());
ProcessedWebhook::create(['id' => $eventId]);
});
});
return response()->noContent();
}
public function reserve(Product $product, int $qty): Reservation
{
return Mutex::synchronized("stock:{$product->sku}", function () use ($product, $qty) {
$available = $product->stock - $product->reservations()->active()->sum('qty');
if ($available < $qty) {
throw new OutOfStock($product, $available);
}
return $product->reservations()->create([
'qty' => $qty,
'expires_at' => now()->addMinutes(15),
]);
}, timeout: 3);
}
use Illuminate\Console\Command;
use Malkusch\Lock\Exception\LockAcquireTimeoutException;
use Siberfx\MutexLock\Facades\Mutex;
class SyncProducts extends Command
{
protected $signature = 'products:sync';
public function handle(): int
{
try {
return Mutex::synchronized('command:products:sync', function () {
$this->info('Syncing…');
app(ProductSync::class)->run();
return self::SUCCESS;
}, timeout: 0);
} catch (LockAcquireTimeoutException) {
$this->warn('Another sync is already running. Skipping.');
return self::SUCCESS;
}
}
}
use Malkusch\Lock\Exception\LockReleaseException;
try {
Mutex::synchronized('job', fn () => $job->run());
} catch (LockReleaseException $e) {
$result = $e->getCodeResult(); // what the callback returned
$exception = $e->getCodeException(); // what the callback threw, if anything
}
use Malkusch\Lock\Exception\LockAcquireTimeoutException;
Mutex::shouldReceive('synchronized')
->andThrow(LockAcquireTimeoutException::create(0));
$this->post('/imports')->assertSessionHasErrors();