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

Commit 6469016

Browse filesBrowse files
committed
Add the Lock Component
1 parent f2a40f7 commit 6469016
Copy full SHA for 6469016

File tree

1 file changed

+319
-0
lines changed
Filter options

1 file changed

+319
-0
lines changed

‎components/lock.rst

Copy file name to clipboard
+319Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
.. index::
2+
single: Lock
3+
single: Components; Lock
4+
5+
The Lock Component
6+
====================
7+
8+
The Lock Component provides a mechanism to garentee an exclusive access into
9+
a critical section. The component ships with ready to use stores for the
10+
most common backends.
11+
12+
.. versionadded:: 3.3
13+
The Lock component was introduced in Symfony 3.3.
14+
15+
Installation
16+
------------
17+
18+
You can install the component in 2 different ways:
19+
20+
* :doc:`Install it via Composer </components/using_components>` (``symfony/lock`` on `Packagist`_);
21+
* Use the official Git repository (https://github.com/symfony/lock).
22+
23+
.. include:: /components/require_autoload.rst.inc
24+
25+
26+
Usage
27+
-----
28+
29+
In order to centralize state of locks, you first need to create a ``Store``.
30+
Then, you can ask to this store to create a Lock for your ``resource``.
31+
32+
The :method:`Symfony\\Component\\Lock\\LockInterface::acquire` method tries to
33+
acquire the lock. If the lock is can not be acquired, the method throws a
34+
:class:`Symfony\\Component\\Lock\\Exception\\LockConflictedException`. You can
35+
safly call the ``acquire()`` method several time, even if you already acquired
36+
it.
37+
38+
.. code-block:: php
39+
40+
use Symfony\Component\Lock\Store\SemaphoreStore;
41+
use Symfony\Component\Lock\Exception\LockConflictedException;
42+
43+
$store = new SemaphoreStore();
44+
$lock = $store->createLock('hello');
45+
46+
try {
47+
$lock->acquire();
48+
// the resource "hello" is locked. You can perform your task safely.
49+
50+
// do whatever you want.
51+
52+
$lock->release();
53+
} catch (LockConflictedException $e) {
54+
// the resource "hello" is already locked by another process
55+
}
56+
57+
The first argument of `createLock` is a string representation of the
58+
``resource`` to lock.
59+
60+
.. note::
61+
62+
In opposition to some other implementations, the Lock Component distinguish
63+
locks instances, even when they are created from the same ``resource``.
64+
If you want to share a lock in several services. You have to share the
65+
instance of Lock returned by the ``Store::createLock`` method.
66+
67+
68+
Blocking locks
69+
--------------
70+
71+
You can pass an optional blocking argument as the first argument to the
72+
:method:`Symfony\\Component\\Lock\\LockInterface::acquire` method, which
73+
defaults to ``false``. If this is set to ``true``, your PHP code will wait
74+
infinitely until the lock is released by another process.
75+
76+
Some ``Store`` (but not all) natively supports this features. When they don't,
77+
you can decorate it with the ``RetryTillSaveStore``.
78+
79+
.. code-block:: php
80+
81+
use Symfony\Component\Lock\Store\RedisStore;
82+
use Symfony\Component\Lock\Store\RetryTillSaveStore;
83+
84+
$store = new RedisStore(new \Predis\Client('tcp://localhost:6379'));
85+
$store = new RetryTillSaveStore($store);
86+
87+
$lock = $store->createLock('hello');
88+
89+
$lock->acquire(true);
90+
91+
92+
93+
Expirable Locks
94+
---------------
95+
96+
Working with a remote ``Store`` is hard. In oposition to local ``Stores``
97+
(like :ref:`FlockStore <lock-store-flock>` or :ref:`SemaphoreStore <lock-store-semaphore>`) there is now way for the remote
98+
``Store`` to know whether or not the locker process is till alive. Due tu bugs,
99+
fatal errors or segmentation fault, we can't garentee that the ``release()``
100+
function will be called, which would cause a ``resource`` to be locked
101+
infinitely.
102+
103+
To fill this gap, the remote ``Stores`` provide an expirable mechanism: The lock
104+
is acquired for a defined amount of time (named TTL for Time To Live).
105+
When the timeout occured, the lock is automatically released even if the locker
106+
don't call the ``release()`` method.
107+
108+
That's why, when you create a lock on an expirable ``Store``. You have to choose
109+
carrefully the correct TTL. When too low, you take the risk to "loose" the lock
110+
(and someone else acquire it) wheras you don't finish your task.
111+
When too hight and the process crash before you call the ``release()`` method,
112+
the ``resource`` will stay lock till the timeout.
113+
114+
115+
.. code-block:: php
116+
117+
use Symfony\Component\Lock\Store\RedisStore;
118+
119+
$store = new RedisStore(new \Predis\Client('tcp://localhost:6379'));
120+
121+
$lock = $store->createLock('hello', 30);
122+
123+
$lock->acquire();
124+
try {
125+
// perfom a job during less than 30 seconds
126+
} finally {
127+
$lock->release()
128+
}
129+
130+
.. tip::
131+
132+
To avoid to let the Lock in a locking state, try to always release an
133+
expirable lock by wraping the job in a try/catch block for instance.
134+
135+
136+
When you have to work on a really long task, you should not set the TTL to
137+
overlaps the duration of this task. Instead, the Lock Component expose a
138+
:method:`Symfony\\Component\\Lock\\LockInterface::refresh` method in order to
139+
put off the TTL of the Lock. Thereby you can choose a small initial TTL, and
140+
regulary refresh the lock
141+
142+
.. code-block:: php
143+
144+
use Symfony\Component\Lock\Store\RedisStore;
145+
146+
$store = new RedisStore(new \Predis\Client('tcp://localhost:6379'));
147+
148+
$lock = $store->createLock('hello', 30);
149+
150+
$lock->acquire();
151+
try {
152+
while (!$finished) {
153+
// perfom a small part of the job.
154+
155+
$lock->refresh();
156+
// resource is locked for 30 more seconds.
157+
}
158+
} finally {
159+
$lock->release()
160+
}
161+
162+
163+
Available Stores
164+
----------------
165+
166+
``Stores`` are classes that implement :class:`Symfony\\Component\\Lock\\StoreInterface`.
167+
This component provides several adapters ready to use in your applications.
168+
169+
Here is a small summary of availanble ``Stores`` and theire capabilities.
170+
171+
+----------------------------------------------+--------+----------+-----------+
172+
| Store | Scope | Blocking | Expirable |
173+
+==============================================+========+==========+===========+
174+
| :ref:`FlockStore <lock-store-flock>` | local | yes | no |
175+
+----------------------------------------------+--------+----------+-----------+
176+
| :ref:`MemcachedStore <lock-store-memcached>` | remote | no | yes |
177+
+----------------------------------------------+--------+----------+-----------+
178+
| :ref:`RedisStore <lock-store-redis>` | remote | no | yes |
179+
+----------------------------------------------+--------+----------+-----------+
180+
| :ref:`SemaphoreStore <lock-store-semaphore>` | local | yes | no |
181+
+----------------------------------------------+--------+----------+-----------+
182+
183+
.. tip::
184+
185+
Calling the :method:`Symfony\\Component\\Lock\\LockInterface::refresh`
186+
method on a Lock created from a non expirable ``Store`` like
187+
:ref:`FlockStore <lock-store-flock>` will do nothing.
188+
189+
.. _lock-store-flock:
190+
191+
FlockStore
192+
~~~~~~~~~~
193+
194+
The FlockStore use the fileSystem on the local computer to lock and store the
195+
``resource``.
196+
It does not supports expiration, but the lock is automaticaly released when the
197+
PHP process is terminated.
198+
199+
200+
.. code-block:: php
201+
202+
use Symfony\Component\Lock\Store\FlockStore;
203+
204+
$store = new FlockStore(sys_get_temp_dir());
205+
206+
The first argument of the constructor is the path to the directory where the
207+
file will be created.
208+
209+
.. caution::
210+
211+
Beware, some filesystem (like some version of NFS) does not support locking.
212+
We suggest to use local file, or to use a Store dedicated to remote usage
213+
like Redis or Memcached.
214+
215+
216+
.. _Packagist: https://packagist.org/packages/symfony/lock
217+
218+
.. _lock-store-memcached:
219+
220+
MemcachedStore
221+
~~~~~~~~~~~~~~
222+
223+
The MemcachedStore stores state of ``resource`` in a Memcached server. This
224+
``Store`` does not support blocking, and expect a TLL to avoid infinity locks.
225+
226+
.. note::
227+
228+
Memcached does not supports TTL lower than 1 seconds.
229+
230+
231+
It requires to have installed Memcached and have created a connection that
232+
implements the ``\Memcached`` classes::
233+
234+
.. code-block:: php
235+
236+
use Symfony\Component\Lock\Store\RedisStore;
237+
238+
$memcached = new \Memcached();
239+
$memcached->addServer('localhost', 11211);
240+
241+
$store = new MemcachedStore($memcached);
242+
243+
.. _lock-store-redis:
244+
245+
RedisStore
246+
~~~~~~~~~~
247+
248+
The RedisStore uses an instance of Redis to store the state of the ``resource``.
249+
This ``Store`` does not support blocking, and expect a TLL to avoid infinity
250+
locks.
251+
252+
It requires to have installed Redis and have created a connection that
253+
implements the ``\Redis``, ``\RedisArray``, ``\RedisCluster`` or ``\Predis``
254+
classes::
255+
256+
.. code-block:: php
257+
258+
use Symfony\Component\Lock\Store\RedisStore;
259+
260+
$redis = new \Redis();
261+
$redis->connect('localhost');
262+
263+
$store = new RedisStore($redis);
264+
265+
.. _lock-store-semaphore:
266+
267+
SemaphoreStore
268+
~~~~~~~~~~~~~~
269+
270+
The SemaphoreStore uses the PHP semaphore function to lock a ``resources``.
271+
272+
273+
.. code-block:: php
274+
275+
use Symfony\Component\Lock\Store\SemaphoreStore;
276+
277+
$store = new SemaphoreStore($redis);
278+
279+
.. _lock-store-combined:
280+
281+
CombinedStore
282+
~~~~~~~~~~~~~
283+
284+
The CombinedStore synchronize several ``Stores`` together. When it's used to
285+
acquired a Lock, it forward the call to the managed ``Stores``, and regarding the
286+
result, uses a quorum to decide whether or not the lock is acquired.
287+
288+
.. note::
289+
290+
This ``Store`` is usefull for High availability application. You can provide
291+
several Redis Server, and use theses server to manage the Lock. A
292+
MajorityQuorum is enougth to safly acquire a lock while it allow some Redis
293+
server failure.
294+
295+
.. code-block:: php
296+
297+
use Symfony\Component\Lock\Quorum\MajorityQuorum;
298+
use Symfony\Component\Lock\Store\CombinedStore;
299+
use Symfony\Component\Lock\Store\RedisStore;
300+
301+
$stores = [];
302+
foreach (['server1', 'server2', 'server3'] as $server) {
303+
$redis= new \Redis();
304+
$redis->connect($server);
305+
306+
$stores[] = new RedisStore($redis);
307+
}
308+
309+
$store = new CombinedStore($stores, new MajorityQuorum());
310+
311+
312+
.. tip::
313+
314+
You can use the CombinedStore with the UnanimousQuorum to implement chained
315+
``Stores``. It'll allow you to acquire easy local locks before asking for a
316+
remote lock
317+
318+
319+
.. _Packagist: https://packagist.org/packages/symfony/lock

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.