A simple Laravel package for distributed lock around Redis atomic locks with retry + backoff (with jitter). Itโs easy to read, chainable, and testable.
You can install the package via composer:
composer require andp97/laravel-locky
You can publish the config file with:
php artisan vendor:publish --tag="laravel-locky-config"
use Pavons\Locky\Facades\Locky;
$result = Locky::make("widgets:{$id}")
->ttl(10)
->run(function () use ($id) {
processWidget($id);
return true;
});
use Pavons\Locky\Facades\Locky;
Locky::make("widgets:{$orderId}")
->ttl(12)
->attempts(7)
->baseDelayMs(75) // 75, 150, 300, 600, 1200, 2000โฆ
->multiplier(2.0)
->maxDelayMs(2000)
->jitter('full') // none|equal|full
->onRetry(function ($key, $attempt, $sleepMs) {
logger()->notice("Retrying lock", compact('key', 'attempt', 'sleepMs'));
})
->onFail(function ($key, $attempts) {
// Optional graceful fallback
report(new \RuntimeException("Lock failed for {$key} after {$attempts} attempts"));
return null; // Returning here prevents throwing; omit to throw
})
->run(function () use ($orderId) {
settleOrder($orderId);
});
public function handle(): void
{
Locky::make("job:import:{$this->batchId}")
->ttl(15)
->attempts(8)
->jitter('equal')
->run(function () {
$this->performImport(); // exclusive section
});
}
-
Use Redis (
CACHE_DRIVER=redis
) soCache::lock()
is truly distributed. -
Pick TTL > worst-case critical section, but keep it tight; split long work to keep the locked part short.
-
Jitter:
-
full
: best at smoothing thundering herds (default). -
equal
: narrows variance while avoiding sync. -
none
: only if you really want deterministic waits.
-
-
Hooks (
onRetry
,onFail
) make it easy to add logs/metrics without cluttering business code.
composer test
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
Please review our security policy on how to report security vulnerabilities.
The MIT License (MIT). Please see License File for more information.