rtckit / react-redlock
Asynchronous distributed locks with Redis and ReactPHP
Installs: 7 766
Dependents: 1
Suggesters: 0
Security: 0
Stars: 16
Watchers: 4
Forks: 2
Open Issues: 0
Requires
- php: >=7.2
- clue/redis-react: ^2.5
- react/event-loop: ^1.2
Requires (Dev)
- phpstan/phpstan: ^0.12
- phpunit/phpunit: ^9.5
- vimeo/psalm: ^4.9
README
Asynchronous Redlock algorithm implementation for PHP
Quickstart
Once installed, you can incorporate Redlock in your projects by instantiating its Custodian; this entity is responsible for lock orchestration and it requires access to a Redis client object instance, e.g.
/* Instantiate prerequisites */ $factory = new \Clue\React\Redis\Factory(); $client = $factory->createLazyClient('127.0.0.1'); /* Instantiate our lock custodian */ $custodian = new \RTCKit\React\Redlock\Custodian($client);
Acquiring locks
For use cases where a binary outcome is desirable, the acquire()
method works best, e.g.:
/** * @param string $resource Redis key name * @param float $ttl Lock's time to live (in seconds) * @param ?string $token Unique identifier for lock in question * @return PromiseInterface */ $custodian->acquire('MyResource', 60, 'r4nd0m_token') ->then(function (?Lock $lock) { if (is_null($lock)) { // Ooops, lock could not be acquired for MyResource } else { // Awesome, MyResource is locked for a minute // ... // Be nice and release the lock when done $custodian->release($lock); } });
Spinlocks
The spin()
method is designed for situations where a process should keep trying acquiring a lock, e.g.
/** * @param int $attempts Maximum spin/tries * @param float $interval Spin/try interval (in seconds) * @param string $resource Redis key name * @param float $ttl Lock's time to live (in seconds) * @param ?string $token Unique identifier for lock in question * @return PromiseInterface */ $custodian->spin(100, 0.5, 'HotResource', 10, 'r4nd0m_token') ->then(function (?Lock $lock): void { if (is_null($lock)) { // Wow, after 100 tries (with a gap of 0.5 seconds) I've // given up acquiring a lock on HotResource } else { // Awesome, HotResource is locked for 10 seconds // ... // Again, be nice and release the lock when done $custodian->release($lock); } })
Lastly, the provided examples are a good starting point.
Requirements
Redlock is compatible with PHP 7.2+ and requires the clue/reactphp-redis library.
Installation
You can add the library as project dependency using Composer:
composer require rtckit/react-redlock
If you only need the library during development, for instance when used in your test suite, then you should add it as a development-only dependency:
composer require --dev rtckit/react-redlock
Tests
To run the test suite, clone this repository and then install dependencies via Composer:
composer install
Then, go to the project root and run:
php -d memory_limit=-1 ./vendor/bin/phpunit -c ./etc/phpunit.xml.dist
Static Analysis
In order to ensure high code quality, Redlock uses PHPStan and Psalm:
php -d memory_limit=-1 ./vendor/bin/phpstan analyse -c ./etc/phpstan.neon -n -vvv --ansi --level=max src php -d memory_limit=-1 ./vendor/bin/psalm --config=./etc/psalm.xml --show-info=true
License
MIT, see LICENSE file.
Acknowledgments
- antirez - Original blog post
- ReactPHP Project
- clue/reactphp-redis - Async Redis client implementation
Contributing
Bug reports (and small patches) can be submitted via the issue tracker. Forking the repository and submitting a Pull Request is preferred for substantial patches.