Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6b6d865

Browse files
Final review and some minor rewords/simplifications
1 parent 2498ccd commit 6b6d865

File tree

1 file changed

+91
-142
lines changed

1 file changed

+91
-142
lines changed

components/lock.rst

Lines changed: 91 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
The Lock Component
66
==================
77

8-
The Lock Component provides a mechanism to guarantee an exclusive access
9-
into a critical section. The component ships with ready to use stores for
10-
the most common backends.
8+
The Lock Component creates and manages `locks`_, a mechanism to provide
9+
exclusive access to a shared resource.
1110

1211
.. versionadded:: 3.3
1312
The Lock component was introduced in Symfony 3.3.
@@ -25,134 +24,118 @@ You can install the component in 2 different ways:
2524
Usage
2625
-----
2726

28-
In order to centralize state of locks, you first need to create a ``Store``.
29-
Then, you can use the :class:`Symfony\\Component\\Lock\\Factory` to create a
30-
Lock for your ``resource``.
27+
Locks are used to guarantee exclusive access to some shared resource. In
28+
Symfony applications, you can use locks for example to ensure that a command is
29+
not executed more than once at the same time (on the same or different servers).
3130

32-
The :method:`Symfony\\Component\\Lock\\LockInterface::acquire` method tries to
33-
acquire the lock. If the lock can not be acquired, the method returns ``false``.
34-
You can safely call the ``acquire()`` method several times, even if you already
35-
acquired it.
36-
37-
.. code-block:: php
31+
In order to manage the state of locks, you first need to create a ``Store``
32+
and then use the :class:`Symfony\\Component\\Lock\\Factory` class to actually
33+
create the lock for some resource::
3834

3935
use Symfony\Component\Lock\Factory;
4036
use Symfony\Component\Lock\Store\SemaphoreStore;
4137

4238
$store = new SemaphoreStore();
4339
$factory = new Factory($store);
44-
$lock = $factory->createLock('invoice-pdf-generation');
4540

