Cache::lock() дає розподілене блокування: лише один процес на всіх серверах виконує ділянку коду одночасно.
$lock = Cache::lock("invoice:{$order->id}:generate", 30);
if ($lock->get()) {
try {
$order->generateInvoice();
} finally {
$lock->release();
}
}
Або коротше - з очікуванням до 5 секунд:
Cache::lock("invoice:{$order->id}:generate", 30)->block(5, function () use ($order) {
$order->generateInvoice();
});
Де це потрібно:
- подвійний клік чи повторний вебхук не має створити два рахунки;
- запланована команда, що запускається на кількох серверах;
- перерахунок дорогого кешу - лише один процес рахує, інші чекають.
Важливі деталі:
- Час життя блокування (30 с) - страховка, якщо процес упав і не звільнив його. Якщо робота може тривати довше, блокування закінчиться посередині, і зайде другий процес. Його подовжують через
refresh(). - Власник:
release()звільняє лише своє блокування. Щоб звільнити з іншого процесу (наприклад, у завданні черги), передають токен черезowner()іrestoreLock(). - Драйвер: потрібен спільний для всіх серверів - Redis, Memcached, database, DynamoDB.
fileблокує лише в межах одного сервера.
Блокування не замінює унікальних індексів у базі: індекс - остання лінія захисту, блокування лише зменшує кількість конфліктів.