Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

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 9 commits into from
Apr 24, 2017
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix typo
  • Loading branch information
jderusse committed Feb 17, 2017
commit 2498ccd4b911752f89d153fd2da78960086ab22e
112 changes: 58 additions & 54 deletions 112 components/lock.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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.

Copy link
Member

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

Blocking locks
Blocking Locks
--------------

You can pass an optional blocking argument as the first argument to the
Expand All @@ -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.

Copy link
Member

Choose a reason for hiding this comment

The 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 {
Expand All @@ -151,7 +155,7 @@ regularly refresh the lock
// resource is locked for 30 more seconds.
}
} finally {
$lock->release()
$lock->release();
}

Available Stores
Expand All @@ -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
Expand All @@ -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.

Expand All @@ -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);
Expand All @@ -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

Expand All @@ -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:

Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.