andp97 / laravel-locky
A simple Laravel package for distributed lock
Fund package maintenance!
Andrea Pavone
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/andp97/laravel-locky
Requires
- php: ^8.4|^8.3
- illuminate/contracts: ^11.0||^12.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.0.0||^9.0.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
README
A simple Laravel package for distributed lock
A simple Laravel package for distributed lock around Redis atomic locks with retry + backoff (with jitter). It’s easy to read, chainable, and testable.
Installation
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"
Usage
Basic
use Pavons\Locky\Facades\Locky; $result = Locky::make("widgets:{$id}") ->ttl(10) ->run(function () use ($id) { processWidget($id); return true; });
With retries, exponential backoff, and full jitter
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); });
In a queued job (recommended)
public function handle(): void { Locky::make("job:import:{$this->batchId}") ->ttl(15) ->attempts(8) ->jitter('equal') ->run(function () { $this->performImport(); // exclusive section }); }
Notes & best practices
-
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.
Testing
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.