@@ -173,6 +173,7 @@ Store Scope Blocking Expiring
173173============================================ ====== ======== ========
174174:ref: `FlockStore <lock-store-flock >` local yes no
175175:ref: `MemcachedStore <lock-store-memcached >` remote no yes
176+ :ref: `PdoStore <lock-store-pdo >` remote no yes
176177:ref: `RedisStore <lock-store-redis >` remote no yes
177178:ref: `SemaphoreStore <lock-store-semaphore >` local yes no
178179============================================ ====== ======== ========
@@ -195,7 +196,7 @@ PHP process is terminated::
195196
196197 Beware that some file systems (such as some types of NFS) do not support
197198 locking. In those cases, it's better to use a directory on a local disk
198- drive or a remote store based on Redis or Memcached.
199+ drive or a remote store based on PDO, Redis or Memcached.
199200
200201.. _lock-store-memcached :
201202
@@ -217,6 +218,48 @@ support blocking, and expects a TTL to avoid stalled locks::
217218
218219 Memcached does not support TTL lower than 1 second.
219220
221+ .. _lock-store-pdo :
222+
223+ PdoStore
224+ ~~~~~~~~
225+
226+ .. versionadded :: 4.2
227+ The PdoStore was introduced Symfony 4.2.
228+
229+
230+ The PdoStore saves locks in an SQL database, it requires a `PDO `_,
231+ `Doctrine DBAL Connection `_, or `Data Source Name (DSN) `_. This store does not
232+ support blocking, and expects a TTL to avoid stalled locks::
233+
234+ use Symfony\Component\Lock\Store\PdoStore;
235+
236+ // a PDO, a Doctrine DBAL connection or DSN for lazy connecting through PDO
237+ $databaseConnectionOrDSN = 'mysql:host=127.0.0.1;dbname=lock';
238+ $store = new PdoStore($databaseConnectionOrDSN, ['db_username' => 'myuser', 'db_password' => 'mypassword']);
239+
240+ .. note ::
241+
242+ This store does not support TTL lower than 1 second.
243+
244+ Before storing locks in the database, you must create the table that stores
245+ the information. The store provides a method called
246+ :method: `Symfony\\ Component\\ Lock\\ Store\\ PdoStore::createTable `
247+ to set up this table for you according to the database engine used::
248+
249+ try {
250+ $store->createTable();
251+ } catch (\PDOException $exception) {
252+ // the table could not be created for some reason
253+ }
254+
255+ A great way to set up the table on production is to call the method on a dev
256+ enviroment, then generate a migration:
257+
258+ .. code-block :: terminal
259+
260+ $ php bin/console doctrine:migrations:diff
261+ $ php bin/console doctrine:migrations:migrate
262+
220263 .. _lock-store-redis :
221264
222265RedisStore
@@ -290,11 +333,11 @@ the component is used in the following way.
290333Remote Stores
291334~~~~~~~~~~~~~
292335
293- Remote stores (:ref: `MemcachedStore <lock-store-memcached >` and
294- :ref: `RedisStore <lock-store-redis >`) use an unique token to recognize the true
295- owner of the lock. This token is stored in the
296- :class: `Symfony\\ Component\\ Lock\\ Key ` object and is used internally by the
297- ``Lock ``, therefore this key must not be shared between processes (session,
336+ Remote stores (:ref: `MemcachedStore <lock-store-memcached >`,
337+ :ref: `PdoStore < lock-store-pdo >` and :ref: ` RedisStore <lock-store-redis >`) use
338+ a unique token to recognize the true owner of the lock. This token is stored
339+ in the :class: `Symfony\\ Component\\ Lock\\ Key ` object and is used internally by
340+ the ``Lock ``, therefore this key must not be shared between processes (session,
298341caching, fork, ...).
299342
300343.. caution ::
@@ -313,11 +356,11 @@ different machines may allow two different processes to acquire the same ``Lock`
313356Expiring Stores
314357~~~~~~~~~~~~~~~
315358
316- Expiring stores (:ref: `MemcachedStore <lock-store-memcached >` and
317- :ref: `RedisStore <lock-store-redis >`) guarantee that the lock is acquired
318- only for the defined duration of time. If the task takes longer to be
319- accomplished, then the lock can be released by the store and acquired by
320- someone else.
359+ Expiring stores (:ref: `MemcachedStore <lock-store-memcached >`,
360+ :ref: `PdoStore <lock-store-pdo >` and :ref: ` RedisStore < lock-store-redis >`)
361+ guarantee that the lock is acquired only for the defined duration of time. If
362+ the task takes longer to be accomplished, then the lock can be released by the
363+ store and acquired by someone else.
321364
322365The ``Lock `` provides several methods to check its health. The ``isExpired() ``
323366method checks whether or not it lifetime is over and the ``getRemainingLifetime() ``
@@ -431,6 +474,30 @@ method uses the Memcached's ``flush()`` method which purges and removes everythi
431474 The method ``flush() `` must not be called, or locks should be stored in a
432475 dedicated Memcached service away from Cache.
433476
477+ PdoStore
478+ ~~~~~~~~~~
479+
480+ The PdoStore relies on the `ACID `_ properties of the SQL engine.
481+
482+ .. caution ::
483+
484+ In a cluster configured with multiple primaries, ensure writes are
485+ synchronously propagated to every nodes, or always use the same node.
486+
487+ .. caution ::
488+
489+ Some SQL engines like MySQL allow to disable the unique constraint check.
490+ Ensure that this is not the case ``SET unique_checks=1; ``.
491+
492+ In order to purge old locks, this store uses a current datetime to define an
493+ expiration date reference. This mechanism relies on all server nodes to
494+ have synchronized clocks.
495+
496+ .. caution ::
497+
498+ To ensure locks don't expire prematurely; the TTLs should be set with
499+ enough extra time to account for any clock drift between nodes.
500+
434501RedisStore
435502~~~~~~~~~~
436503
@@ -501,6 +568,7 @@ instance, during the deployment of a new version. Processes with new
501568configuration must not be started while old processes with old configuration
502569are still running.
503570
571+ .. _`ACID` : https://en.wikipedia.org/wiki/ACID
504572.. _`locks` : https://en.wikipedia.org/wiki/Lock_(computer_science)
505573.. _Packagist : https://packagist.org/packages/symfony/lock
506574.. _`PHP semaphore functions` : http://php.net/manual/en/book.sem.php
0 commit comments