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 34bec23

Browse filesBrowse files
jderussewouterj
authored andcommitted
Add a documentation page for lock in FW
1 parent a97bbc8 commit 34bec23
Copy full SHA for 34bec23

File tree

4 files changed

+303
-54
lines changed
Filter options

4 files changed

+303
-54
lines changed

‎components/lock.rst

Copy file name to clipboardExpand all lines: components/lock.rst
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ The Lock Component
88
The Lock Component creates and manages `locks`_, a mechanism to provide
99
exclusive access to a shared resource.
1010

11+
If you're using the Symfony Framework, read the
12+
:doc:`Symfony Framework Lock documentation </lock>`.
13+
1114
Installation
1215
------------
1316

‎index.rst

Copy file name to clipboardExpand all lines: index.rst
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Topics
4343
frontend
4444
http_cache
4545
http_client
46+
lock
4647
logging
4748
mailer
4849
mercure

‎lock.rst

Copy file name to clipboard
+292Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
.. index::
2+
single: Lock
3+
4+
Dealing with Concurrency with Locks
5+
===================================
6+
7+
When a program runs concurrently, some part of code which modify shared
8+
resources should not be accessed by multiple processes at the same time.
9+
Symfony's :doc:`Lock component </components/lock>` provides a locking mechanism to ensure
10+
that only one process is running the critical section of code at any point of
11+
time to prevent race condition from happening.
12+
13+
The following example shows a typical usage of the lock::
14+
15+
$lock = $lockFactory->createLock('pdf-invoice-generation');
16+
if (!$lock->acquire()) {
17+
return;
18+
}
19+
20+
// critical section of code
21+
$service->method();
22+
23+
$lock->release();
24+
25+
Installation
26+
------------
27+
28+
In applications using :ref:`Symfony Flex <symfony-flex>`, run this command to
29+
install the Lock component:
30+
31+
.. code-block:: terminal
32+
33+
$ composer require symfony/lock
34+
35+
Configuring Lock with FrameworkBundle
36+
-------------------------------------
37+
38+
By default, Symfony provides a :ref:`Semaphore <lock-store-semaphore>`
39+
when available, or a :ref:`Flock <lock-store-flock>` otherwise. You can configure
40+
this behavior by using the ``lock`` key like:
41+
42+
.. configuration-block::
43+
44+
.. code-block:: yaml
45+
46+
# config/packages/lock.yaml
47+
framework:
48+
lock: ~
49+
lock: 'flock'
50+
lock: 'flock:///path/to/file'
51+
lock: 'semaphore'
52+
lock: 'memcached://m1.docker'
53+
lock: ['memcached://m1.docker', 'memcached://m2.docker']
54+
lock: 'redis://r1.docker'
55+
lock: ['redis://r1.docker', 'redis://r2.docker']
56+
lock: 'zookeeper://z1.docker'
57+
lock: 'zookeeper://z1.docker,z2.docker'
58+
lock: 'sqlite:///%kernel.project_dir%/var/lock.db'
59+
lock: 'mysql:host=127.0.0.1;dbname=lock'
60+
lock: 'pgsql:host=127.0.0.1;dbname=lock'
61+
lock: 'sqlsrv:server=localhost;Database=test'
62+
lock: 'oci:host=localhost;dbname=test'
63+
lock: '%env(LOCK_DSN)%'
64+
65+
# named locks
66+
lock:
67+
invoice: ['semaphore', 'redis://r2.docker']
68+
report: 'semaphore'
69+
70+
.. code-block:: xml
71+
72+
<!-- config/packages/lock.xml -->
73+
<?xml version="1.0" encoding="UTF-8" ?>
74+
<container xmlns="http://symfony.com/schema/dic/services"
75+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
76+
xmlns:framework="http://symfony.com/schema/dic/symfony"
77+
xsi:schemaLocation="http://symfony.com/schema/dic/services
78+
https://symfony.com/schema/dic/services/services-1.0.xsd
79+
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
80+
81+
<framework:config>
82+
<framework:lock>
83+
<framework:resource>flock</framework:resource>
84+
85+
<framework:resource>flock:///path/to/file</framework:resource>
86+
87+
<framework:resource>semaphore</framework:resource>
88+
89+
<framework:resource>memcached://m1.docker</framework:resource>
90+
91+
<framework:resource>memcached://m1.docker</framework:resource>
92+
<framework:resource>memcached://m2.docker</framework:resource>
93+
94+
<framework:resource>redis://r1.docker</framework:resource>
95+
96+
<framework:resource>redis://r1.docker</framework:resource>
97+
<framework:resource>redis://r2.docker</framework:resource>
98+
99+
<framework:resource>zookeeper://z1.docker</framework:resource>
100+
101+
<framework:resource>zookeeper://z1.docker,z2.docker</framework:resource>
102+
103+
<framework:resource>sqlite:///%kernel.project_dir%/var/lock.db</framework:resource>
104+
105+
<framework:resource>mysql:host=127.0.0.1;dbname=lock</framework:resource>
106+
107+
<framework:resource>pgsql:host=127.0.0.1;dbname=lock</framework:resource>
108+
109+
<framework:resource>sqlsrv:server=localhost;Database=test</framework:resource>
110+
111+
<framework:resource>oci:host=localhost;dbname=test</framework:resource>
112+
113+
<framework:resource>%env(LOCK_DSN)%</framework:resource>
114+
115+
<!-- named locks -->
116+
<framework:resource name="invoice">semaphore</framework:resource>
117+
<framework:resource name="invoice">redis://r2.docker</framework:resource>
118+
<framework:resource name="report">semaphore</framework:resource>
119+
</framework:lock>
120+
</framework:config>
121+
</container>
122+
123+
.. code-block:: php
124+
125+
// config/packages/lock.php
126+
$container->loadFromExtension('framework', [
127+
'lock' => null,
128+
'lock' => 'flock',
129+
'lock' => 'flock:///path/to/file',
130+
'lock' => 'semaphore',
131+
'lock' => 'memcached://m1.docker',
132+
'lock' => ['memcached://m1.docker', 'memcached://m2.docker'],
133+
'lock' => 'redis://r1.docker',
134+
'lock' => ['redis://r1.docker', 'redis://r2.docker'],
135+
'lock' => 'zookeeper://z1.docker',
136+
'lock' => 'zookeeper://z1.docker,z2.docker',
137+
'lock' => 'sqlite:///%kernel.project_dir%/var/lock.db',
138+
'lock' => 'mysql:host=127.0.0.1;dbname=lock',
139+
'lock' => 'pgsql:host=127.0.0.1;dbname=lock',
140+
'lock' => 'sqlsrv:server=localhost;Database=test',
141+
'lock' => 'oci:host=localhost;dbname=test',
142+
'lock' => '%env(LOCK_DSN)%',
143+
144+
// named locks
145+
'lock' => [
146+
'invoice' => ['semaphore', 'redis://r2.docker'],
147+
'report' => 'semaphore',
148+
],
149+
]);
150+
151+
Locking a Resource
152+
------------------
153+
154+
To lock the default resource, autowire the lock using
155+
:class:`Symfony\\Component\\Lock\\LockInterface` (service id ``lock``)::
156+
157+
// src/Controller/PdfController.php
158+
namespace App\Controller;
159+
160+
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
161+
use Symfony\Component\Lock\LockInterface;
162+
163+
class PdfController extends AbstractController
164+
{
165+
/**
166+
* @Route("/download/terms-of-use.pdf")
167+
*/
168+
public function downloadPdf(LockInterface $lock, MyPdfGeneratorService $pdf)
169+
{
170+
$lock->acquire(true);
171+
172+
// heavy computation
173+
$myPdf = $pdf->getOrCreatePdf();
174+
175+
$lock->release();
176+
177+
// ...
178+
}
179+
}
180+
181+
.. caution::
182+
183+
The same instance of ``LockInterface`` won't block when calling ``acquire``
184+
multiple times. Inside the same process, when several services share the
185+
same instance of ``LockInterface``, they won't lock each other. When the
186+
same process run concurent tasks, inject the ``LockFactory`` instead.
187+
188+
Locking a Dynamic Resource
189+
--------------------------
190+
191+
Sometimes the application is able to cut the resource into small pieces in order
192+
to lock a small subset of process and let other through. In our previous example
193+
with see how to lock the ``$pdf->getOrCreatePdf('terms-of-use')`` for everybody,
194+
now let's see how to lock ``$pdf->getOrCreatePdf($version)`` only for
195+
processes asking for the same ``$version``::
196+
197+
// src/Controller/PdfController.php
198+
namespace App\Controller;
199+
200+
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
201+
use Symfony\Component\Lock\LockInterface;
202+
203+
class PdfController extends AbstractController
204+
{
205+
/**
206+
* @Route("/download/{version}/terms-of-use.pdf")
207+
*/
208+
public function downloadPdf($version, LockFactory $lockFactory, MyPdfGeneratorService $pdf)
209+
{
210+
$lock = $lockFactory->createLock($version);
211+
$lock->acquire(true);
212+
213+
// heavy computation
214+
$myPdf = $pdf->getOrCreatePdf($version);
215+
216+
$lock->release();
217+
218+
// ...
219+
}
220+
}
221+
222+
Named Lock
223+
----------
224+
225+
If the application needs different kind of Stores alongside each other, Symfony
226+
provides :ref:`named lock <reference-lock-resources-name>`::
227+
228+
.. configuration-block::
229+
230+
.. code-block:: yaml
231+
232+
# config/packages/lock.yaml
233+
framework:
234+
lock:
235+
invoice: ['semaphore', 'redis://r2.docker']
236+
report: 'semaphore'
237+
238+
.. code-block:: xml
239+
240+
<!-- config/packages/lock.xml -->
241+
<?xml version="1.0" encoding="UTF-8" ?>
242+
<container xmlns="http://symfony.com/schema/dic/services"
243+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
244+
xmlns:framework="http://symfony.com/schema/dic/symfony"
245+
xsi:schemaLocation="http://symfony.com/schema/dic/services
246+
https://symfony.com/schema/dic/services/services-1.0.xsd
247+
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
248+
249+
<framework:config>
250+
<framework:lock>
251+
<framework:resource name="invoice">semaphore</framework:resource>
252+
<framework:resource name="invoice">redis://r2.docker</framework:resource>
253+
<framework:resource name="report">semaphore</framework:resource>
254+
</framework:lock>
255+
</framework:config>
256+
</container>
257+
258+
.. code-block:: php
259+
260+
// config/packages/lock.php
261+
$container->loadFromExtension('framework', [
262+
'lock' => [
263+
'invoice' => ['semaphore', 'redis://r2.docker'],
264+
'report' => 'semaphore',
265+
],
266+
]);
267+
268+
Each name becomes a service where the service id suffixed by the name of the
269+
lock (e.g. ``lock.invoice``). An autowiring alias is also created for each lock
270+
using the camel case version of its name suffixed by ``Lock`` - e.g. ``invoice``
271+
can be injected automatically by naming the argument ``$invoiceLock`` and
272+
type-hinting it with :class:`Symfony\\Component\\Lock\\LockInterface`.
273+
274+
Symfony also provide a corresponding factory and store following the same rules
275+
(e.g. ``invoice`` generates a ``lock.invoice.factory`` and
276+
``lock.invoice.store``, both can be injected automatically by naming
277+
respectively ``$invoiceLockFactory`` and ``$invoiceLockStore`` and type-hinted
278+
with :class:`Symfony\\Component\\Lock\\LockFactory` and
279+
:class:`Symfony\\Component\\Lock\\PersistingStoreInterface`)
280+
281+
Blocking Store
282+
--------------
283+
284+
If you want to use the ``RetryTillSaveStore`` for :ref:`non-blocking locks <lock-blocking-locks>`,
285+
you can do it by :doc:`decorating the store </service_container/service_decoration>` service:
286+
287+
.. code-block:: yaml
288+
289+
lock.default.retry_till_save.store:
290+
class: Symfony\Component\Lock\Store\RetryTillSaveStore
291+
decorates: lock.default.store
292+
arguments: ['@lock.default.retry_till_save.store.inner', 100, 50]

