WARNING: This component was finally removed from Symfony 3.3 and postponed for Symfony 3.4, to have more time to polish and stabilize it.
In computer science, a lock is "a synchronization mechanism for enforcing limits on access to a resource in an environment where there are many threads of execution". In Symfony applications locks are used for example to prevent more than one simultaneous execution of a given command in the server.
In Symfony 2.6 we added a LockHandler utility to simplify the creation of
locks. Internally, this utility uses PHP's flock()
function, so it's based
on the file system and cannot create locks shared in different servers.
In Symfony 3.3 we decided to extend this mechanism and we created a brand new Lock component with these features:
- It supports multiple lock stores:
flock()
, PHP Semaphore extension, Redis and Memcache; - It creates blocking, non-blocking and auto-expiring locks;
- It allows combining several stores (e.g. Redis and flock simultaneously) and it can apply different strategies (consensus, majority).
When used as an independent component, you first define the store to use and then create a factory used to create the locks:
1 2 3 4 5
use Symfony\Component\Lock\Factory;
use Symfony\Component\Lock\Store\SemaphoreStore;
$store = new SemaphoreStore();
$factory = new Factory($store);
Then, you can acquire, check, renew or release the locks:
1 2 3 4 5 6 7 8 9
// ...
$lock = $factory->createLock('pdf-invoice-generation');
if ($lock->acquire()) {
// The resource "pdf-invoice-generation" is locked.
// You can compute and generate invoice safely here.
$lock->release();
}
When used within the Symfony framework, its usage is greatly simplified. First, define the type of lock to use (you can define more than one lock type if needed):
1 2 3 4 5 6 7 8 9
# app/config/config.yml
framework:
# these are all the supported flock stores
lock: 'flock'
lock: 'semaphore'
lock: 'memcached://m1.docker'
lock: ['memcached://m1.docker', 'memcached://m2.docker']
lock: 'redis://r1.docker'
lock: ['redis://r1.docker', 'redis://r2.docker']
Then, use the lock
service to acquire, check, renew or release the locks:
1 2 3 4 5 6 7 8 9 10 11
// non-blocking, non-expiring
$lock = $container->get('lock')->acquire();
// blocking, non-expiring (waits indefinitely until the lock is acquired)
$lock = $container->get('lock')->acquire(true);
// non-blocking, expiring in 60 seconds (unless the lock is refreshed)
$lock = $container->get('lock')->acquire(false, 60);
// normal operations on the lock
$lock->isAcquired();
$lock->refresh();
$lock->release();
The documentation of the component is ready but not merged yet and the integration with the Symfony framework is also defined but pending to be merged.
Very useful for concurrence situations, thanks.
Great add ! thanks !
This is awesome! This is what was missing in my console component apps, for example long running cron jobs ;)
Amazing work, thanks.
A little typo :
blocking, non-expiring (waits indefinitely until the lock is acquired)
Should be
blocking, non-expiring (waits indefinitely until the lock is released)
@AbdelKader I don't think it's a typo ... but your version is correct too. It all depends on the perspective: "I'm waiting ... until I acquire the lock" vs. "I'm waiting ... until others release the lock".
Nice additions! Thanks
@javier it make sense. Thanks !