@@ -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,46 @@ 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+
227+ The PdoStore saves locks in an SQL database, it requires a `PDO `_,
228+ `Doctrine DBAL Connection `_, or `Data Source Name (DSN) `_. This store does not
229+ support blocking, and expects a TTL to avoid stalled locks::
230+
231+ use Symfony\Component\Lock\Store\PdoStore;
232+
233+
234+ // a PDO, a Doctrine DBAL connection or DSN for lazy connecting through PDO
235+ $databaseConnectionOrDSN = 'mysql:host=127.0.0.1;dbname=lock';
236+ $store = new PdoStore($databaseConnectionOrDSN, ['db_username' => 'myuser', 'db_password' => 'mypassword']);
237+
238+ .. note ::
239+
240+ This store does not support TTL lower than 1 second.
241+
242+ Before storing locks in the database, you must create the table that stores
243+ the information. The store provides a method called
244+ :method: `Symfony\\ Component\\ Lock\\ Store\\ PdoStore::createTable `
245+ to set up this table for you according to the database engine used::
246+
247+ try {
248+ $store->createTable();
249+ } catch (\PDOException $exception) {
250+ // the table could not be created for some reason
251+ }
252+
253+ A great way to set up the table on production is to call the method on a dev
254+ enviroment, then generate a migration:
255+
256+ .. code-block :: terminal
257+
258+ $ php bin/console doctrine:migrations:diff
259+ $ php bin/console doctrine:migrations:migrate
260+
220261 .. _lock-store-redis :
221262
222263RedisStore
@@ -290,11 +331,11 @@ the component is used in the following way.
290331Remote Stores
291332~~~~~~~~~~~~~
292333
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,
334+ Remote stores (:ref: `MemcachedStore <lock-store-memcached >`,
335+ :ref: `PdoStore < lock-store-pdo >` and :ref: ` RedisStore <lock-store-redis >`) use
336+ an unique token to recognize the true owner of the lock. This token is stored
337+ in the :class: `Symfony\\ Component\\ Lock\\ Key ` object and is used internally by
338+ the ``Lock ``, therefore this key must not be shared between processes (session,
298339caching, fork, ...).
299340
300341.. caution ::
@@ -313,11 +354,11 @@ different machines may allow two different processes to acquire the same ``Lock`
313354Expiring Stores
314355~~~~~~~~~~~~~~~
315356
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.
357+ Expiring stores (:ref: `MemcachedStore <lock-store-memcached >`,
358+ :ref: `PdoStore <lock-store-pdo >` and :ref: ` RedisStore < lock-store-redis >`)
359+ guarantee that the lock is acquired only for the defined duration of time. If
360+ the task takes longer to be accomplished, then the lock can be released by the
361+ store and acquired by someone else.
321362
322363The ``Lock `` provides several methods to check its health. The ``isExpired() ``
323364method checks whether or not it lifetime is over and the ``getRemainingLifetime() ``
@@ -431,6 +472,30 @@ method uses the Memcached's ``flush()`` method which purges and removes everythi
431472 The method ``flush() `` must not be called, or locks should be stored in a
432473 dedicated Memcached service away from Cache.
433474
475+ PdoStore
476+ ~~~~~~~~~~
477+
478+ Pdo stores rely on the `ACID `_ properties of the SQL engine.
479+
480+ .. caution ::
481+
482+ In a cluster configured with multiple master, ensure writes are
483+ synchronously propaged to every nodes, or always use the same node.
484+
485+ .. caution ::
486+
487+ Some SQL engine like MySQL allows to disable unique constraint check.
488+ Ensure that this is not the case ``SET unique_checks=1; ``.
489+
490+ In order to purge old lock, this store use a current datetime to define a
491+ expiration date reference. This mechanism relies on all client and server nodes
492+ to have synchronized clock.
493+
494+ .. caution ::
495+
496+ To ensure locks don't expire prematurely; the ttl's should be set with
497+ enough extra time to account for any clock drift between nodes.
498+
434499RedisStore
435500~~~~~~~~~~
436501
@@ -501,6 +566,7 @@ instance, during the deployment of a new version. Processes with new
501566configuration must not be started while old processes with old configuration
502567are still running.
503568
569+ .. _`ACID` : https://en.wikipedia.org/wiki/ACID
504570.. _`locks` : https://en.wikipedia.org/wiki/Lock_(computer_science)
505571.. _Packagist : https://packagist.org/packages/symfony/lock
506572.. _`PHP semaphore functions` : http://php.net/manual/en/book.sem.php
0 commit comments