‎reference/configuration/framework.rst

Copy file name to clipboardExpand all lines: reference/configuration/framework.rst
+7-54Lines changed: 7 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2720,21 +2720,7 @@ A list of lock stores to be created by the framework extension.
27202720
27212721
# config/packages/lock.yaml
27222722
framework:
2723-
# these are all the supported lock stores
2724-
lock: ~
2725-
lock: 'flock'
2726-
lock: 'flock:///path/to/file'
2727-
lock: 'semaphore'
2728-
lock: 'memcached://m1.docker'
2729-
lock: ['memcached://m1.docker', 'memcached://m2.docker']
2730-
lock: 'redis://r1.docker'
2731-
lock: ['redis://r1.docker', 'redis://r2.docker']
2732-
lock: '%env(MEMCACHED_OR_REDIS_URL)%'
2733-
2734-
# named locks
2735-
lock:
2736-
invoice: ['redis://r1.docker', 'redis://r2.docker']
2737-
report: 'semaphore'
2723+
lock: '%env(LOCK_DSN)%'
27382724
27392725
.. code-block:: xml
27402726
@@ -2749,29 +2735,7 @@ A list of lock stores to be created by the framework extension.
27492735
27502736
<framework:config>
27512737
<framework:lock>
2752-
<!-- these are all the supported lock stores -->
2753-
<framework:resource>flock</framework:resource>
2754-
2755-
<framework:resource>flock:///path/to/file</framework:resource>
2756-
2757-
<framework:resource>semaphore</framework:resource>
2758-
2759-
<framework:resource>memcached://m1.docker</framework:resource>
2760-
2761-
<framework:resource>memcached://m1.docker</framework:resource>
2762-
<framework:resource>memcached://m2.docker</framework:resource>
2763-
2764-
<framework:resource>redis://r1.docker</framework:resource>
2765-
2766-
<framework:resource>redis://r1.docker</framework:resource>
2767-
<framework:resource>redis://r2.docker</framework:resource>
2768-
2769-
<framework:resource>%env(REDIS_URL)%</framework:resource>
2770-
2771-
<!-- named locks -->
2772-
<framework:resource name="invoice">redis://r1.docker</framework:resource>
2773-
<framework:resource name="invoice">redis://r2.docker</framework:resource>
2774-
<framework:resource name="report">semaphore</framework:resource>
2738+
<framework:resource>%env(LOCK_DSN)%</framework:resource>
27752739
</framework:lock>
27762740
</framework:config>
27772741
</container>
@@ -2780,24 +2744,13 @@ A list of lock stores to be created by the framework extension.
27802744
27812745
// config/packages/lock.php
27822746
$container->loadFromExtension('framework', [
2783-
// these are all the supported lock stores
2784-
'lock' => null,
2785-
'lock' => 'flock',
2786-
'lock' => 'flock:///path/to/file',
2787-
'lock' => 'semaphore',
2788-
'lock' => 'memcached://m1.docker',
2789-
'lock' => ['memcached://m1.docker', 'memcached://m2.docker'],
2790-
'lock' => 'redis://r1.docker',
2791-
'lock' => ['redis://r1.docker', 'redis://r2.docker'],
2792-
'lock' => '%env(MEMCACHED_OR_REDIS_URL)%',
2793-
2794-
// named locks
2795-
'lock' => [
2796-
'invoice' => ['redis://r1.docker', 'redis://r2.docker'],
2797-
'report' => 'semaphore',
2798-
],
2747+
'lock' => '%env(LOCK_DSN)%',
27992748
]);
28002749
2750+
.. seealso::
2751+
2752+
For more details, see :doc:`/lock`.
2753+
28012754
.. _reference-lock-resources-name:
28022755

28032756
name

0 commit comments

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