From 6469016c2c792c8950c0dcca769c002816bca678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Sat, 14 Jan 2017 22:19:21 +0100 Subject: [PATCH 1/9] Add the Lock Component --- components/lock.rst | 319 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 components/lock.rst diff --git a/components/lock.rst b/components/lock.rst new file mode 100644 index 00000000000..34ef93b2fb3 --- /dev/null +++ b/components/lock.rst @@ -0,0 +1,319 @@ +.. index:: + single: Lock + single: Components; Lock + +The Lock Component +==================== + + The Lock Component provides a mechanism to garentee an exclusive access into + a critical section. The component ships with ready to use stores for the + most common backends. + +.. versionadded:: 3.3 + The Lock component was introduced in Symfony 3.3. + +Installation +------------ + +You can install the component in 2 different ways: + +* :doc:`Install it via Composer ` (``symfony/lock`` on `Packagist`_); +* Use the official Git repository (https://github.com/symfony/lock). + +.. include:: /components/require_autoload.rst.inc + + +Usage +----- + +In order to centralize state of locks, you first need to create a ``Store``. +Then, you can ask to this store to create a Lock for your ``resource``. + +The :method:`Symfony\\Component\\Lock\\LockInterface::acquire` method tries to +acquire the lock. If the lock is can not be acquired, the method throws a +:class:`Symfony\\Component\\Lock\\Exception\\LockConflictedException`. You can +safly call the ``acquire()`` method several time, even if you already acquired +it. + +.. code-block:: php + + use Symfony\Component\Lock\Store\SemaphoreStore; + use Symfony\Component\Lock\Exception\LockConflictedException; + + $store = new SemaphoreStore(); + $lock = $store->createLock('hello'); + + try { + $lock->acquire(); + // the resource "hello" is locked. You can perform your task safely. + + // do whatever you want. + + $lock->release(); + } catch (LockConflictedException $e) { + // the resource "hello" is already locked by another process + } + +The first argument of `createLock` is a string representation of the +``resource`` to lock. + +.. note:: + + In opposition to some other implementations, the Lock Component distinguish + locks instances, even when they are created from the same ``resource``. + If you want to share a lock in several services. You have to share the + instance of Lock returned by the ``Store::createLock`` method. + + +Blocking locks +-------------- + +You can pass an optional blocking argument as the first argument to the +:method:`Symfony\\Component\\Lock\\LockInterface::acquire` method, which +defaults to ``false``. If this is set to ``true``, your PHP code will wait +infinitely until the lock is released by another process. + +Some ``Store`` (but not all) natively supports this features. When they don't, +you can decorate it with the ``RetryTillSaveStore``. + +.. code-block:: php + + use Symfony\Component\Lock\Store\RedisStore; + use Symfony\Component\Lock\Store\RetryTillSaveStore; + + $store = new RedisStore(new \Predis\Client('tcp://localhost:6379')); + $store = new RetryTillSaveStore($store); + + $lock = $store->createLock('hello'); + + $lock->acquire(true); + + + +Expirable Locks +--------------- + +Working with a remote ``Store`` is hard. In oposition to local ``Stores`` +(like :ref:`FlockStore ` or :ref:`SemaphoreStore `) there is now way for the remote +``Store`` to know whether or not the locker process is till alive. Due tu bugs, +fatal errors or segmentation fault, we can't garentee that the ``release()`` +function will be called, which would cause a ``resource`` to be locked +infinitely. + +To fill this gap, the remote ``Stores`` provide an expirable mechanism: The lock +is acquired for a defined amount of time (named TTL for Time To Live). +When the timeout occured, the lock is automatically released even if the locker +don't call the ``release()`` method. + +That's why, when you create a lock on an expirable ``Store``. You have to choose +carrefully the correct TTL. When too low, you take the risk to "loose" the lock +(and someone else acquire it) wheras you don't finish your task. +When too hight and the process crash before you call the ``release()`` method, +the ``resource`` will stay lock till the timeout. + + +.. code-block:: php + + use Symfony\Component\Lock\Store\RedisStore; + + $store = new RedisStore(new \Predis\Client('tcp://localhost:6379')); + + $lock = $store->createLock('hello', 30); + + $lock->acquire(); + try { + // perfom a job during less than 30 seconds + } finally { + $lock->release() + } + +.. tip:: + + To avoid to let the Lock in a locking state, try to always release an + expirable lock by wraping the job in a try/catch block for instance. + + +When you have to work on a really long task, you should not set the TTL to +overlaps the duration of this task. Instead, the Lock Component expose a +:method:`Symfony\\Component\\Lock\\LockInterface::refresh` method in order to +put off the TTL of the Lock. Thereby you can choose a small initial TTL, and +regulary refresh the lock + +.. code-block:: php + + use Symfony\Component\Lock\Store\RedisStore; + + $store = new RedisStore(new \Predis\Client('tcp://localhost:6379')); + + $lock = $store->createLock('hello', 30); + + $lock->acquire(); + try { + while (!$finished) { + // perfom a small part of the job. + + $lock->refresh(); + // resource is locked for 30 more seconds. + } + } finally { + $lock->release() + } + + +Available Stores +---------------- + +``Stores`` are classes that implement :class:`Symfony\\Component\\Lock\\StoreInterface`. +This component provides several adapters ready to use in your applications. + +Here is a small summary of availanble ``Stores`` and theire capabilities. + ++----------------------------------------------+--------+----------+-----------+ +| Store | Scope | Blocking | Expirable | ++==============================================+========+==========+===========+ +| :ref:`FlockStore ` | local | yes | no | ++----------------------------------------------+--------+----------+-----------+ +| :ref:`MemcachedStore ` | remote | no | yes | ++----------------------------------------------+--------+----------+-----------+ +| :ref:`RedisStore ` | remote | no | yes | ++----------------------------------------------+--------+----------+-----------+ +| :ref:`SemaphoreStore ` | local | yes | no | ++----------------------------------------------+--------+----------+-----------+ + +.. tip:: + + Calling the :method:`Symfony\\Component\\Lock\\LockInterface::refresh` + method on a Lock created from a non expirable ``Store`` like + :ref:`FlockStore ` will do nothing. + +.. _lock-store-flock: + +FlockStore +~~~~~~~~~~ + +The FlockStore use the fileSystem on the local computer to lock and store the +``resource``. +It does not supports expiration, but the lock is automaticaly released when the +PHP process is terminated. + + +.. code-block:: php + + use Symfony\Component\Lock\Store\FlockStore; + + $store = new FlockStore(sys_get_temp_dir()); + +The first argument of the constructor is the path to the directory where the +file will be created. + +.. caution:: + + Beware, some filesystem (like some version of NFS) does not support locking. + We suggest to use local file, or to use a Store dedicated to remote usage + like Redis or Memcached. + + +.. _Packagist: https://packagist.org/packages/symfony/lock + +.. _lock-store-memcached: + +MemcachedStore +~~~~~~~~~~~~~~ + +The MemcachedStore stores state of ``resource`` in a Memcached server. This +``Store`` does not support blocking, and expect a TLL to avoid infinity locks. + +.. note:: + + Memcached does not supports TTL lower than 1 seconds. + + +It requires to have installed Memcached and have created a connection that +implements the ``\Memcached`` classes:: + +.. code-block:: php + + use Symfony\Component\Lock\Store\RedisStore; + + $memcached = new \Memcached(); + $memcached->addServer('localhost', 11211); + + $store = new MemcachedStore($memcached); + +.. _lock-store-redis: + +RedisStore +~~~~~~~~~~ + +The RedisStore uses an instance of Redis to store the state of the ``resource``. +This ``Store`` does not support blocking, and expect a TLL to avoid infinity +locks. + +It requires to have installed Redis and have created a connection that +implements the ``\Redis``, ``\RedisArray``, ``\RedisCluster`` or ``\Predis`` +classes:: + +.. code-block:: php + + use Symfony\Component\Lock\Store\RedisStore; + + $redis = new \Redis(); + $redis->connect('localhost'); + + $store = new RedisStore($redis); + +.. _lock-store-semaphore: + +SemaphoreStore +~~~~~~~~~~~~~~ + +The SemaphoreStore uses the PHP semaphore function to lock a ``resources``. + + +.. code-block:: php + + use Symfony\Component\Lock\Store\SemaphoreStore; + + $store = new SemaphoreStore($redis); + +.. _lock-store-combined: + +CombinedStore +~~~~~~~~~~~~~ + +The CombinedStore synchronize several ``Stores`` together. When it's used to +acquired a Lock, it forward the call to the managed ``Stores``, and regarding the +result, uses a quorum to decide whether or not the lock is acquired. + +.. note:: + + This ``Store`` is usefull for High availability application. You can provide + several Redis Server, and use theses server to manage the Lock. A + MajorityQuorum is enougth to safly acquire a lock while it allow some Redis + server failure. + +.. code-block:: php + + use Symfony\Component\Lock\Quorum\MajorityQuorum; + use Symfony\Component\Lock\Store\CombinedStore; + use Symfony\Component\Lock\Store\RedisStore; + + $stores = []; + foreach (['server1', 'server2', 'server3'] as $server) { + $redis= new \Redis(); + $redis->connect($server); + + $stores[] = new RedisStore($redis); + } + + $store = new CombinedStore($stores, new MajorityQuorum()); + + +.. tip:: + + You can use the CombinedStore with the UnanimousQuorum to implement chained + ``Stores``. It'll allow you to acquire easy local locks before asking for a + remote lock + + +.. _Packagist: https://packagist.org/packages/symfony/lock From 60bfe032ceaf2f5c158dd68167f2332f0f7ffe12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Sun, 15 Jan 2017 18:07:56 +0100 Subject: [PATCH 2/9] Fix typo --- components/lock.rst | 62 ++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/components/lock.rst b/components/lock.rst index 34ef93b2fb3..86e89f0a8e0 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -5,9 +5,9 @@ The Lock Component ==================== - The Lock Component provides a mechanism to garentee an exclusive access into - a critical section. The component ships with ready to use stores for the - most common backends. + The Lock Component provides a mechanism to guarantee an exclusive access + into a critical section. The component ships with ready to use stores for + the most common backends. .. versionadded:: 3.3 The Lock component was introduced in Symfony 3.3. @@ -22,7 +22,6 @@ You can install the component in 2 different ways: .. include:: /components/require_autoload.rst.inc - Usage ----- @@ -30,9 +29,9 @@ In order to centralize state of locks, you first need to create a ``Store``. Then, you can ask to this store to create a Lock for your ``resource``. The :method:`Symfony\\Component\\Lock\\LockInterface::acquire` method tries to -acquire the lock. If the lock is can not be acquired, the method throws a +acquire the lock. If the lock can not be acquired, the method throws a :class:`Symfony\\Component\\Lock\\Exception\\LockConflictedException`. You can -safly call the ``acquire()`` method several time, even if you already acquired +safely call the ``acquire()`` method several times, even if you already acquired it. .. code-block:: php @@ -41,30 +40,30 @@ it. use Symfony\Component\Lock\Exception\LockConflictedException; $store = new SemaphoreStore(); - $lock = $store->createLock('hello'); + $lock = $store->createLock('invoice-pdf-generation'); try { $lock->acquire(); - // the resource "hello" is locked. You can perform your task safely. + // the resource "invoice-pdf-generation" is locked. - // do whatever you want. + // You can compute and generate invoice safely here. $lock->release(); } catch (LockConflictedException $e) { - // the resource "hello" is already locked by another process + // the resource "invoice-pdf-generation" is already locked by another process } -The first argument of `createLock` is a string representation of the +The first argument of ``createLock`` is a string representation of the ``resource`` to lock. .. note:: - In opposition to some other implementations, the Lock Component distinguish - locks instances, even when they are created from the same ``resource``. + In opposition to some other implementations, the Lock Component + distinguishes locks instances, even when they are created from the same + ``resource``. If you want to share a lock in several services. You have to share the instance of Lock returned by the ``Store::createLock`` method. - Blocking locks -------------- @@ -73,7 +72,7 @@ You can pass an optional blocking argument as the first argument to the defaults to ``false``. If this is set to ``true``, your PHP code will wait infinitely until the lock is released by another process. -Some ``Store`` (but not all) natively supports this features. When they don't, +Some ``Store`` (but not all) natively supports this feature. When they don't, you can decorate it with the ``RetryTillSaveStore``. .. code-block:: php @@ -84,20 +83,17 @@ you can decorate it with the ``RetryTillSaveStore``. $store = new RedisStore(new \Predis\Client('tcp://localhost:6379')); $store = new RetryTillSaveStore($store); - $lock = $store->createLock('hello'); + $lock = $store->createLock('notification-flush'); $lock->acquire(true); - - Expirable Locks --------------- -Working with a remote ``Store`` is hard. In oposition to local ``Stores`` -(like :ref:`FlockStore ` or :ref:`SemaphoreStore `) there is now way for the remote -``Store`` to know whether or not the locker process is till alive. Due tu bugs, -fatal errors or segmentation fault, we can't garentee that the ``release()`` -function will be called, which would cause a ``resource`` to be locked +Working with a remote ``Store`` is hard. There is now way for the remote +``Store`` to know if the locker process is till alive. +Due to bugs, fatal errors or segmentation fault, we can't guarantee that the +``release()`` method will be called, which would cause a ``resource`` to be locked infinitely. To fill this gap, the remote ``Stores`` provide an expirable mechanism: The lock @@ -106,9 +102,9 @@ When the timeout occured, the lock is automatically released even if the locker don't call the ``release()`` method. That's why, when you create a lock on an expirable ``Store``. You have to choose -carrefully the correct TTL. When too low, you take the risk to "loose" the lock -(and someone else acquire it) wheras you don't finish your task. -When too hight and the process crash before you call the ``release()`` method, +carefully the correct TTL. When too low, you take the risk to "lose" the lock +(and someone else acquire it) whereas you don't finish your task. +When too high and the process crash before you call the ``release()`` method, the ``resource`` will stay lock till the timeout. @@ -118,7 +114,7 @@ the ``resource`` will stay lock till the timeout. $store = new RedisStore(new \Predis\Client('tcp://localhost:6379')); - $lock = $store->createLock('hello', 30); + $lock = $store->createLock('charts-generation', 30); $lock->acquire(); try { @@ -129,12 +125,11 @@ the ``resource`` will stay lock till the timeout. .. tip:: - To avoid to let the Lock in a locking state, try to always release an + To avoid letting the Lock in a locking state, try to always release an expirable lock by wraping the job in a try/catch block for instance. - When you have to work on a really long task, you should not set the TTL to -overlaps the duration of this task. Instead, the Lock Component expose a +overlap the duration of this task. Instead, the Lock Component expose a :method:`Symfony\\Component\\Lock\\LockInterface::refresh` method in order to put off the TTL of the Lock. Thereby you can choose a small initial TTL, and regulary refresh the lock @@ -145,7 +140,7 @@ regulary refresh the lock $store = new RedisStore(new \Predis\Client('tcp://localhost:6379')); - $lock = $store->createLock('hello', 30); + $lock = $store->createLock('charts-generation', 30); $lock->acquire(); try { @@ -159,14 +154,13 @@ regulary refresh the lock $lock->release() } - Available Stores ---------------- ``Stores`` are classes that implement :class:`Symfony\\Component\\Lock\\StoreInterface`. This component provides several adapters ready to use in your applications. -Here is a small summary of availanble ``Stores`` and theire capabilities. +Here is a small summary of available ``Stores`` and their capabilities. +----------------------------------------------+--------+----------+-----------+ | Store | Scope | Blocking | Expirable | @@ -289,7 +283,7 @@ result, uses a quorum to decide whether or not the lock is acquired. This ``Store`` is usefull for High availability application. You can provide several Redis Server, and use theses server to manage the Lock. A - MajorityQuorum is enougth to safly acquire a lock while it allow some Redis + MajorityQuorum is enougth to safely acquire a lock while it allow some Redis server failure. .. code-block:: php From d1d3b7126696feb4432af1dbb6c77b066a895c31 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 16 Jan 2017 10:08:21 +0100 Subject: [PATCH 3/9] Fixed minor typos and syntax issues --- components/lock.rst | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/components/lock.rst b/components/lock.rst index 86e89f0a8e0..45d36192a4e 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -98,7 +98,7 @@ infinitely. To fill this gap, the remote ``Stores`` provide an expirable mechanism: The lock is acquired for a defined amount of time (named TTL for Time To Live). -When the timeout occured, the lock is automatically released even if the locker +When the timeout occurred, the lock is automatically released even if the locker don't call the ``release()`` method. That's why, when you create a lock on an expirable ``Store``. You have to choose @@ -126,13 +126,13 @@ the ``resource`` will stay lock till the timeout. .. tip:: To avoid letting the Lock in a locking state, try to always release an - expirable lock by wraping the job in a try/catch block for instance. + expirable lock by wrapping the job in a try/catch block for instance. When you have to work on a really long task, you should not set the TTL to overlap the duration of this task. Instead, the Lock Component expose a :method:`Symfony\\Component\\Lock\\LockInterface::refresh` method in order to put off the TTL of the Lock. Thereby you can choose a small initial TTL, and -regulary refresh the lock +regularly refresh the lock .. code-block:: php @@ -145,7 +145,7 @@ regulary refresh the lock $lock->acquire(); try { while (!$finished) { - // perfom a small part of the job. + // perform a small part of the job. $lock->refresh(); // resource is locked for 30 more seconds. @@ -186,10 +186,8 @@ FlockStore ~~~~~~~~~~ The FlockStore use the fileSystem on the local computer to lock and store the -``resource``. -It does not supports expiration, but the lock is automaticaly released when the -PHP process is terminated. - +``resource``. It does not supports expiration, but the lock is automatically +released when the PHP process is terminated. .. code-block:: php @@ -206,7 +204,6 @@ file will be created. We suggest to use local file, or to use a Store dedicated to remote usage like Redis or Memcached. - .. _Packagist: https://packagist.org/packages/symfony/lock .. _lock-store-memcached: @@ -221,7 +218,6 @@ The MemcachedStore stores state of ``resource`` in a Memcached server. This Memcached does not supports TTL lower than 1 seconds. - It requires to have installed Memcached and have created a connection that implements the ``\Memcached`` classes:: @@ -263,7 +259,6 @@ SemaphoreStore The SemaphoreStore uses the PHP semaphore function to lock a ``resources``. - .. code-block:: php use Symfony\Component\Lock\Store\SemaphoreStore; @@ -276,14 +271,14 @@ CombinedStore ~~~~~~~~~~~~~ The CombinedStore synchronize several ``Stores`` together. When it's used to -acquired a Lock, it forward the call to the managed ``Stores``, and regarding the -result, uses a quorum to decide whether or not the lock is acquired. +acquired a Lock, it forwards the call to the managed ``Stores``, and regarding +the result, uses a quorum to decide whether or not the lock is acquired. .. note:: - This ``Store`` is usefull for High availability application. You can provide + This ``Store`` is useful for High availability application. You can provide several Redis Server, and use theses server to manage the Lock. A - MajorityQuorum is enougth to safely acquire a lock while it allow some Redis + MajorityQuorum is enough to safely acquire a lock while it allow some Redis server failure. .. code-block:: php @@ -293,7 +288,7 @@ result, uses a quorum to decide whether or not the lock is acquired. use Symfony\Component\Lock\Store\RedisStore; $stores = []; - foreach (['server1', 'server2', 'server3'] as $server) { + foreach (array('server1', 'server2', 'server3') as $server) { $redis= new \Redis(); $redis->connect($server); @@ -302,12 +297,10 @@ result, uses a quorum to decide whether or not the lock is acquired. $store = new CombinedStore($stores, new MajorityQuorum()); - .. tip:: You can use the CombinedStore with the UnanimousQuorum to implement chained ``Stores``. It'll allow you to acquire easy local locks before asking for a remote lock - .. _Packagist: https://packagist.org/packages/symfony/lock From 2498ccd4b911752f89d153fd2da78960086ab22e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Mon, 16 Jan 2017 20:49:09 +0100 Subject: [PATCH 4/9] Fix typo --- components/lock.rst | 112 +++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 54 deletions(-) diff --git a/components/lock.rst b/components/lock.rst index 45d36192a4e..ead85d067f9 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -3,7 +3,7 @@ single: Components; Lock The Lock Component -==================== +================== The Lock Component provides a mechanism to guarantee an exclusive access into a critical section. The component ships with ready to use stores for @@ -26,31 +26,29 @@ Usage ----- In order to centralize state of locks, you first need to create a ``Store``. -Then, you can ask to this store to create a Lock for your ``resource``. +Then, you can use the :class:`Symfony\\Component\\Lock\\Factory` to create a +Lock for your ``resource``. The :method:`Symfony\\Component\\Lock\\LockInterface::acquire` method tries to -acquire the lock. If the lock can not be acquired, the method throws a -:class:`Symfony\\Component\\Lock\\Exception\\LockConflictedException`. You can -safely call the ``acquire()`` method several times, even if you already acquired -it. +acquire the lock. If the lock can not be acquired, the method returns ``false``. +You can safely call the ``acquire()`` method several times, even if you already +acquired it. .. code-block:: php + use Symfony\Component\Lock\Factory; use Symfony\Component\Lock\Store\SemaphoreStore; - use Symfony\Component\Lock\Exception\LockConflictedException; $store = new SemaphoreStore(); - $lock = $store->createLock('invoice-pdf-generation'); + $factory = new Factory($store); + $lock = $factory->createLock('invoice-pdf-generation'); - try { - $lock->acquire(); + if ($lock->acquire()) { // the resource "invoice-pdf-generation" is locked. // You can compute and generate invoice safely here. $lock->release(); - } catch (LockConflictedException $e) { - // the resource "invoice-pdf-generation" is already locked by another process } The first argument of ``createLock`` is a string representation of the @@ -62,9 +60,9 @@ The first argument of ``createLock`` is a string representation of the distinguishes locks instances, even when they are created from the same ``resource``. If you want to share a lock in several services. You have to share the - instance of Lock returned by the ``Store::createLock`` method. + instance of Lock returned by the ``Factory::createLock`` method. -Blocking locks +Blocking Locks -------------- You can pass an optional blocking argument as the first argument to the @@ -77,70 +75,76 @@ you can decorate it with the ``RetryTillSaveStore``. .. code-block:: php + use Symfony\Component\Lock\Factory; use Symfony\Component\Lock\Store\RedisStore; use Symfony\Component\Lock\Store\RetryTillSaveStore; $store = new RedisStore(new \Predis\Client('tcp://localhost:6379')); $store = new RetryTillSaveStore($store); - $lock = $store->createLock('notification-flush'); + $factory = new Factory($store); + + $lock = $factory->createLock('notification-flush'); $lock->acquire(true); -Expirable Locks ---------------- +Expiring Locks +-------------- -Working with a remote ``Store`` is hard. There is now way for the remote -``Store`` to know if the locker process is till alive. +Working with a remote ``Store`` is hard. There is no way for the remote +``Store`` to know if the locker process is still alive. Due to bugs, fatal errors or segmentation fault, we can't guarantee that the ``release()`` method will be called, which would cause a ``resource`` to be locked infinitely. -To fill this gap, the remote ``Stores`` provide an expirable mechanism: The lock -is acquired for a defined amount of time (named TTL for Time To Live). -When the timeout occurred, the lock is automatically released even if the locker +To fill this gap, the remote ``Store`` provide an expiration mechanism: The +lock is acquired for a defined amount of time (named TTL for Time To Live). +When the timeout occurs, the lock is automatically released even if the locker don't call the ``release()`` method. -That's why, when you create a lock on an expirable ``Store``. You have to choose -carefully the correct TTL. When too low, you take the risk to "lose" the lock +That's why, when you create a lock on an expiring ``Store``, you have to choose +carefully the correct TTL. When too low, you take the risk to "loose" the lock (and someone else acquire it) whereas you don't finish your task. When too high and the process crash before you call the ``release()`` method, the ``resource`` will stay lock till the timeout. - .. code-block:: php + use Symfony\Component\Lock\Factory; use Symfony\Component\Lock\Store\RedisStore; $store = new RedisStore(new \Predis\Client('tcp://localhost:6379')); - $lock = $store->createLock('charts-generation', 30); + $factory = new Factory($store); + $lock = $factory->createLock('charts-generation', 30); $lock->acquire(); try { // perfom a job during less than 30 seconds } finally { - $lock->release() + $lock->release(); } .. tip:: To avoid letting the Lock in a locking state, try to always release an - expirable lock by wrapping the job in a try/catch block for instance. + expiring lock by wrapping the job in a try/catch block for instance. When you have to work on a really long task, you should not set the TTL to -overlap the duration of this task. Instead, the Lock Component expose a +overlap the duration of this task. Instead, the Lock Component exposes a :method:`Symfony\\Component\\Lock\\LockInterface::refresh` method in order to put off the TTL of the Lock. Thereby you can choose a small initial TTL, and -regularly refresh the lock +regularly refresh the lock. .. code-block:: php + use Symfony\Component\Lock\Factory; use Symfony\Component\Lock\Store\RedisStore; $store = new RedisStore(new \Predis\Client('tcp://localhost:6379')); - $lock = $store->createLock('charts-generation', 30); + $factory = new Factory($store); + $lock = $factory->createLock('charts-generation', 30); $lock->acquire(); try { @@ -151,7 +155,7 @@ regularly refresh the lock // resource is locked for 30 more seconds. } } finally { - $lock->release() + $lock->release(); } Available Stores @@ -162,22 +166,22 @@ This component provides several adapters ready to use in your applications. Here is a small summary of available ``Stores`` and their capabilities. -+----------------------------------------------+--------+----------+-----------+ -| Store | Scope | Blocking | Expirable | -+==============================================+========+==========+===========+ -| :ref:`FlockStore ` | local | yes | no | -+----------------------------------------------+--------+----------+-----------+ -| :ref:`MemcachedStore ` | remote | no | yes | -+----------------------------------------------+--------+----------+-----------+ -| :ref:`RedisStore ` | remote | no | yes | -+----------------------------------------------+--------+----------+-----------+ -| :ref:`SemaphoreStore ` | local | yes | no | -+----------------------------------------------+--------+----------+-----------+ ++----------------------------------------------+--------+----------+----------+ +| Store | Scope | Blocking | Expiring | ++==============================================+========+==========+==========+ +| :ref:`FlockStore ` | local | yes | no | ++----------------------------------------------+--------+----------+----------+ +| :ref:`MemcachedStore ` | remote | no | yes | ++----------------------------------------------+--------+----------+----------+ +| :ref:`RedisStore ` | remote | no | yes | ++----------------------------------------------+--------+----------+----------+ +| :ref:`SemaphoreStore ` | local | yes | no | ++----------------------------------------------+--------+----------+----------+ .. tip:: Calling the :method:`Symfony\\Component\\Lock\\LockInterface::refresh` - method on a Lock created from a non expirable ``Store`` like + method on a Lock created from a non expiring ``Store`` like :ref:`FlockStore ` will do nothing. .. _lock-store-flock: @@ -185,8 +189,8 @@ Here is a small summary of available ``Stores`` and their capabilities. FlockStore ~~~~~~~~~~ -The FlockStore use the fileSystem on the local computer to lock and store the -``resource``. It does not supports expiration, but the lock is automatically +The FlockStore uses the fileSystem on the local computer to lock and store the +``resource``. It does not support expiration, but the lock is automatically released when the PHP process is terminated. .. code-block:: php @@ -200,7 +204,7 @@ file will be created. .. caution:: - Beware, some filesystem (like some version of NFS) does not support locking. + Beware, some filesystems (like some version of NFS) do not support locking. We suggest to use local file, or to use a Store dedicated to remote usage like Redis or Memcached. @@ -212,18 +216,18 @@ MemcachedStore ~~~~~~~~~~~~~~ The MemcachedStore stores state of ``resource`` in a Memcached server. This -``Store`` does not support blocking, and expect a TLL to avoid infinity locks. +``Store`` does not support blocking, and expects a TLL to avoid infinity locks. .. note:: - Memcached does not supports TTL lower than 1 seconds. + Memcached does not support TTL lower than 1 second. It requires to have installed Memcached and have created a connection that -implements the ``\Memcached`` classes:: +implements the ``\Memcached`` class. .. code-block:: php - use Symfony\Component\Lock\Store\RedisStore; + use Symfony\Component\Lock\Store\MemcachedStore; $memcached = new \Memcached(); $memcached->addServer('localhost', 11211); @@ -241,7 +245,7 @@ locks. It requires to have installed Redis and have created a connection that implements the ``\Redis``, ``\RedisArray``, ``\RedisCluster`` or ``\Predis`` -classes:: +classes .. code-block:: php @@ -257,13 +261,13 @@ classes:: SemaphoreStore ~~~~~~~~~~~~~~ -The SemaphoreStore uses the PHP semaphore function to lock a ``resources``. +The SemaphoreStore uses the PHP semaphore functions to lock a ``resources``. .. code-block:: php use Symfony\Component\Lock\Store\SemaphoreStore; - $store = new SemaphoreStore($redis); + $store = new SemaphoreStore(); .. _lock-store-combined: From 6b6d8652732693db50f9cf68263c453d8ea1ff03 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 17 Feb 2017 09:29:46 +0100 Subject: [PATCH 5/9] Final review and some minor rewords/simplifications --- components/lock.rst | 233 +++++++++++++++++--------------------------- 1 file changed, 91 insertions(+), 142 deletions(-) diff --git a/components/lock.rst b/components/lock.rst index ead85d067f9..622669ccd8c 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -5,9 +5,8 @@ The Lock Component ================== - The Lock Component provides a mechanism to guarantee an exclusive access - into a critical section. The component ships with ready to use stores for - the most common backends. + The Lock Component creates and manages `locks`_, a mechanism to provide + exclusive access to a shared resource. .. versionadded:: 3.3 The Lock component was introduced in Symfony 3.3. @@ -25,55 +24,55 @@ You can install the component in 2 different ways: Usage ----- -In order to centralize state of locks, you first need to create a ``Store``. -Then, you can use the :class:`Symfony\\Component\\Lock\\Factory` to create a -Lock for your ``resource``. +Locks are used to guarantee exclusive access to some shared resource. In +Symfony applications, you can use locks for example to ensure that a command is +not executed more than once at the same time (on the same or different servers). -The :method:`Symfony\\Component\\Lock\\LockInterface::acquire` method tries to -acquire the lock. If the lock can not be acquired, the method returns ``false``. -You can safely call the ``acquire()`` method several times, even if you already -acquired it. - -.. code-block:: php +In order to manage the state of locks, you first need to create a ``Store`` +and then use the :class:`Symfony\\Component\\Lock\\Factory` class to actually +create the lock for some resource:: use Symfony\Component\Lock\Factory; use Symfony\Component\Lock\Store\SemaphoreStore; $store = new SemaphoreStore(); $factory = new Factory($store); - $lock = $factory->createLock('invoice-pdf-generation'); - if ($lock->acquire()) { - // the resource "invoice-pdf-generation" is locked. +Then, call to the :method:`Symfony\\Component\\Lock\\LockInterface::acquire` +method to try to acquire the lock. Its first argument is an arbitrary string +that represents the locked resource:: + + // ... + $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(); } -The first argument of ``createLock`` is a string representation of the -``resource`` to lock. +If the lock can not be acquired, the method returns ``false``. You can safely +call the ``acquire()`` method repeatedly, even if you already acquired it. .. note:: - In opposition to some other implementations, the Lock Component - distinguishes locks instances, even when they are created from the same - ``resource``. - If you want to share a lock in several services. You have to share the - instance of Lock returned by the ``Factory::createLock`` method. + Unlike other implementations, the Lock Component distinguishes locks + instances even when they are created for the same resource. If you want to + share a lock in several services, share the ``Lock`` instance returned by + the ``Factory::createLock`` method. Blocking Locks -------------- -You can pass an optional blocking argument as the first argument to the -:method:`Symfony\\Component\\Lock\\LockInterface::acquire` method, which -defaults to ``false``. If this is set to ``true``, your PHP code will wait -infinitely until the lock is released by another process. - -Some ``Store`` (but not all) natively supports this feature. When they don't, -you can decorate it with the ``RetryTillSaveStore``. +By default, when a lock cannot be acquired, the ``acquire`` method returns +``false`` immediately. In case you want to wait (indefinitely) until the lock +can be created, pass ``false`` as the argument of the ``acquire()`` method. This +is called a **blocking lock** because the execution of your application stops +until the lock is acquired. -.. code-block:: php +Some of the built-in ``Store`` classes support this feature. When they don't, +you can decorate them with the ``RetryTillSaveStore`` class:: use Symfony\Component\Lock\Factory; use Symfony\Component\Lock\Store\RedisStore; @@ -81,69 +80,53 @@ you can decorate it with the ``RetryTillSaveStore``. $store = new RedisStore(new \Predis\Client('tcp://localhost:6379')); $store = new RetryTillSaveStore($store); - $factory = new Factory($store); $lock = $factory->createLock('notification-flush'); - $lock->acquire(true); Expiring Locks -------------- -Working with a remote ``Store`` is hard. There is no way for the remote -``Store`` to know if the locker process is still alive. -Due to bugs, fatal errors or segmentation fault, we can't guarantee that the -``release()`` method will be called, which would cause a ``resource`` to be locked -infinitely. +Locks created remotely are difficult to manage because there is no way for the +remote ``Store`` to know if the locker process is still alive. Due to bugs, +fatal errors or segmentation faults, we can't guarantee that the ``release()`` +method will be called, which would cause the resource to be locked infinitely. -To fill this gap, the remote ``Store`` provide an expiration mechanism: The -lock is acquired for a defined amount of time (named TTL for Time To Live). -When the timeout occurs, the lock is automatically released even if the locker -don't call the ``release()`` method. +The best solution in those cases is to create **expiring locks**, which are +released automatically after some amount of time has passed (called TTL for +*Time To Live*). This time, in seconds, is configured as the second argument of +the ``createLock()`` method. If needed, these locks can also be released early +with the ``release()`` method. -That's why, when you create a lock on an expiring ``Store``, you have to choose -carefully the correct TTL. When too low, you take the risk to "loose" the lock -(and someone else acquire it) whereas you don't finish your task. -When too high and the process crash before you call the ``release()`` method, -the ``resource`` will stay lock till the timeout. - -.. code-block:: php - - use Symfony\Component\Lock\Factory; - use Symfony\Component\Lock\Store\RedisStore; +The trickiest part when working with expiring locks is choosing the right TTL. +If it's too short, other processes could acquire the lock before finishing your +work; it it's too long and the process crashes before calling the ``release()`` +method, the resource will stay locked until the timeout:: - $store = new RedisStore(new \Predis\Client('tcp://localhost:6379')); - - $factory = new Factory($store); + // ... + // create a expiring lock that lasts 30 seconds $lock = $factory->createLock('charts-generation', 30); $lock->acquire(); try { - // perfom a job during less than 30 seconds + // perform a job during less than 30 seconds } finally { $lock->release(); } .. tip:: - To avoid letting the Lock in a locking state, try to always release an - expiring lock by wrapping the job in a try/catch block for instance. + To avoid letting the lock in a locking state, it's recommended to wrap the + job in a try/catch/finally block to always try to release the expiring lock. -When you have to work on a really long task, you should not set the TTL to -overlap the duration of this task. Instead, the Lock Component exposes a -:method:`Symfony\\Component\\Lock\\LockInterface::refresh` method in order to -put off the TTL of the Lock. Thereby you can choose a small initial TTL, and -regularly refresh the lock. +In case of long-running tasks, it's better to start with a not too long TTL and +then use the :method:`Symfony\\Component\\Lock\\LockInterface::refresh` method +to reset the TTL to its original value:: .. code-block:: php - use Symfony\Component\Lock\Factory; - use Symfony\Component\Lock\Store\RedisStore; - - $store = new RedisStore(new \Predis\Client('tcp://localhost:6379')); - - $factory = new Factory($store); + // ... $lock = $factory->createLock('charts-generation', 30); $lock->acquire(); @@ -151,8 +134,8 @@ regularly refresh the lock. while (!$finished) { // perform a small part of the job. + // renew the lock for 30 more seconds. $lock->refresh(); - // resource is locked for 30 more seconds. } } finally { $lock->release(); @@ -161,71 +144,48 @@ regularly refresh the lock. Available Stores ---------------- -``Stores`` are classes that implement :class:`Symfony\\Component\\Lock\\StoreInterface`. -This component provides several adapters ready to use in your applications. - -Here is a small summary of available ``Stores`` and their capabilities. +Locks are created and managed in ``Stores``, which are classes that implement +:class:`Symfony\\Component\\Lock\\StoreInterface`. The component includes the +following built-in store types: -+----------------------------------------------+--------+----------+----------+ -| Store | Scope | Blocking | Expiring | -+==============================================+========+==========+==========+ -| :ref:`FlockStore ` | local | yes | no | -+----------------------------------------------+--------+----------+----------+ -| :ref:`MemcachedStore ` | remote | no | yes | -+----------------------------------------------+--------+----------+----------+ -| :ref:`RedisStore ` | remote | no | yes | -+----------------------------------------------+--------+----------+----------+ -| :ref:`SemaphoreStore ` | local | yes | no | -+----------------------------------------------+--------+----------+----------+ -.. tip:: - - Calling the :method:`Symfony\\Component\\Lock\\LockInterface::refresh` - method on a Lock created from a non expiring ``Store`` like - :ref:`FlockStore ` will do nothing. +============================================ ====== ======== ======== +Store Scope Blocking Expiring +============================================ ====== ======== ======== +:ref:`FlockStore ` local yes no +:ref:`MemcachedStore ` remote no yes +:ref:`RedisStore ` remote no yes +:ref:`SemaphoreStore ` local yes no +============================================ ====== ======== ======== .. _lock-store-flock: FlockStore ~~~~~~~~~~ -The FlockStore uses the fileSystem on the local computer to lock and store the -``resource``. It does not support expiration, but the lock is automatically -released when the PHP process is terminated. - -.. code-block:: php +The FlockStore uses the file system on the local computer to create the locks. +It does not support expiration, but the lock is automatically released when the +PHP process is terminated:: use Symfony\Component\Lock\Store\FlockStore; + // the argument is the path of the directory where the locks are created $store = new FlockStore(sys_get_temp_dir()); -The first argument of the constructor is the path to the directory where the -file will be created. - .. caution:: - Beware, some filesystems (like some version of NFS) do not support locking. - We suggest to use local file, or to use a Store dedicated to remote usage - like Redis or Memcached. - -.. _Packagist: https://packagist.org/packages/symfony/lock + Beware that some file systems (such as some types of NFS) do not support + locking. In those cases, it's better to use a local file or a remote store + based on Redis or Memcached. .. _lock-store-memcached: MemcachedStore ~~~~~~~~~~~~~~ -The MemcachedStore stores state of ``resource`` in a Memcached server. This -``Store`` does not support blocking, and expects a TLL to avoid infinity locks. - -.. note:: - - Memcached does not support TTL lower than 1 second. - -It requires to have installed Memcached and have created a connection that -implements the ``\Memcached`` class. - -.. code-block:: php +The MemcachedStore saves locks on a Memcached server, so first you must create +a Memcached connection implements the ``\Memcached`` class. This store does not +support blocking, and expects a TTL to avoid stalled locks:: use Symfony\Component\Lock\Store\MemcachedStore; @@ -234,20 +194,19 @@ implements the ``\Memcached`` class. $store = new MemcachedStore($memcached); +.. note:: + + Memcached does not support TTL lower than 1 second. + .. _lock-store-redis: RedisStore ~~~~~~~~~~ -The RedisStore uses an instance of Redis to store the state of the ``resource``. -This ``Store`` does not support blocking, and expect a TLL to avoid infinity -locks. - -It requires to have installed Redis and have created a connection that -implements the ``\Redis``, ``\RedisArray``, ``\RedisCluster`` or ``\Predis`` -classes - -.. code-block:: php +The RedisStore saves locks on a Redis server, so first you must create a Redis +connection implements the ``\Redis``, ``\RedisArray``, ``\RedisCluster`` or +``\Predis`` classes. This store does not support blocking, and expects a TTL to +avoid stalled locks:: use Symfony\Component\Lock\Store\RedisStore; @@ -261,9 +220,7 @@ classes SemaphoreStore ~~~~~~~~~~~~~~ -The SemaphoreStore uses the PHP semaphore functions to lock a ``resources``. - -.. code-block:: php +The SemaphoreStore uses the `PHP semaphore functions`_ to create the locks:: use Symfony\Component\Lock\Store\SemaphoreStore; @@ -274,18 +231,11 @@ The SemaphoreStore uses the PHP semaphore functions to lock a ``resources``. CombinedStore ~~~~~~~~~~~~~ -The CombinedStore synchronize several ``Stores`` together. When it's used to -acquired a Lock, it forwards the call to the managed ``Stores``, and regarding -the result, uses a quorum to decide whether or not the lock is acquired. - -.. note:: - - This ``Store`` is useful for High availability application. You can provide - several Redis Server, and use theses server to manage the Lock. A - MajorityQuorum is enough to safely acquire a lock while it allow some Redis - server failure. - -.. code-block:: php +The CombinedStore is designed for High Availability applications because it +manages several stores in sync (for example, several Redis servers). When a lock +is being acquired, it forwards the call to all the managed stores, and it +collects their responses. If a simple majority of stores have acquired the lock, +then the lock is considered as acquired; otherwise is not acquired:: use Symfony\Component\Lock\Quorum\MajorityQuorum; use Symfony\Component\Lock\Store\CombinedStore; @@ -301,10 +251,9 @@ the result, uses a quorum to decide whether or not the lock is acquired. $store = new CombinedStore($stores, new MajorityQuorum()); -.. tip:: - - You can use the CombinedStore with the UnanimousQuorum to implement chained - ``Stores``. It'll allow you to acquire easy local locks before asking for a - remote lock +Instead of the simple majority strategy (``MajorityQuorum``) you can use the +``UnanimousQuorum`` to require the lock to be acquired in all the stores. +.. _`locks`: https://en.wikipedia.org/wiki/Lock_(computer_science) .. _Packagist: https://packagist.org/packages/symfony/lock +.. _`PHP semaphore functions`: http://php.net/manual/en/book.sem.php From 17248e1e869fc87cdf4c3069c85f20ef398cc0df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Fri, 17 Feb 2017 10:36:20 +0100 Subject: [PATCH 6/9] Remove codeblock --- components/lock.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/lock.rst b/components/lock.rst index 622669ccd8c..22f523f03ea 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -124,8 +124,6 @@ In case of long-running tasks, it's better to start with a not too long TTL and then use the :method:`Symfony\\Component\\Lock\\LockInterface::refresh` method to reset the TTL to its original value:: -.. code-block:: php - // ... $lock = $factory->createLock('charts-generation', 30); From d6a218c8ace40b0f4d02d0d56ed5cfd4fc502bba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Fri, 17 Feb 2017 17:11:59 +0100 Subject: [PATCH 7/9] Rename quorum strategy --- components/lock.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/lock.rst b/components/lock.rst index 22f523f03ea..7bd8c2f57c0 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -235,7 +235,7 @@ is being acquired, it forwards the call to all the managed stores, and it collects their responses. If a simple majority of stores have acquired the lock, then the lock is considered as acquired; otherwise is not acquired:: - use Symfony\Component\Lock\Quorum\MajorityQuorum; + use Symfony\Component\Lock\Quorum\ConsensusStrategy; use Symfony\Component\Lock\Store\CombinedStore; use Symfony\Component\Lock\Store\RedisStore; @@ -247,10 +247,10 @@ then the lock is considered as acquired; otherwise is not acquired:: $stores[] = new RedisStore($redis); } - $store = new CombinedStore($stores, new MajorityQuorum()); + $store = new CombinedStore($stores, new ConsensusStrategy()); -Instead of the simple majority strategy (``MajorityQuorum``) you can use the -``UnanimousQuorum`` to require the lock to be acquired in all the stores. +Instead of the simple majority strategy (``ConsensusStrategy``) you can use the +``UnanimousStrategy`` to require the lock to be acquired in all the stores. .. _`locks`: https://en.wikipedia.org/wiki/Lock_(computer_science) .. _Packagist: https://packagist.org/packages/symfony/lock From d4326c0e0175246b74f44e14a089c6bcba4693f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Thu, 2 Mar 2017 08:39:35 +0100 Subject: [PATCH 8/9] Reduce usage of "you" --- components/lock.rst | 47 +++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/components/lock.rst b/components/lock.rst index 7bd8c2f57c0..804782b0d7e 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -38,8 +38,8 @@ create the lock for some resource:: $store = new SemaphoreStore(); $factory = new Factory($store); -Then, call to the :method:`Symfony\\Component\\Lock\\LockInterface::acquire` -method to try to acquire the lock. Its first argument is an arbitrary string +Then, a call to the :method:`Symfony\\Component\\Lock\\LockInterface::acquire` +method will try to acquire the lock. Its first argument is an arbitrary string that represents the locked resource:: // ... @@ -52,27 +52,27 @@ that represents the locked resource:: $lock->release(); } -If the lock can not be acquired, the method returns ``false``. You can safely -call the ``acquire()`` method repeatedly, even if you already acquired it. +If the lock can not be acquired, the method returns ``false``. The ``acquire()`` +method can be safely called repeatedly, even if the lock is already acquired. .. note:: Unlike other implementations, the Lock Component distinguishes locks - instances even when they are created for the same resource. If you want to - share a lock in several services, share the ``Lock`` instance returned by - the ``Factory::createLock`` method. + instances even when they are created for the same resource. If a lock has + to be used by several services, they should share the same ``Lock`` instance + returned by the ``Factory::createLock`` method. Blocking Locks -------------- By default, when a lock cannot be acquired, the ``acquire`` method returns -``false`` immediately. In case you want to wait (indefinitely) until the lock -can be created, pass ``false`` as the argument of the ``acquire()`` method. This +``false`` immediately. To wait (indefinitely) until the lock +can be created, pass ``true`` as the argument of the ``acquire()`` method. This is called a **blocking lock** because the execution of your application stops until the lock is acquired. Some of the built-in ``Store`` classes support this feature. When they don't, -you can decorate them with the ``RetryTillSaveStore`` class:: +they can be decorated with the ``RetryTillSaveStore`` class:: use Symfony\Component\Lock\Factory; use Symfony\Component\Lock\Store\RedisStore; @@ -90,7 +90,7 @@ Expiring Locks Locks created remotely are difficult to manage because there is no way for the remote ``Store`` to know if the locker process is still alive. Due to bugs, -fatal errors or segmentation faults, we can't guarantee that the ``release()`` +fatal errors or segmentation faults, it cannot be guaranteed that ``release()`` method will be called, which would cause the resource to be locked infinitely. The best solution in those cases is to create **expiring locks**, which are @@ -100,12 +100,12 @@ the ``createLock()`` method. If needed, these locks can also be released early with the ``release()`` method. The trickiest part when working with expiring locks is choosing the right TTL. -If it's too short, other processes could acquire the lock before finishing your -work; it it's too long and the process crashes before calling the ``release()`` +If it's too short, other processes could acquire the lock before finishing the +job; it it's too long and the process crashes before calling the ``release()`` method, the resource will stay locked until the timeout:: // ... - // create a expiring lock that lasts 30 seconds + // create an expiring lock that lasts 30 seconds $lock = $factory->createLock('charts-generation', 30); $lock->acquire(); @@ -173,16 +173,16 @@ PHP process is terminated:: .. caution:: Beware that some file systems (such as some types of NFS) do not support - locking. In those cases, it's better to use a local file or a remote store - based on Redis or Memcached. + locking. In those cases, it's better to use a directory on a local disk + drive or a remote store based on Redis or Memcached. .. _lock-store-memcached: MemcachedStore ~~~~~~~~~~~~~~ -The MemcachedStore saves locks on a Memcached server, so first you must create -a Memcached connection implements the ``\Memcached`` class. This store does not +The MemcachedStore saves locks on a Memcached server, it requires a Memcached +connection implementing the ``\Memcached`` class. This store does not support blocking, and expects a TTL to avoid stalled locks:: use Symfony\Component\Lock\Store\MemcachedStore; @@ -201,8 +201,8 @@ support blocking, and expects a TTL to avoid stalled locks:: RedisStore ~~~~~~~~~~ -The RedisStore saves locks on a Redis server, so first you must create a Redis -connection implements the ``\Redis``, ``\RedisArray``, ``\RedisCluster`` or +The RedisStore saves locks on a Redis server, it requires a Redis connection +implementing the ``\Redis``, ``\RedisArray``, ``\RedisCluster`` or ``\Predis`` classes. This store does not support blocking, and expects a TTL to avoid stalled locks:: @@ -233,7 +233,7 @@ The CombinedStore is designed for High Availability applications because it manages several stores in sync (for example, several Redis servers). When a lock is being acquired, it forwards the call to all the managed stores, and it collects their responses. If a simple majority of stores have acquired the lock, -then the lock is considered as acquired; otherwise is not acquired:: +then the lock is considered as acquired; otherwise as not acquired:: use Symfony\Component\Lock\Quorum\ConsensusStrategy; use Symfony\Component\Lock\Store\CombinedStore; @@ -249,8 +249,9 @@ then the lock is considered as acquired; otherwise is not acquired:: $store = new CombinedStore($stores, new ConsensusStrategy()); -Instead of the simple majority strategy (``ConsensusStrategy``) you can use the -``UnanimousStrategy`` to require the lock to be acquired in all the stores. +Instead of the simple majority strategy (``ConsensusStrategy``) an +``UnanimousStrategy`` can be used to require the lock to be acquired in all +he stores. .. _`locks`: https://en.wikipedia.org/wiki/Lock_(computer_science) .. _Packagist: https://packagist.org/packages/symfony/lock From 3035fe993528a51f5f39e8e83d25fbff096bd2f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Wed, 22 Mar 2017 23:33:48 +0100 Subject: [PATCH 9/9] Rename quorum into strategy --- components/lock.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/lock.rst b/components/lock.rst index 804782b0d7e..cf9c174781a 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -28,7 +28,7 @@ Locks are used to guarantee exclusive access to some shared resource. In Symfony applications, you can use locks for example to ensure that a command is not executed more than once at the same time (on the same or different servers). -In order to manage the state of locks, you first need to create a ``Store`` +In order to manage the state of locks, a ``Store`` needs to be created first and then use the :class:`Symfony\\Component\\Lock\\Factory` class to actually create the lock for some resource:: @@ -235,7 +235,7 @@ is being acquired, it forwards the call to all the managed stores, and it collects their responses. If a simple majority of stores have acquired the lock, then the lock is considered as acquired; otherwise as not acquired:: - use Symfony\Component\Lock\Quorum\ConsensusStrategy; + use Symfony\Component\Lock\Strategy\ConsensusStrategy; use Symfony\Component\Lock\Store\CombinedStore; use Symfony\Component\Lock\Store\RedisStore; @@ -251,7 +251,7 @@ then the lock is considered as acquired; otherwise as not acquired:: Instead of the simple majority strategy (``ConsensusStrategy``) an ``UnanimousStrategy`` can be used to require the lock to be acquired in all -he stores. +the stores. .. _`locks`: https://en.wikipedia.org/wiki/Lock_(computer_science) .. _Packagist: https://packagist.org/packages/symfony/lock