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 5544b5c

Browse filesBrowse files
committed
minor #7364 Add docs for the Lock component (jderusse, javiereguiluz)
This PR was merged into the master branch. Discussion ---------- Add docs for the Lock component See symfony/symfony#21093 Commits ------- 3035fe9 Rename quorum into strategy d4326c0 Reduce usage of "you" d6a218c Rename quorum strategy 17248e1 Remove codeblock 6b6d865 Final review and some minor rewords/simplifications 2498ccd Fix typo d1d3b71 Fixed minor typos and syntax issues 60bfe03 Fix typo 6469016 Add the Lock Component
2 parents 7be8dec + 3035fe9 commit 5544b5c
Copy full SHA for 5544b5c

File tree

1 file changed

+258
-0
lines changed
Filter options

1 file changed

+258
-0
lines changed

‎components/lock.rst

Copy file name to clipboard
+258Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
.. index::
2+
single: Lock
3+
single: Components; Lock
4+
5+
The Lock Component
6+
==================
7+
8+
The Lock Component creates and manages `locks`_, a mechanism to provide
9+
exclusive access to a shared resource.
10+
11+
.. versionadded:: 3.3
12+
The Lock component was introduced in Symfony 3.3.
13+
14+
Installation
15+
------------
16+
17+
You can install the component in 2 different ways:
18+
19+
* :doc:`Install it via Composer </components/using_components>` (``symfony/lock`` on `Packagist`_);
20+
* Use the official Git repository (https://github.com/symfony/lock).
21+
22+
.. include:: /components/require_autoload.rst.inc
23+
24+
Usage
25+
-----
26+
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).
30+
31+
In order to manage the state of locks, a ``Store`` needs to be created first
32+
and then use the :class:`Symfony\\Component\\Lock\\Factory` class to actually
33+
create the lock for some resource::
34+
35+
use Symfony\Component\Lock\Factory;
36+
use Symfony\Component\Lock\Store\SemaphoreStore;
37+
38+
$store = new SemaphoreStore();
39+
$factory = new Factory($store);
40+
41+
Then, a call to the :method:`Symfony\\Component\\Lock\\LockInterface::acquire`
42+
method will 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');
47+
48+
if ($lock->acquire()) {
49+
// The resource "pdf-invoice-generation" is locked.
50+
// You can compute and generate invoice safely here.
51+
52+
$lock->release();
53+
}
54+
55+
If the lock can not be acquired, the method returns ``false``. The ``acquire()``
56+
method can be safely called repeatedly, even if the lock is already acquired.
57+
58+
.. note::
59+
60+
Unlike other implementations, the Lock Component distinguishes locks
61+
instances even when they are created for the same resource. If a lock has
62+
to be used by several services, they should share the same ``Lock`` instance
63+
returned by the ``Factory::createLock`` method.
64+
65+
Blocking Locks
66+
--------------
67+
68+
By default, when a lock cannot be acquired, the ``acquire`` method returns
69+
``false`` immediately. To wait (indefinitely) until the lock
70+
can be created, pass ``true`` 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.
73+
74+
Some of the built-in ``Store`` classes support this feature. When they don't,
75+
they can be decorated with the ``RetryTillSaveStore`` class::
76+
77+
use Symfony\Component\Lock\Factory;
78+
use Symfony\Component\Lock\Store\RedisStore;
79+
use Symfony\Component\Lock\Store\RetryTillSaveStore;
80+
81+
$store = new RedisStore(new \Predis\Client('tcp://localhost:6379'));
82+
$store = new RetryTillSaveStore($store);
83+
$factory = new Factory($store);
84+
85+
$lock = $factory->createLock('notification-flush');
86+
$lock->acquire(true);
87+
88+
Expiring Locks
89+
--------------
90+
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, it cannot be guaranteed that ``release()``
94+
method will be called, which would cause the resource to be locked infinitely.
95+
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.
101+
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 the
104+
job; it it's too long and the process crashes before calling the ``release()``
105+
method, the resource will stay locked until the timeout::
106+
107+
// ...
108+
// create an expiring lock that lasts 30 seconds
109+
$lock = $factory->createLock('charts-generation', 30);
110+
111+
$lock->acquire();
112+
try {
113+
// perform a job during less than 30 seconds
114+
} finally {
115+
$lock->release();
116+
}
117+
118+
.. tip::
119+
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.
122+
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::
126+
127+
// ...
128+
$lock = $factory->createLock('charts-generation', 30);
129+
130+
$lock->acquire();
131+
try {
132+
while (!$finished) {
133+
// perform a small part of the job.
134+
135+
// renew the lock for 30 more seconds.
136+
$lock->refresh();
137+
}
138+
} finally {
139+
$lock->release();
140+
}
141+
142+
Available Stores
143+
----------------
144+
145+
Locks are created and managed in ``Stores``, which are classes that implement
146+
:class:`Symfony\\Component\\Lock\\StoreInterface`. The component includes the
147+
following built-in store types:
148+
149+
150+
============================================ ====== ======== ========
151+
Store Scope Blocking Expiring
152+
============================================ ====== ======== ========
153+
:ref:`FlockStore <lock-store-flock>` local yes no
154+
:ref:`MemcachedStore <lock-store-memcached>` remote no yes
155+
:ref:`RedisStore <lock-store-redis>` remote no yes
156+
:ref:`SemaphoreStore <lock-store-semaphore>` local yes no
157+
============================================ ====== ======== ========
158+
159+
.. _lock-store-flock:
160+
161+
FlockStore
162+
~~~~~~~~~~
163+
164+
The FlockStore uses the file system on the local computer to create the locks.
165+
It does not support expiration, but the lock is automatically released when the
166+
PHP process is terminated::
167+
168+
use Symfony\Component\Lock\Store\FlockStore;
169+
170+
// the argument is the path of the directory where the locks are created
171+
$store = new FlockStore(sys_get_temp_dir());
172+
173+
.. caution::
174+
175+
Beware that some file systems (such as some types of NFS) do not support
176+
locking. In those cases, it's better to use a directory on a local disk
177+
drive or a remote store based on Redis or Memcached.
178+
179+
.. _lock-store-memcached:
180+
181+
MemcachedStore
182+
~~~~~~~~~~~~~~
183+
184+
The MemcachedStore saves locks on a Memcached server, it requires a Memcached
185+
connection implementing the ``\Memcached`` class. This store does not
186+
support blocking, and expects a TTL to avoid stalled locks::
187+
188+
use Symfony\Component\Lock\Store\MemcachedStore;
189+
190+
$memcached = new \Memcached();
191+
$memcached->addServer('localhost', 11211);
192+
193+
$store = new MemcachedStore($memcached);
194+
195+
.. note::
196+
197+
Memcached does not support TTL lower than 1 second.
198+
199+
.. _lock-store-redis:
200+
201+
RedisStore
202+
~~~~~~~~~~
203+
204+
The RedisStore saves locks on a Redis server, it requires a Redis connection
205+
implementing the ``\Redis``, ``\RedisArray``, ``\RedisCluster`` or
206+
``\Predis`` classes. This store does not support blocking, and expects a TTL to
207+
avoid stalled locks::
208+
209+
use Symfony\Component\Lock\Store\RedisStore;
210+
211+
$redis = new \Redis();
212+
$redis->connect('localhost');
213+
214+
$store = new RedisStore($redis);
215+
216+
.. _lock-store-semaphore:
217+
218+
SemaphoreStore
219+
~~~~~~~~~~~~~~
220+
221+
The SemaphoreStore uses the `PHP semaphore functions`_ to create the locks::
222+
223+
use Symfony\Component\Lock\Store\SemaphoreStore;
224+
225+
$store = new SemaphoreStore();
226+
227+
.. _lock-store-combined:
228+
229+
CombinedStore
230+
~~~~~~~~~~~~~
231+
232+
The CombinedStore is designed for High Availability applications because it
233+
manages several stores in sync (for example, several Redis servers). When a lock
234+
is being acquired, it forwards the call to all the managed stores, and it
235+
collects their responses. If a simple majority of stores have acquired the lock,
236+
then the lock is considered as acquired; otherwise as not acquired::
237+
238+
use Symfony\Component\Lock\Strategy\ConsensusStrategy;
239+
use Symfony\Component\Lock\Store\CombinedStore;
240+
use Symfony\Component\Lock\Store\RedisStore;
241+
242+
$stores = [];
243+
foreach (array('server1', 'server2', 'server3') as $server) {
244+
$redis= new \Redis();
245+
$redis->connect($server);
246+
247+
$stores[] = new RedisStore($redis);
248+
}
249+
250+
$store = new CombinedStore($stores, new ConsensusStrategy());
251+
252+
Instead of the simple majority strategy (``ConsensusStrategy``) an
253+
``UnanimousStrategy`` can be used to require the lock to be acquired in all
254+
the stores.
255+
256+
.. _`locks`: https://en.wikipedia.org/wiki/Lock_(computer_science)
257+
.. _Packagist: https://packagist.org/packages/symfony/lock
258+
.. _`PHP semaphore functions`: http://php.net/manual/en/book.sem.php

0 commit comments

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