-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Add docs for the Lock component #7364
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
6469016
Add the Lock Component
jderusse 60bfe03
Fix typo
jderusse d1d3b71
Fixed minor typos and syntax issues
javiereguiluz 2498ccd
Fix typo
jderusse 6b6d865
Final review and some minor rewords/simplifications
javiereguiluz 17248e1
Remove codeblock
jderusse d6a218c
Rename quorum strategy
jderusse d4326c0
Reduce usage of "you"
jderusse 3035fe9
Rename quorum into strategy
jderusse File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Fix typo
- Loading branch information
commit 2498ccd4b911752f89d153fd2da78960086ab22e
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extra blank line here |
||
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,31 +166,31 @@ 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 <lock-store-flock>` | local | yes | no | | ||
+----------------------------------------------+--------+----------+-----------+ | ||
| :ref:`MemcachedStore <lock-store-memcached>` | remote | no | yes | | ||
+----------------------------------------------+--------+----------+-----------+ | ||
| :ref:`RedisStore <lock-store-redis>` | remote | no | yes | | ||
+----------------------------------------------+--------+----------+-----------+ | ||
| :ref:`SemaphoreStore <lock-store-semaphore>` | local | yes | no | | ||
+----------------------------------------------+--------+----------+-----------+ | ||
+----------------------------------------------+--------+----------+----------+ | ||
| Store | Scope | Blocking | Expiring | | ||
+==============================================+========+==========+==========+ | ||
| :ref:`FlockStore <lock-store-flock>` | local | yes | no | | ||
+----------------------------------------------+--------+----------+----------+ | ||
| :ref:`MemcachedStore <lock-store-memcached>` | remote | no | yes | | ||
+----------------------------------------------+--------+----------+----------+ | ||
| :ref:`RedisStore <lock-store-redis>` | remote | no | yes | | ||
+----------------------------------------------+--------+----------+----------+ | ||
| :ref:`SemaphoreStore <lock-store-semaphore>` | 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 <lock-store-flock>` 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 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: | ||
|
||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra blank line here should be removed