wordcoolframework / coolrace
handle race condition
Fund package maintenance!
wordcoolframework
Requires
- php: ^8.2
- illuminate/contracts: ^10.0||^11.0||^12.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.1.1||^7.10.0
- orchestra/testbench: ^10.0.0||^9.0.0||^8.22.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-arch: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
This package is auto-updated.
Last update: 2025-06-28 18:25:12 UTC
README
CoolRace
is a PHP library designed to handle distributed locking in a Laravel application. It provides a flexible and robust way to manage locks for critical sections of code, ensuring that only one process can execute a given block of code at a time. The library supports various locking drivers and includes features like context prefixes, retries, and model-specific locking.
Features
- Distributed Locking: Prevent race conditions in distributed systems.
- Context Prefixing: Add prefixes to lock keys for better organization.
- Retry Mechanism: Automatically retry acquiring locks with configurable delays.
- Model Locking: Lock operations based on Eloquent model instances.
- Multiple Locks: Acquire multiple locks in a sorted order to avoid deadlocks.
- Customizable Drivers: Use different locking backends (e.g., file-based locking with
FileLockDriver
). - Timeout Handling: Handle lock acquisition failures gracefully with timeouts.
Installation
To use CoolRace
in your Laravel project, follow these steps:
-
Require the Package Ensure your project uses PHP 7.4+ and Laravel 8.x or higher. Install the package via Composer:
composer require wordcoolframework/coolrace
-
Set Up the Lock Driver Configure the lock driver (e.g.,
FileLockDriver
) by providing a storage path for lock files. Update your configuration or service provider as needed. -
Register the Service If not automatically registered, bind
CoolRace
to your Laravel service container:$this->app->singleton(CoolRace::class, function () { return new CoolRace(new FileLockDriver(storage_path('locks'))); });
Usage
The CoolRace
class provides several methods to manage locks. Below are the primary methods and their usage.
1. Basic Lock
The lock
method acquires a lock for a given key, executes a callback, and releases the lock afterward. If the lock cannot be acquired within the timeout, it throws a LockTimeoutException
.
use Wordcoolframework\CoolRace\CoolRace; $coolRace = new CoolRace(new FileLockDriver(storage_path('locks'))); $result = $coolRace->lock('my-resource', function () { // Critical section return 'Operation completed'; }, 10);
2. Try Lock
The tryLock
method attempts to acquire a lock but returns null
instead of throwing an exception if the lock cannot be acquired.
$result = $coolRace->tryLock('my-resource', function () { return 'Operation completed'; }, 5); if ($result === null) { echo "Could not acquire lock"; } else { echo $result; }
3. Lock with Retry
The lockWithRetry
method attempts to acquire a lock multiple times with a delay between attempts. If all retries fail, it throws a LockTimeoutException
.
$result = $coolRace->lockWithRetry('my-resource', function () { return 'Operation completed'; }, 10, 3, 500);
4. Model Locking
The lockForModel
method creates a lock based on an Eloquent model's class name and primary key.
use App\Models\User; $user = User::find(1); $result = $coolRace->lockForModel($user, function () use ($user) { $user->balance += 100; $user->save(); return 'Balance updated'; });
5. Multiple Locks
The lockMultiple
method acquires multiple locks in a sorted order to avoid deadlocks, executes the callback, and releases all locks.
$result = $coolRace->lockMultiple(['resource1', 'resource2'], function () { return 'Multiple resources locked'; }, 10);
6. Context Prefix
The withContext
method creates a new instance with a prefix for all lock keys, useful for scoping locks.
$scopedCoolRace = $coolRace->withContext('user:123'); $result = $scopedCoolRace->lock('action', function () { return 'Scoped operation'; });
7. Check Lock Status
The isLocked
method checks if a lock is currently held.
if ($coolRace->isLocked('my-resource')) { echo "Resource is locked"; } else { echo "Resource is available"; }
8. Manual Lock Management
Use acquire
and release
for manual lock management without a callback.
if ($coolRace->acquire('my-resource', 10)) { try { // Critical section } finally { $coolRace->release('my-resource'); } }
9. Release All Locks by Prefix
The forceReleaseAllByPrefix
method releases all locks matching a given prefix (if supported by the driver).
$coolRace->forceReleaseAllByPrefix('user:123');
10. Lock Until a Specific Time
The lockUntil
method acquires a lock with a timeout based on a DateTimeInterface
instance.
$until = new \DateTime('+1 minute'); $result = $coolRace->lockUntil('my-resource', function () { return 'Operation completed'; }, $until);
Example: Preventing Double Payments
Here’s a practical example of using CoolRace
to prevent double payments for an order:
use Wordcoolframework\CoolRace\CoolRace; use App\Models\Order; $coolRace = app(CoolRace::class); $order = Order::find(123); $result = $coolRace->lockForModel($order, function () use ($order) { if ($order->is_paid) { throw new Exception('Order already paid'); } // Process payment $order->is_paid = true; $order->save(); return 'Payment processed'; }); echo $result;
FileLockDriver
The FileLockDriver
is a simple file-based locking driver included with CoolRace
. It stores lock files in a specified directory and uses file creation as a locking mechanism.
Configuration
use Wordcoolframework\CoolRace\Drivers\FileLockDriver; $driver = new FileLockDriver(storage_path('locks')); $coolRace = new CoolRace($driver);
Notes
- Ensure the lock directory is writable by the PHP process.
- The driver sanitizes lock keys to prevent invalid file names.
- The
releaseAllByPrefix
method deletes all lock files matching the given prefix.
Error Handling
- LockTimeoutException: Thrown by
lock
andlockWithRetry
when a lock cannot be acquired within the timeout or retries. - Handle exceptions in your application logic to gracefully manage lock failures.
try { $result = $coolRace->lock('my-resource', function () { return 'Operation completed'; }); } catch (LockTimeoutException $e) { echo "Failed to acquire lock: " . $e->getMessage(); }
Contributing
Contributions are welcome! Please submit pull requests or issues to the GitHub repository.
License
This project is licensed under the MIT License.