46-
if ($lock->acquire()) {
47-
// the resource "invoice-pdf-generation" is locked.
41+
Then, call to the :method:`Symfony\\Component\\Lock\\LockInterface::acquire`
42+
method to try to acquire the lock. Its first argument is an arbitrary string
43+
that represents the locked resource::
44+
45+
// ...
46+
$lock = $factory->createLock('pdf-invoice-generation');
4847

48+
if ($lock->acquire()) {
49+
// The resource "pdf-invoice-generation" is locked.
4950
// You can compute and generate invoice safely here.
5051

5152
$lock->release();
5253
}
5354

54-
The first argument of ``createLock`` is a string representation of the
55-
``resource`` to lock.
55+
If the lock can not be acquired, the method returns ``false``. You can safely
56+
call the ``acquire()`` method repeatedly, even if you already acquired it.
5657

5758
.. note::
5859

59-
In opposition to some other implementations, the Lock Component
60-
distinguishes locks instances, even when they are created from the same
61-
``resource``.
62-
If you want to share a lock in several services. You have to share the
63-
instance of Lock returned by the ``Factory::createLock`` method.
60+
Unlike other implementations, the Lock Component distinguishes locks
61+
instances even when they are created for the same resource. If you want to
62+
share a lock in several services, share the ``Lock`` instance returned by
63+
the ``Factory::createLock`` method.
6464

6565
Blocking Locks
6666
--------------
6767

68-
You can pass an optional blocking argument as the first argument to the
69-
:method:`Symfony\\Component\\Lock\\LockInterface::acquire` method, which
70-
defaults to ``false``. If this is set to ``true``, your PHP code will wait
71-
infinitely until the lock is released by another process.
72-
73-
Some ``Store`` (but not all) natively supports this feature. When they don't,
74-
you can decorate it with the ``RetryTillSaveStore``.
68+
By default, when a lock cannot be acquired, the ``acquire`` method returns
69+
``false`` immediately. In case you want to wait (indefinitely) until the lock
70+
can be created, pass ``false`` as the argument of the ``acquire()`` method. This
71+
is called a **blocking lock** because the execution of your application stops
72+
until the lock is acquired.
7573

76-
.. code-block:: php
74+
Some of the built-in ``Store`` classes support this feature. When they don't,
75+
you can decorate them with the ``RetryTillSaveStore`` class::
7776

7877
use Symfony\Component\Lock\Factory;
7978
use Symfony\Component\Lock\Store\RedisStore;
8079
use Symfony\Component\Lock\Store\RetryTillSaveStore;
8180

8281
$store = new RedisStore(new \Predis\Client('tcp://localhost:6379'));
8382
$store = new RetryTillSaveStore($store);
84-
8583
$factory = new Factory($store);
8684

8785
$lock = $factory->createLock('notification-flush');
88-
8986
$lock->acquire(true);
9087

9188
Expiring Locks
9289
--------------
9390

94-
Working with a remote ``Store`` is hard. There is no way for the remote
95-
``Store`` to know if the locker process is still alive.
96-
Due to bugs, fatal errors or segmentation fault, we can't guarantee that the
97-
``release()`` method will be called, which would cause a ``resource`` to be locked
98-
infinitely.
91+
Locks created remotely are difficult to manage because there is no way for the
92+
remote ``Store`` to know if the locker process is still alive. Due to bugs,
93+
fatal errors or segmentation faults, we can't guarantee that the ``release()``
94+
method will be called, which would cause the resource to be locked infinitely.
9995

100-
To fill this gap, the remote ``Store`` provide an expiration mechanism: The
101-
lock is acquired for a defined amount of time (named TTL for Time To Live).
102-
When the timeout occurs, the lock is automatically released even if the locker
103-
don't call the ``release()`` method.
96+
The best solution in those cases is to create **expiring locks**, which are
97+
released automatically after some amount of time has passed (called TTL for
98+
*Time To Live*). This time, in seconds, is configured as the second argument of
99+
the ``createLock()`` method. If needed, these locks can also be released early
100+
with the ``release()`` method.
104101

105-
That's why, when you create a lock on an expiring ``Store``, you have to choose
106-
carefully the correct TTL. When too low, you take the risk to "loose" the lock
107-
(and someone else acquire it) whereas you don't finish your task.
108-
When too high and the process crash before you call the ``release()`` method,
109-
the ``resource`` will stay lock till the timeout.
110-
111-
.. code-block:: php
112-
113-
use Symfony\Component\Lock\Factory;
114-
use Symfony\Component\Lock\Store\RedisStore;
102+
The trickiest part when working with expiring locks is choosing the right TTL.
103+
If it's too short, other processes could acquire the lock before finishing your
104+
work; it it's too long and the process crashes before calling the ``release()``
105+
method, the resource will stay locked until the timeout::
115106

116-
$store = new RedisStore(new \Predis\Client('tcp://localhost:6379'));
117-
118-
$factory = new Factory($store);
107+
// ...
108+
// create a expiring lock that lasts 30 seconds
119109
$lock = $factory->createLock('charts-generation', 30);
120110

121111
$lock->acquire();
122112
try {
123-
// perfom a job during less than 30 seconds
113+
// perform a job during less than 30 seconds
124114
} finally {
125115
$lock->release();
126116
}
127117

128118
.. tip::
129119

130-
To avoid letting the Lock in a locking state, try to always release an
131-
expiring lock by wrapping the job in a try/catch block for instance.
120+
To avoid letting the lock in a locking state, it's recommended to wrap the
121+
job in a try/catch/finally block to always try to release the expiring lock.
132122

133-
When you have to work on a really long task, you should not set the TTL to
134-
overlap the duration of this task. Instead, the Lock Component exposes a
135-
:method:`Symfony\\Component\\Lock\\LockInterface::refresh` method in order to
136-
put off the TTL of the Lock. Thereby you can choose a small initial TTL, and
137-
regularly refresh the lock.
123+
In case of long-running tasks, it's better to start with a not too long TTL and
124+
then use the :method:`Symfony\\Component\\Lock\\LockInterface::refresh` method
125+
to reset the TTL to its original value::
138126

139127
.. code-block:: php
140128
141-
use Symfony\Component\Lock\Factory;
142-
use Symfony\Component\Lock\Store\RedisStore;
143-
144-
$store = new RedisStore(new \Predis\Client('tcp://localhost:6379'));
145-
146-
$factory = new Factory($store);
129+
// ...
147130
$lock = $factory->createLock('charts-generation', 30);
148131
149132
$lock->acquire();
150133
try {
151134
while (!$finished) {
152135
// perform a small part of the job.
153136
137+
// renew the lock for 30 more seconds.
154138
$lock->refresh();
155-
// resource is locked for 30 more seconds.
156139
}
157140
} finally {
158141
$lock->release();
@@ -161,71 +144,48 @@ regularly refresh the lock.
161144
Available Stores
162145
----------------
163146

164-
``Stores`` are classes that implement :class:`Symfony\\Component\\Lock\\StoreInterface`.
165-
This component provides several adapters ready to use in your applications.
166-
167-
Here is a small summary of available ``Stores`` and their capabilities.
147+
Locks are created and managed in ``Stores``, which are classes that implement
148+
:class:`Symfony\\Component\\Lock\\StoreInterface`. The component includes the
149+
following built-in store types:
168150

169-
+----------------------------------------------+--------+----------+----------+
170-
| Store | Scope | Blocking | Expiring |
171-
+==============================================+========+==========+==========+
172-
| :ref:`FlockStore <lock-store-flock>` | local | yes | no |
173-
+----------------------------------------------+--------+----------+----------+
174-
| :ref:`MemcachedStore <lock-store-memcached>` | remote | no | yes |
175-
+----------------------------------------------+--------+----------+----------+
176-
| :ref:`RedisStore <lock-store-redis>` | remote | no | yes |
177-
+----------------------------------------------+--------+----------+----------+
178-
| :ref:`SemaphoreStore <lock-store-semaphore>` | local | yes | no |
179-
+----------------------------------------------+--------+----------+----------+
180151

181-
.. tip::
182-
183-
Calling the :method:`Symfony\\Component\\Lock\\LockInterface::refresh`
184-
method on a Lock created from a non expiring ``Store`` like
185-
:ref:`FlockStore <lock-store-flock>` will do nothing.
152+
============================================ ====== ======== ========
153+
Store Scope Blocking Expiring
154+
============================================ ====== ======== ========
155+
:ref:`FlockStore <lock-store-flock>` local yes no
156+
:ref:`MemcachedStore <lock-store-memcached>` remote no yes
157+
:ref:`RedisStore <lock-store-redis>` remote no yes
158+
:ref:`SemaphoreStore <lock-store-semaphore>` local yes no
159+
============================================ ====== ======== ========
186160

187161
.. _lock-store-flock:
188162

189163
FlockStore
190164
~~~~~~~~~~
191165

192-
The FlockStore uses the fileSystem on the local computer to lock and store the
193-
``resource``. It does not support expiration, but the lock is automatically
194-
released when the PHP process is terminated.
195-
196-
.. code-block:: php
166+
The FlockStore uses the file system on the local computer to create the locks.
167+
It does not support expiration, but the lock is automatically released when the
168+
PHP process is terminated::
197169

198170
use Symfony\Component\Lock\Store\FlockStore;
199171

172+
// the argument is the path of the directory where the locks are created
200173
$store = new FlockStore(sys_get_temp_dir());
201174

202-
The first argument of the constructor is the path to the directory where the
203-
file will be created.
204-
205175
.. caution::
206176

207-
Beware, some filesystems (like some version of NFS) do not support locking.
208-
We suggest to use local file, or to use a Store dedicated to remote usage
209-
like Redis or Memcached.
210-
211-
.. _Packagist: https://packagist.org/packages/symfony/lock
177+
Beware that some file systems (such as some types of NFS) do not support
178+
locking. In those cases, it's better to use a local file or a remote store
179+
based on Redis or Memcached.
212180

213181
.. _lock-store-memcached:
214182

215183
MemcachedStore
216184
~~~~~~~~~~~~~~
217185

218-
The MemcachedStore stores state of ``resource`` in a Memcached server. This
219-
``Store`` does not support blocking, and expects a TLL to avoid infinity locks.
220-
221-
.. note::
222-
223-
Memcached does not support TTL lower than 1 second.
224-
225-
It requires to have installed Memcached and have created a connection that
226-
implements the ``\Memcached`` class.
227-
228-
.. code-block:: php
186+
The MemcachedStore saves locks on a Memcached server, so first you must create
187+
a Memcached connection implements the ``\Memcached`` class. This store does not
188+
support blocking, and expects a TTL to avoid stalled locks::
229189

230190
use Symfony\Component\Lock\Store\MemcachedStore;
231191

@@ -234,20 +194,19 @@ implements the ``\Memcached`` class.
234194

235195
$store = new MemcachedStore($memcached);
236196

197+
.. note::
198+
199+
Memcached does not support TTL lower than 1 second.
200+
237201
.. _lock-store-redis:
238202

239203
RedisStore
240204
~~~~~~~~~~
241205

242-
The RedisStore uses an instance of Redis to store the state of the ``resource``.
243-
This ``Store`` does not support blocking, and expect a TLL to avoid infinity
244-
locks.
245-
246-
It requires to have installed Redis and have created a connection that
247-
implements the ``\Redis``, ``\RedisArray``, ``\RedisCluster`` or ``\Predis``
248-
classes
249-
250-
.. code-block:: php
206+
The RedisStore saves locks on a Redis server, so first you must create a Redis
207+
connection implements the ``\Redis``, ``\RedisArray``, ``\RedisCluster`` or
208+
``\Predis`` classes. This store does not support blocking, and expects a TTL to
209+
avoid stalled locks::
251210

252211
use Symfony\Component\Lock\Store\RedisStore;
253212

@@ -261,9 +220,7 @@ classes
261220
SemaphoreStore
262221
~~~~~~~~~~~~~~
263222

264-
The SemaphoreStore uses the PHP semaphore functions to lock a ``resources``.
265-
266-
.. code-block:: php
223+
The SemaphoreStore uses the `PHP semaphore functions`_ to create the locks::
267224

268225
use Symfony\Component\Lock\Store\SemaphoreStore;
269226

@@ -274,18 +231,11 @@ The SemaphoreStore uses the PHP semaphore functions to lock a ``resources``.
274231
CombinedStore
275232
~~~~~~~~~~~~~
276233

277-
The CombinedStore synchronize several ``Stores`` together. When it's used to
278-
acquired a Lock, it forwards the call to the managed ``Stores``, and regarding
279-
the result, uses a quorum to decide whether or not the lock is acquired.
280-
281-
.. note::
282-
283-
This ``Store`` is useful for High availability application. You can provide
284-
several Redis Server, and use theses server to manage the Lock. A
285-
MajorityQuorum is enough to safely acquire a lock while it allow some Redis
286-
server failure.
287-
288-
.. code-block:: php
234+
The CombinedStore is designed for High Availability applications because it
235+
manages several stores in sync (for example, several Redis servers). When a lock
236+
is being acquired, it forwards the call to all the managed stores, and it
237+
collects their responses. If a simple majority of stores have acquired the lock,
238+
then the lock is considered as acquired; otherwise is not acquired::
289239

290240
use Symfony\Component\Lock\Quorum\MajorityQuorum;
291241
use Symfony\Component\Lock\Store\CombinedStore;
@@ -301,10 +251,9 @@ the result, uses a quorum to decide whether or not the lock is acquired.
301251

302252
$store = new CombinedStore($stores, new MajorityQuorum());
303253

304-
.. tip::
305-
306-
You can use the CombinedStore with the UnanimousQuorum to implement chained
307-
``Stores``. It'll allow you to acquire easy local locks before asking for a
308-
remote lock
254+
Instead of the simple majority strategy (``MajorityQuorum``) you can use the
255+
``UnanimousQuorum`` to require the lock to be acquired in all the stores.
309256

257+
.. _`locks`: https://en.wikipedia.org/wiki/Lock_(computer_science)
310258
.. _Packagist: https://packagist.org/packages/symfony/lock
259+
.. _`PHP semaphore functions`: http://php.net/manual/en/book.sem.php

0 commit comments

Comments
 (0)