The Lock Component

The Lock Component creates and manages locks, a mechanism to provide exclusive access to a shared resource.

ロック コンポーネントは、共有リソースへの排他的アクセスを提供するメカニズムであるロックを作成および管理します。

If you're using the Symfony Framework, read the Symfony Framework Lock documentation.

Symfony Framework を使用している場合は、Symfony Framework Lock のドキュメントをお読みください。

Installation

1
$ composer require symfony/lock

Note

ノート

If you install this component outside of a Symfony application, you must require the vendor/autoload.php file in your code to enable the class autoloading mechanism provided by Composer. Read this article for more details.

このコンポーネントを Symfony アプリケーションの外部にインストールする場合は、Composer が提供するクラス自動ロード メカニズムを有効にするために、コード内に vendor/autoload.php ファイルを必要とする必要があります。詳細については、この記事をお読みください。

Usage

Locks are used to guarantee exclusive access to some shared resource. In Symfony applications, you can use locks for example to ensure that a command is not executed more than once at the same time (on the same or different servers).

ロックは、一部の共有リソースへの排他的アクセスを保証するために使用されます。 Symfony アプリケーションでは、たとえば、コマンドが (同じまたは異なるサーバーで) 同時に複数回実行されないようにするためにロックを使用できます。

Locks are created using a LockFactory class, which in turn requires another class to manage the storage of locks:

ロックは LockFactory クラスを使用して作成されます。これには、ロックのストレージを管理する別のクラスが必要です。
1
2
3
4
5
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\SemaphoreStore;

$store = new SemaphoreStore();
$factory = new LockFactory($store);

The lock is created by calling the createLock() method. Its first argument is an arbitrary string that represents the locked resource. Then, a call to the acquire() method will try to acquire the lock:

ロックは createLock() メソッドを呼び出すことによって作成されます。最初の引数は、ロックされたリソースを表す任意の文字列です。次に、 acquire() メソッドを呼び出してロックを取得しようとします。
1
2
3
4
5
6
7
8
9
// ...
$lock = $factory->createLock('pdf-creation');

if ($lock->acquire()) {
    // The resource "pdf-creation" is locked.
    // You can compute and generate the invoice safely here.

    $lock->release();
}

If the lock can not be acquired, the method returns false. The acquire() method can be safely called repeatedly, even if the lock is already acquired.

ロックを取得できない場合、メソッドは false を返します。 acquire() メソッドは、ロックが既に取得されている場合でも、安全に繰り返し呼び出すことができます。

Note

ノート

Unlike other implementations, the Lock Component distinguishes lock instances even when they are created for the same resource. It means that for a given scope and resource one lock instance can be acquired multiple times. If a lock has to be used by several services, they should share the same Lock instance returned by the LockFactory::createLock method.

他の実装とは異なり、ロック コンポーネントは、同じリソースに対して作成された場合でもロック インスタンスを区別します。これは、特定のスコープとリソースに対して、1 つのロック インスタンスを複数回取得できることを意味します。複数のサービスでロックを使用する必要がある場合は、LockFactory::createLock メソッドによって返される同じ Lockinstance を共有する必要があります。

Tip

ヒント

If you don't release the lock explicitly, it will be released automatically upon instance destruction. In some cases, it can be useful to lock a resource across several requests. To disable the automatic release behavior, set the third argument of the createLock() method to false.

ロックを明示的に解放しない場合、インスタンスの破棄時に自動的に解放されます。場合によっては、複数のリクエストにわたってリソースをロックすると便利な場合があります。自動解放動作を無効にするには、createLock() メソッドの 3 番目の引数を false に設定します。

Serializing Locks

The Key contains the state of the Lock and can be serialized. This allows the user to begin a long job in a process by acquiring the lock, and continue the job in another process using the same lock:

Key には theLock の状態が含まれており、シリアル化できます。これにより、ユーザーはロックを取得してプロセス内の長いジョブを開始し、同じロックを使用して別のプロセスでジョブを続行できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
use Symfony\Component\Lock\Key;
use Symfony\Component\Lock\Lock;

$key = new Key('article.'.$article->getId());
$lock = new Lock(
    $key,
    $this->store,
    300,  // ttl
    false // autoRelease
);
$lock->acquire(true);

$this->bus->dispatch(new RefreshTaxonomy($article, $key));

Note

ノート

Don't forget to set the autoRelease argument to false in the Lock constructor to avoid releasing the lock when the destructor is called.

デストラクタが呼び出されたときにロックが解放されないように、Lock コンストラクタで autoRelease 引数を false に設定することを忘れないでください。

Not all stores are compatible with serialization and cross-process locking: for example, the kernel will automatically release semaphores acquired by the SemaphoreStore store. If you use an incompatible store (see lock stores for supported stores), an exception will be thrown when the application tries to serialize the key.

すべてのストアがシリアル化およびクロスプロセス ロックと互換性があるわけではありません。たとえば、カーネルは、SemaphoreStore ストアによって取得されたセマフォを自動的に解放します。互換性のないストア (サポートされているストアについてはロック ストアを参照) を使用すると、アプリケーションがキーをシリアル化しようとすると例外がスローされます。

Blocking Locks

By default, when a lock cannot be acquired, the acquire method returns false immediately. To wait (indefinitely) until the lock can be created, pass true as the argument of the acquire() method. This is called a blocking lock because the execution of your application stops until the lock is acquired:

デフォルトでは、ロックを取得できない場合、acquire メソッドはすぐに false を返します。ロックが作成されるまで (無期限に) 待機するには、acquire() メソッドの引数として true を渡します。これは、ロックが取得されるまでアプリケーションの実行が停止するため、ブロッキング ロックと呼ばれます。
1
2
3
4
5
6
7
8
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\RedisStore;

$store = new RedisStore(new \Predis\Client('tcp://localhost:6379'));
$factory = new LockFactory($store);

$lock = $factory->createLock('pdf-creation');
$lock->acquire(true);

When the store does not support blocking locks by implementing the BlockingStoreInterface interface (see lock stores for supported stores), the Lock class will retry to acquire the lock in a non-blocking way until the lock is acquired.

ストアが BlockingStoreInterface インターフェースを実装することによってブロッキング ロックをサポートしていない場合 (サポートされているストアについてはロック ストアを参照)、Lock クラスは、ロックが取得されるまで非ブロッキングの方法でロックの取得を再試行します。

Expiring Locks

Locks created remotely are difficult to manage because there is no way for the remote Store to know if the locker process is still alive. Due to bugs, fatal errors or segmentation faults, it cannot be guaranteed that the release() method will be called, which would cause the resource to be locked infinitely.

リモートで作成されたロックは、ロッカー プロセスがまだ有効かどうかをリモートの Store が知る方法がないため、管理が困難です。バグ、致命的なエラー、またはセグメンテーション違反のため、release() メソッドが呼び出されることを保証できません。これにより、リソースが無限にロックされます。

The best solution in those cases is to create expiring locks, which are released automatically after some amount of time has passed (called TTL for Time To Live). This time, in seconds, is configured as the second argument of the createLock() method. If needed, these locks can also be released early with the release() method.

このような場合の最善の解決策は、一定の時間が経過すると自動的に解放される期限切れロックを作成することです (TTL は Time To Live と呼ばれます)。この時間 (秒単位) は、createLock() メソッドの 2 番目の引数として構成されます。必要に応じて、これらのロックを release() メソッドで早期に解放することもできます。

The trickiest part when working with expiring locks is choosing the right TTL. If it's too short, other processes could acquire the lock before finishing the job; if it's too long and the process crashes before calling the release() method, the resource will stay locked until the timeout:

期限切れのロックを操作する際に最も注意が必要なのは、適切な TTL を選択することです。TTL が短すぎると、ジョブを終了する前に他のプロセスがロックを取得する可能性があります。長すぎて release() メソッドを呼び出す前にプロセスがクラッシュした場合、リソースはタイムアウトまでロックされたままになります。
1
2
3
4
5
6
7
8
9
10
11
12
// ...
// create an expiring lock that lasts 30 seconds (default is 300.0)
$lock = $factory->createLock('pdf-creation', ttl: 30);

if (!$lock->acquire()) {
    return;
}
try {
    // perform a job during less than 30 seconds
} finally {
    $lock->release();
}

Tip

ヒント

To avoid leaving the lock in a locked state, it's recommended to wrap the job in a try/catch/finally block to always try to release the expiring lock.

ロックがロックされた状態のままになるのを避けるために、ジョブを try/catch/finally ブロックでラップして、期限切れのロックを常に解放しようとすることをお勧めします。

In case of long-running tasks, it's better to start with a not too long TTL and then use the refresh() method to reset the TTL to its original value:

実行時間の長いタスクの場合は、長すぎない TTL から開始してから、refresh() メソッドを使用して TTL を元の値にリセットすることをお勧めします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ...
$lock = $factory->createLock('pdf-creation', ttl: 30);

if (!$lock->acquire()) {
    return;
}
try {
    while (!$finished) {
        // perform a small part of the job.

        // renew the lock for 30 more seconds.
        $lock->refresh();
    }
} finally {
    $lock->release();
}

Tip

ヒント

Another useful technique for long-running tasks is to pass a custom TTL as an argument of the refresh() method to change the default lock TTL:

長時間実行されるタスクのもう 1 つの便利な手法は、refresh() メソッドの引数としてカスタム TTL を渡して、デフォルトのロック TTL を変更することです。
1
2
3
4
5
6
7
$lock = $factory->createLock('pdf-creation', ttl: 30);
// ...
// refresh the lock for 30 seconds
$lock->refresh();
// ...
// refresh the lock for 600 seconds (next refresh() call will be 30 seconds again)
$lock->refresh(600);

This component also provides two useful methods related to expiring locks: getRemainingLifetime() (which returns null or a float as seconds) and isExpired() (which returns a boolean).

このコンポーネントは、期限切れのロックに関連する 2 つの便利なメソッドも提供します。

Automatically Releasing The Lock

Locks are automatically released when their Lock objects are destroyed. This is an implementation detail that is important when sharing Locks between processes. In the example below, pcntl_fork() creates two processes and the Lock will be released automatically as soon as one process finishes:

Lock オブジェクトが破棄されると、ロックは自動的に解放されます。これは、プロセス間でロックを共有するときに重要な実装の詳細です。以下の例では、pcntl_fork() は 2 つのプロセスを作成し、一方のプロセスが終了するとすぐに theLock が自動的に解放されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ...
$lock = $factory->createLock('pdf-creation');
if (!$lock->acquire()) {
    return;
}

$pid = pcntl_fork();
if (-1 === $pid) {
    // Could not fork
    exit(1);
} elseif ($pid) {
    // Parent process
    sleep(30);
} else {
    // Child process
    echo 'The lock will be released now.';
    exit(0);
}
// ...

To disable this behavior, set the autoRelease argument of LockFactory::createLock() to false. That will make the lock acquired for 3600 seconds or until Lock::release() is called:

この動作を無効にするには、LockFactory::createLock() の autoRelease 引数を false に設定します。これにより、3600 秒間、または Lock::release() が呼び出されるまで、ロックが取得されます。
1
2
3
4
5
$lock = $factory->createLock(
    'pdf-creation',
    3600, // ttl
    false // autoRelease
);

Shared Locks

A shared or readers-writer lock is a synchronization primitive that allows concurrent access for read-only operations, while write operations require exclusive access. This means that multiple threads can read the data in parallel but an exclusive lock is needed for writing or modifying data. They are used for example for data structures that cannot be updated atomically and are invalid until the update is complete.

共有ロックまたはリーダー/ライター ロックは、読み取り専用操作の同時アクセスを許可する同期プリミティブであり、書き込み操作には排他的アクセスが必要です。これは、複数のスレッドがデータを並行して読み取ることができることを意味しますが、データの書き込みまたは変更には排他ロックが必要です。たとえば、アトミックに更新できず、更新が完了するまで無効なデータ構造に使用されます。

Use the acquireRead() method to acquire a read-only lock, and acquire() method to acquire a write lock:

acquireRead() メソッドを使用して読み取り専用ロックを取得し、acquire() メソッドを使用して書き込みロックを取得します。
1
2
3
4
$lock = $factory->createLock('user-'.$user->id);
if (!$lock->acquireRead()) {
    return;
}

Similar to the acquire() method, pass true as the argument of acquireRead() to acquire the lock in a blocking mode:

acquire() メソッドと同様に、acquireRead() の引数として true を渡して、ブロッキング モードでロックを取得します。
1
2
$lock = $factory->createLock('user-'.$user->id);
$lock->acquireRead(true);

Note

ノート

The priority policy of Symfony's shared locks depends on the underlying store (e.g. Redis store prioritizes readers vs writers).

Symfony の共有ロックの優先度ポリシーは、基礎となるストアによって異なります (たとえば、Redis ストアはリーダーとライターを優先します)。

When a read-only lock is acquired with the acquireRead() method, it's possible to promote the lock, and change it to a write lock, by calling the acquire() method:

acquireRead() メソッドで読み取り専用ロックを取得すると、acquire() メソッドを呼び出すことで、ロックを昇格させ、書き込みロックに変更することができます。
1
2
3
4
5
6
7
8
9
$lock = $factory->createLock('user-'.$userId);
$lock->acquireRead(true);

if (!$this->shouldUpdate($userId)) {
    return;
}

$lock->acquire(true); // Promote the lock to a write lock
$this->update($userId);

In the same way, it's possible to demote a write lock, and change it to a read-only lock by calling the acquireRead() method.

同様に、acquireRead() メソッドを呼び出すことで、書き込みロックを降格し、読み取り専用ロックに変更することができます。

When the provided store does not implement the SharedLockStoreInterface interface (see lock stores for supported stores), the Lock class will fallback to a write lock by calling the acquire() method.

提供されたストアがSharedLockStoreInterface インターフェイスを実装していない場合 (サポートされているストアについてはロック ストアを参照)、Lock クラスは acquire() メソッドを呼び出して書き込みロックにフォールバックします。

The Owner of The Lock

Locks that are acquired for the first time are owned [1]_ by the Lock instance that acquired it. If you need to check whether the current Lock instance is (still) the owner of a lock, you can use the isAcquired() method:

初めて取得されたロックは、それを取得した Lock インスタンスによって [1]_ 所有されます。現在の Lock インスタンスが (まだ) ロックの所有者であるかどうかを確認する必要がある場合は、isAcquired() メソッドを使用できます。
1
2
3
if ($lock->isAcquired()) {
    // We (still) own the lock
}

Because some lock stores have expiring locks, it is possible for an instance to lose the lock it acquired automatically:

一部のロック ストアには期限切れのロックがあるため、インスタンスが自動的に取得したロックを失う可能性があります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// If we cannot acquire ourselves, it means some other process is already working on it
if (!$lock->acquire()) {
    return;
}

$this->beginTransaction();

// Perform a very long process that might exceed TTL of the lock

if ($lock->isAcquired()) {
    // Still all good, no other instance has acquired the lock in the meantime, we're safe
    $this->commit();
} else {
    // Bummer! Our lock has apparently exceeded TTL and another process has started in
    // the meantime so it's not safe for us to commit.
    $this->rollback();
    throw new \Exception('Process failed');
}

Caution

注意

A common pitfall might be to use the isAcquired() method to check if a lock has already been acquired by any process. As you can see in this example you have to use acquire() for this. The isAcquired() method is used to check if the lock has been acquired by the current process only.

よくある落とし穴は、isAcquired() メソッドを使用して、ロックがプロセスによって既に取得されているかどうかを確認することです。この例でわかるように、これには acquire() を使用する必要があります。 isAcquired() メソッドは、ロックが現在のプロセスによってのみ取得されたかどうかを確認するために使用されます。
.. [1] Technically, the true owners of the lock are the ones that share the same instance of Key,
not Lock. But from a user perspective, Key is internal and you will likely only be working with the Lock instance so it's easier to think of the Lock instance as being the one that is the owner of the lock.
ロックではありません。しかし、ユーザーの観点からは、Key は内部的なものであり、Lock インスタンスのみを操作する可能性が高いため、Lock インスタンスはロックの所有者であると考える方が簡単です。

Available Stores

Locks are created and managed in Stores, which are classes that implement PersistingStoreInterface and, optionally, BlockingStoreInterface.

ロックは、PersistingStoreInterface およびオプションで BlockingStoreInterface を実装するクラスであるストアで作成および管理されます。

The component includes the following built-in store types:

このコンポーネントには、次の組み込みストア タイプが含まれています。
Store Scope Blocking Expiring Sharing
FlockStore local yes no yes
MemcachedStore remote no yes no
MongoDbStore remote no yes no
PdoStore remote no yes no
DoctrineDbalStore remote no yes no
PostgreSqlStore remote yes no yes
DoctrineDbalPostgreSqlStore remote yes no yes
RedisStore remote no yes yes
SemaphoreStore local yes no no
ZookeeperStore remote no no no

Tip

ヒント

A special InMemoryStore is available for saving locks in memory during a process, and can be useful for testing.

特別な InMemoryStore は、プロセス中にメモリにロックを保存するために利用でき、テストに役立ちます。

FlockStore

The FlockStore uses the file system on the local computer to create the locks. It does not support expiration, but the lock is automatically released when the lock object goes out of scope and is freed by the garbage collector (for example when the PHP process ends):

FlockStore は、ローカル コンピューター上のファイル システムを使用してロックを作成します。有効期限はサポートされていませんが、ロック オブジェクトがスコープ外になり、ガベージ コレクターによって解放されると (たとえば、PHP プロセスが終了したとき)、ロックは自動的に解放されます。
1
2
3
4
5
use Symfony\Component\Lock\Store\FlockStore;

// the argument is the path of the directory where the locks are created
// if none is given, sys_get_temp_dir() is used internally.
$store = new FlockStore('/var/stores');

Caution

注意

Beware that some file systems (such as some types of NFS) do not support locking. In those cases, it's better to use a directory on a local disk drive or a remote store.

一部のファイル システム (一部の種類の NFS など) はロックをサポートしていないことに注意してください。そのような場合は、ローカル ディスク ドライブまたはリモート ストアのディレクトリを使用することをお勧めします。

MemcachedStore

The MemcachedStore saves locks on a Memcached server, it requires a Memcached connection implementing the \Memcached class. This store does not support blocking, and expects a TTL to avoid stalled locks:

MemcachedStore は Memcached サーバーにロックを保存します。これには \Memcached クラスを実装する Memcachedconnection が必要です。このストアはブロッキングをサポートしておらず、TTL がロックの停止を回避することを期待しています。
1
2
3
4
5
6
use Symfony\Component\Lock\Store\MemcachedStore;

$memcached = new \Memcached();
$memcached->addServer('localhost', 11211);

$store = new MemcachedStore($memcached);

Note

ノート

Memcached does not support TTL lower than 1 second.

Memcached は、1 秒未満の TTL をサポートしていません。

MongoDbStore

The MongoDbStore saves locks on a MongoDB server >=2.2, it requires a \MongoDB\Collection or \MongoDB\Client from mongodb/mongodb or a MongoDB Connection String. This store does not support blocking and expects a TTL to avoid stalled locks:

MongoDbStore は MongoDB サーバー >=2.2 でロックを保存します。これには、mongodb/mongodb または aMongoDB 接続文字列からの \MongoDB\Collection または \MongoDB\Client が必要です。
1
2
3
4
5
6
7
8
9
10
11
use Symfony\Component\Lock\Store\MongoDbStore;

$mongo = 'mongodb://localhost/database?collection=lock';
$options = [
    'gcProbablity' => 0.001,
    'database' => 'myapp',
    'collection' => 'lock',
    'uriOptions' => [],
    'driverOptions' => [],
];
$store = new MongoDbStore($mongo, $options);

The MongoDbStore takes the following $options (depending on the first parameter type):

MongoDbStore は、次の $options を取ります (最初のパラメーターの型によって異なります)。
Option Description
gcProbablity Should a TTL Index be created expressed as a probability from 0.0 to 1.0 (Defaults to 0.001)
database The name of the database
collection The name of the collection
uriOptions Array of URI options for MongoDBClient::__construct
driverOptions Array of driver options for MongoDBClient::__construct

When the first parameter is a:

最初のパラメーターが次の場合:

MongoDB\Collection:

MongoDB\コレクション:
  • $options['database'] is ignored
    $options['database'] は無視されます
  • $options['collection'] is ignored
    $options['collection'] は無視されます

MongoDB\Client:

MongoDB\クライアント:
  • $options['database'] is mandatory
    $options['database'] は必須です
  • $options['collection'] is mandatory
    $options['collection'] は必須です

MongoDB Connection String:

MongoDB 接続文字列:
  • $options['database'] is used otherwise /path from the DSN, at least one is mandatory
    それ以外の場合は $options['database'] が使用されます /DSN からのパス、少なくとも 1 つが必須です
  • $options['collection'] is used otherwise ?collection= from the DSN, at least one is mandatory
    それ以外の場合は $options['collection'] が使用されます ?collection= DSN から、少なくとも 1 つが必須です

Note

ノート

The collection querystring parameter is not part of the MongoDB Connection String definition. It is used to allow constructing a MongoDbStore using a Data Source Name (DSN) without $options.

コレクション クエリ文字列パラメーターは、MongoDB 接続文字列定義の一部ではありません。$options なしでデータ ソース名 (DSN) を使用して MongoDbStore を構築できるようにするために使用されます。

PdoStore

The PdoStore saves locks in an SQL database. It is identical to DoctrineDbalStore but requires a PDO connection or a Data Source Name (DSN). This store does not support blocking, and expects a TTL to avoid stalled locks:

PdoStore はロックを SQL データベースに保存します。 DoctrineDbalStore と同じですが、PDO 接続またはデータ ソース名 (DSN) が必要です。このストアはブロッキングをサポートしておらず、TTL がロックの停止を回避することを期待しています。
1
2
3
4
5
use Symfony\Component\Lock\Store\PdoStore;

// a PDO or DSN for lazy connecting through PDO
$databaseConnectionOrDSN = 'mysql:host=127.0.0.1;dbname=app';
$store = new PdoStore($databaseConnectionOrDSN, ['db_username' => 'myuser', 'db_password' => 'mypassword']);

Note

ノート

This store does not support TTL lower than 1 second.

このストアは、1 秒未満の TTL をサポートしていません。

The table where values are stored is created automatically on the first call to the save() method. You can also create this table explicitly by calling the createTable() method in your code.

値が格納されるテーブルは、save() メソッドの最初の呼び出しで自動的に作成されます。コードで createTable() メソッドを呼び出して、このテーブルを明示的に作成することもできます。

DoctrineDbalStore

The DoctrineDbalStore saves locks in an SQL database. It is identical to PdoStore but requires a Doctrine DBAL Connection, or a Doctrine DBAL URL. This store does not support blocking, and expects a TTL to avoid stalled locks:

DoctrineDbalStore はロックを SQL データベースに保存します。 PdoStore と同じですが、Doctrine DBAL 接続または Doctrine DBAL URL が必要です。このストアドはブロッキングをサポートしておらず、TTL がロックの停止を回避することを期待しています。
1
2
3
4
5
use Symfony\Component\Lock\Store\DoctrineDbalStore;

// a Doctrine DBAL connection or DSN
$connectionOrURL = 'mysql://myuser:mypassword@127.0.0.1/app';
$store = new DoctrineDbalStore($connectionOrURL);

Note

ノート

This store does not support TTL lower than 1 second.

このストアは、1 秒未満の TTL をサポートしていません。

The table where values are stored is created automatically on the first call to the save() method. You can also add this table to your schema by calling configureSchema() method in your code or create this table explicitly by calling the createTable() method.

値が格納されるテーブルは、save() メソッドへの最初の呼び出しで自動的に作成されます。また、コード内で configureSchema() メソッドを呼び出してこのテーブルをスキーマに追加するか、createTable() メソッドを呼び出してこのテーブルを明示的に作成することもできます。

PostgreSqlStore

The PostgreSqlStore and DoctrineDbalPostgreSqlStore uses Advisory Locks provided by PostgreSQL. It is identical to DoctrineDbalPostgreSqlStore but requires PDO connection or a Data Source Name (DSN). It supports native blocking, as well as sharing locks:

PostgreSqlStore と DoctrineDbalPostgreSqlStore は、PostgreSQL が提供するアドバイザリ ロックを使用します。DoctrineDbalPostgreSqlStore と同じですが、PDO 接続またはデータ ソース名 (DSN) が必要です。ネイティブ ブロッキングと共有ロックをサポートします。
1
2
3
4
5
use Symfony\Component\Lock\Store\PostgreSqlStore;

// a PDO instance or DSN for lazy connecting through PDO
$databaseConnectionOrDSN = 'pgsql:host=localhost;port=5634;dbname=app';
$store = new PostgreSqlStore($databaseConnectionOrDSN, ['db_username' => 'myuser', 'db_password' => 'mypassword']);

In opposite to the PdoStore, the PostgreSqlStore does not need a table to store locks and it does not expire.

PdoStore とは対照的に、PostgreSqlStore はロックを格納するためのテーブルを必要とせず、有効期限もありません。

DoctrineDbalPostgreSqlStore

The DoctrineDbalPostgreSqlStore uses Advisory Locks provided by PostgreSQL. It is identical to PostgreSqlStore but requires a Doctrine DBAL Connection or a Doctrine DBAL URL. It supports native blocking, as well as sharing locks:

DoctrineDbalPostgreSqlStore は、PostgreSQL が提供するアドバイザリ ロックを使用します。PostgreSqlStore と同じですが、Doctrine DBAL 接続または Doctrine DBAL URL が必要です。ネイティブ ブロッキングと共有ロックをサポートします。
1
2
3
4
5
use Symfony\Component\Lock\Store\DoctrineDbalPostgreSqlStore;

// a Doctrine Connection or DSN
$databaseConnectionOrDSN = 'postgresql+advisory://myuser:mypassword@127.0.0.1:5634/lock';
$store = new DoctrineDbalPostgreSqlStore($databaseConnectionOrDSN);

In opposite to the DoctrineDbalStore, the DoctrineDbalPostgreSqlStore does not need a table to store locks and does not expire.

DoctrineDbalStore とは対照的に、DoctrineDbalPostgreSqlStore はロックを保存するためのテーブルを必要とせず、有効期限もありません。

RedisStore

The RedisStore saves locks on a Redis server, it requires a Redis connection implementing the \Redis, \RedisArray, \RedisCluster or \Predis classes. This store does not support blocking, and expects a TTL to avoid stalled locks:

RedisStore は Redis サーバーにロックを保存します。\Redis、\RedisArray、\RedisCluster または \Predis クラスを実装する Redis 接続が必要です。このストアはブロッキングをサポートしておらず、ロックの停止を回避するために TTL を想定しています。
1
2
3
4
5
6
use Symfony\Component\Lock\Store\RedisStore;

$redis = new \Redis();
$redis->connect('localhost');

$store = new RedisStore($redis);

SemaphoreStore

The SemaphoreStore uses the PHP semaphore functions to create the locks:

SemaphoreStore は、PHP セマフォ関数を使用してロックを作成します。
1
2
3
use Symfony\Component\Lock\Store\SemaphoreStore;

$store = new SemaphoreStore();

CombinedStore

The CombinedStore is designed for High Availability applications because it manages several stores in sync (for example, several Redis servers). When a lock is acquired, it forwards the call to all the managed stores, and it collects their responses. If a simple majority of stores have acquired the lock, then the lock is considered acquired:

CombinedStore は、複数のストア (複数の Redis サーバーなど) を同期して管理するため、高可用性アプリケーション向けに設計されています。ロックが取得されると、すべての管理対象ストアに呼び出しが転送され、応答が収集されます。単純過半数のストアがロックを取得した場合、ロックは取得されたと見なされます。
1
2
3
4
5
6
7
8
9
10
11
12
13
use Symfony\Component\Lock\Store\CombinedStore;
use Symfony\Component\Lock\Store\RedisStore;
use Symfony\Component\Lock\Strategy\ConsensusStrategy;

$stores = [];
foreach (['server1', 'server2', 'server3'] as $server) {
    $redis = new \Redis();
    $redis->connect($server);

    $stores[] = new RedisStore($redis);
}

$store = new CombinedStore($stores, new ConsensusStrategy());

Instead of the simple majority strategy (ConsensusStrategy) an UnanimousStrategy can be used to require the lock to be acquired in all the stores:

単純な多数決戦略 (ConsensusStrategy) の代わりに UnanimousStrategy を使用して、すべてのストアでロックを取得する必要があります。
1
2
3
4
use Symfony\Component\Lock\Store\CombinedStore;
use Symfony\Component\Lock\Strategy\UnanimousStrategy;

$store = new CombinedStore($stores, new UnanimousStrategy());

Caution

注意

In order to get high availability when using the ConsensusStrategy, the minimum cluster size must be three servers. This allows the cluster to keep working when a single server fails (because this strategy requires that the lock is acquired for more than half of the servers).

ConsensusStrategy を使用して高可用性を実現するには、最小クラスター サイズを 3 台のサーバーにする必要があります。これにより、単一のサーバーに障害が発生した場合でも、クラスターは動作し続けることができます (この戦略では、半分以上のサーバーに対してロックを取得する必要があるため)。

ZookeeperStore

The ZookeeperStore saves locks on a ZooKeeper server. It requires a ZooKeeper connection implementing the \Zookeeper class. This store does not support blocking and expiration but the lock is automatically released when the PHP process is terminated:

ZookeeperStore は、ZooKeeper サーバーにロックを保存します。 \Zookeeper クラスを実装する ZooKeeperconnection が必要です。このストアはブロックと有効期限をサポートしていませんが、PHP プロセスが終了するとロックは自動的に解放されます。
1
2
3
4
5
6
7
use Symfony\Component\Lock\Store\ZookeeperStore;

$zookeeper = new \Zookeeper('localhost:2181');
// use the following to define a high-availability cluster:
// $zookeeper = new \Zookeeper('localhost1:2181,localhost2:2181,localhost3:2181');

$store = new ZookeeperStore($zookeeper);

Note

ノート

Zookeeper does not require a TTL as the nodes used for locking are ephemeral and die when the PHP process is terminated.

PHP プロセスが終了すると、ロックに使用されるノードはエフェメラランドになるため、Zookeeper は TTL を必要としません。

Reliability

The component guarantees that the same resource can't be locked twice as long as the component is used in the following way.

コンポーネントは、コンポーネントが次のように使用されている限り、同じリソースが 2 回ロックされないことを保証します。

Remote Stores

Remote stores (MemcachedStore, MongoDbStore, PdoStore, PostgreSqlStore, RedisStore and ZookeeperStore) use a unique token to recognize the true owner of the lock. This token is stored in the Key object and is used internally by the Lock.

リモート ストア (MemcachedStore、MongoDbStore、PdoStore、PostgreSqlStore、RedisStore、ZookeeperStore) は一意のトークンを使用して、ロックの真の所有者を認識します。このトークンは Key オブジェクトに格納され、Lock によって内部的に使用されます。

Every concurrent process must store the Lock on the same server. Otherwise two different machines may allow two different processes to acquire the same Lock.

すべての同時プロセスは、同じサーバーにロックを保存する必要があります。そうしないと、2 つの異なるマシンが 2 つの異なるプロセスで同じロックを取得する可能性があります。

Caution

注意

To guarantee that the same server will always be safe, do not use Memcached behind a LoadBalancer, a cluster or round-robin DNS. Even if the main server is down, the calls must not be forwarded to a backup or failover server.

同じサーバーが常に安全であることを保証するには、ロードバランサー、クラスター、またはラウンドロビン DNS の背後で Memcachedb を使用しないでください。メイン サーバーがダウンしている場合でも、呼び出しをバックアップ サーバーまたはフェールオーバー サーバーに転送してはなりません。

Expiring Stores

Expiring stores (MemcachedStore, MongoDbStore, PdoStore and RedisStore) guarantee that the lock is acquired only for the defined duration of time. If the task takes longer to be accomplished, then the lock can be released by the store and acquired by someone else.

期限切れのストア (MemcachedStore、MongoDbStore、PdoStore、および RedisStore) は、ロックが定義された期間のみ取得されることを保証します。タスクの完了に時間がかかる場合は、ストアがロックを解放し、別の誰かがロックを取得できます。

The Lock provides several methods to check its health. The isExpired() method checks whether or not its lifetime is over and the getRemainingLifetime() method returns its time to live in seconds.

Lock は、その状態をチェックするいくつかの方法を提供します。 isExpired() メソッドはその有効期間が終了したかどうかをチェックし、getRemainingLifetime() メソッドは存続時間を秒単位で返します。

Using the above methods, a robust code would be:

上記の方法を使用すると、堅牢なコードは次のようになります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ...
$lock = $factory->createLock('pdf-creation', 30);

if (!$lock->acquire()) {
    return;
}
while (!$finished) {
    if ($lock->getRemainingLifetime() <= 5) {
        if ($lock->isExpired()) {
            // lock was lost, perform a rollback or send a notification
            throw new \RuntimeException('Lock lost during the overall process');
        }

        $lock->refresh();
    }

    // Perform the task whose duration MUST be less than 5 minutes
}

Caution

注意

Choose wisely the lifetime of the Lock and check whether its remaining time to live is enough to perform the task.

ロックの有効期間を賢明に選択し、タスクを実行するのに十分な残り時間があるかどうかを確認してください。

Caution

注意

Storing a Lock usually takes a few milliseconds, but network conditions may increase that time a lot (up to a few seconds). Take that into account when choosing the right TTL.

通常、ロックの保存には数ミリ秒かかりますが、ネットワークの状態によっては、その時間が大幅に長くなる場合があります (最大で数秒)。適切な TTL を選択するときは、この点を考慮してください。

By design, locks are stored on servers with a defined lifetime. If the date or time of the machine changes, a lock could be released sooner than expected.

設計上、ロックは定義された有効期間でサーバーに保存されます。マシンの日付または時刻が変わると、予想よりも早くロックが解除される可能性があります。

Caution

注意

To guarantee that date won't change, the NTP service should be disabled and the date should be updated when the service is stopped.

日付が変更されないことを保証するには、NTP サービスを無効にし、サービスが停止したときに日付を更新する必要があります。

FlockStore

By using the file system, this Store is reliable as long as concurrent processes use the same physical directory to store locks.

ファイル システムを使用することにより、同時プロセスが同じ物理ディレクトリを使用してロックを格納する限り、このストアは信頼できます。

Processes must run on the same machine, virtual machine or container. Be careful when updating a Kubernetes or Swarm service because, for a short period of time, there can be two containers running in parallel.

プロセスは、同じマシン、仮想マシン、またはコンテナーで実行する必要があります。Kubernetes または Swarm サービスを更新するときは注意してください。短期間、2 つのコンテナーが並行して実行される可能性があるためです。

The absolute path to the directory must remain the same. Be careful of symlinks that could change at anytime: Capistrano and blue/green deployment often use that trick. Be careful when the path to that directory changes between two deployments.

ディレクトリへの絶対パスは同じままにする必要があります。いつでも変更される可能性のあるシンボリック リンクに注意してください。そのディレクトリへのパスが 2 つのデプロイメント間で変更される場合は注意してください。

Some file systems (such as some types of NFS) do not support locking.

一部のファイル システム (一部の種類の NFS など) は、ロックをサポートしていません。

Caution

注意

All concurrent processes must use the same physical file system by running on the same machine and using the same absolute path to the lock directory.

すべての同時プロセスは、同じマシン上で実行し、ロック ディレクトリへの同じ絶対パスを使用することにより、同じ物理ファイル システムを使用する必要があります。

Using a FlockStore in an HTTP context is incompatible with multiple front servers, unless to ensure that the same resource will always be locked on the same machine or to use a well configured shared file system.

HTTP コンテキストで FlockStore を使用することは、同じリソースが常に同じマシン上でロックされるようにするか、適切に構成された共有ファイル システムを使用する場合を除き、マルチフロント サーバーと互換性がありません。

Files on the file system can be removed during a maintenance operation. For instance, to clean up the /tmp directory or after a reboot of the machine when a directory uses tmpfs. It's not an issue if the lock is released when the process ended, but it is in case of Lock reused between requests.

ファイル システム上のファイルは、メンテナンス操作中に削除できます。たとえば、ディレクトリが tmpfs を使用している場合に、/tmp ディレクトリをクリーンアップしたり、マシンの再起動後に実行したりします。プロセスが終了したときにロックが解放されても問題ありませんが、リクエスト間でロックが再利用された場合です。

Caution

注意

Do not store locks on a volatile file system if they have to be reused in several requests.

ロックをいくつかのリクエストで再利用する必要がある場合は、揮発性ファイル システムにロックを保存しないでください。

MemcachedStore

The way Memcached works is to store items in memory. That means that by using the MemcachedStore the locks are not persisted and may disappear by mistake at any time.

Memcached が機能する方法は、アイテムをメモリに保存することです。つまり、MemcachedStore を使用すると、ロックが保持されず、いつでも誤って消える可能性があります。

If the Memcached service or the machine hosting it restarts, every lock would be lost without notifying the running processes.

Memcached サービスまたはそれをホストするマシンが再起動すると、実行中のプロセスに通知せずにすべてのロックが失われます。

Caution

注意

To avoid that someone else acquires a lock after a restart, it's recommended to delay service start and wait at least as long as the longest lock TTL.

再起動後に他の誰かがロックを取得するのを避けるために、サービスの開始を遅らせ、少なくとも最長のロック TTL まで待機することをお勧めします。

By default Memcached uses a LRU mechanism to remove old entries when the service needs space to add new items.

デフォルトでは、Memcached は LRU メカニズムを使用して、サービスが新しいアイテムを追加するためのスペースを必要とするときに古いエントリを削除します。

Caution

注意

The number of items stored in Memcached must be under control. If it's not possible, LRU should be disabled and Lock should be stored in a dedicated Memcached service away from Cache.

Memcached に格納されるアイテムの数は、制御下にある必要があります。それが不可能な場合は、LRU を無効にし、ロックをキャッシュから離れた専用の Memcached サービスに保存する必要があります。

When the Memcached service is shared and used for multiple usage, Locks could be removed by mistake. For instance some implementation of the PSR-6 clear() method uses the Memcached's flush() method which purges and removes everything.

Memcached サービスが共有され、複数の用途で使用されている場合、ロックが誤って削除される可能性があります。たとえば、PSR-6 の clear() メソッドの一部の実装では、すべてをパージして削除する Memcached の flush() メソッドを使用します。

Caution

注意

The method flush() must not be called, or locks should be stored in a dedicated Memcached service away from Cache.

メソッド flush() を呼び出してはなりません。または、ロックをキャッシュから離れた専用の Memcached サービスに保存する必要があります。

MongoDbStore

Caution

注意

The locked resource name is indexed in the _id field of the lock collection. Beware that an indexed field's value in MongoDB can be a maximum of 1024 bytes in length including the structural overhead.

ロックされたリソース名は、lockcollection の _id フィールドでインデックス化されます。 MongoDB のインデックス付きフィールドの値の長さは、構造的なオーバーヘッドを含めて最大 1024 バイトになることに注意してください。

A TTL index must be used to automatically clean up expired locks. Such an index can be created manually:

期限切れのロックを自動的にクリーンアップするには、TTL インデックスを使用する必要があります。このようなインデックスは手動で作成できます。
1
2
3
4
db.lock.createIndex(
    { "expires_at": 1 },
    { "expireAfterSeconds": 0 }
)

Alternatively, the method MongoDbStore::createTtlIndex(int $expireAfterSeconds = 0) can be called once to create the TTL index during database setup. Read more about Expire Data from Collections by Setting TTL in MongoDB.

または、MongoDbStore::createTtlIndex(int $expireAfterSeconds = 0) メソッドを 1 回呼び出して、データベースのセットアップ中に TTL インデックスを作成することもできます。詳細については、MongoDB で TTL を設定してコレクションからデータを期限切れにするをご覧ください。

Tip

ヒント

MongoDbStore will attempt to automatically create a TTL index. It's recommended to set constructor option gcProbablity to 0.0 to disable this behavior if you have manually dealt with TTL index creation.

MongoDbStore は、TTL インデックスを自動的に作成しようとします。 TTL インデックスの作成を手動で処理した場合は、コンストラクタ オプション gcProbablity を 0.0 に設定してこの動作を無効にすることをお勧めします。

Caution

注意

This store relies on all PHP application and database nodes to have synchronized clocks for lock expiry to occur at the correct time. To ensure locks don't expire prematurely; the lock TTL should be set with enough extra time in expireAfterSeconds to account for any clock drift between nodes.

このストアは、すべての PHP アプリケーションおよびデータベース ノードに依存して、ロックの有効期限が正しい時間に発生するようにクロックを同期させます。ロックが早期に期限切れにならないようにするため。ロック TTL は、ノード間のクロック ドリフトを考慮して、expireAfterSeconds に十分な追加時間を設定する必要があります。

writeConcern and readConcern are not specified by MongoDbStore meaning the collection's settings will take effect. readPreference is primary for all queries. Read more about Replica Set Read and Write Semantics in MongoDB.

writeConcern と readConcern は MongoDbStore で指定されていないため、コレクションの設定が有効になります。readPreference はすべてのクエリで優先されます。

PdoStore

The PdoStore relies on the ACID properties of the SQL engine.

PdoStore は、SQL エンジンの ACID プロパティに依存しています。

Caution

注意

In a cluster configured with multiple primaries, ensure writes are synchronously propagated to every node, or always use the same node.

複数のプライマリで構成されたクラスターでは、書き込みがすべてのノードに同期的に伝達されるようにするか、常に同じノードを使用します。

Caution

注意

Some SQL engines like MySQL allow to disable the unique constraint check. Ensure that this is not the case SET unique_checks=1;.

MySQL などの一部の SQL エンジンでは、一意の制約チェックを無効にすることができます。SET unique_checks=1; にならないようにしてください。

In order to purge old locks, this store uses a current datetime to define an expiration date reference. This mechanism relies on all server nodes to have synchronized clocks.

古いロックを消去するために、このストアは現在の日時を使用して有効期限の参照を定義します。このメカニズムは、クロックが同期されていることをすべてのサーバー ノードに依存しています。

Caution

注意

To ensure locks don't expire prematurely; the TTLs should be set with enough extra time to account for any clock drift between nodes.

ロックが早期に期限切れにならないようにするため。 TTL は、ノード間のクロック ドリフトを考慮して、十分な時間をかけて設定する必要があります。

PostgreSqlStore

The PdoStore relies on the Advisory Locks properties of the PostgreSQL database. That means that by using PostgreSqlStore the locks will be automatically released at the end of the session in case the client cannot unlock for any reason.

PdoStore は、PostgreSQL データベースの Advisory Locks プロパティに依存しています。つまり、PostgreSqlStore を使用すると、クライアントが何らかの理由でロックを解除できない場合に備えて、セッションの最後にロックが自動的に解除されます。

If the PostgreSQL service or the machine hosting it restarts, every lock would be lost without notifying the running processes.

PostgreSQL サービスまたはそれをホストするマシンが再起動すると、実行中のプロセスに通知せずにすべてのロックが失われます。

If the TCP connection is lost, the PostgreSQL may release locks without notifying the application.

TCP 接続が失われると、PostgreSQL はアプリケーションに通知せずにロックを解放することがあります。

RedisStore

The way Redis works is to store items in memory. That means that by using the RedisStore the locks are not persisted and may disappear by mistake at any time.

Redis の仕組みは、アイテムをメモリに保存することです。つまり、RedisStore を使用すると、ロックが保持されず、いつでも誤って消える可能性があります。

If the Redis service or the machine hosting it restarts, every locks would be lost without notifying the running processes.

Redis サービスまたはそれをホストするマシンが再起動すると、実行中のプロセスに通知せずにすべてのロックが失われます。

Caution

注意

To avoid that someone else acquires a lock after a restart, it's recommended to delay service start and wait at least as long as the longest lock TTL.

再起動後に他の誰かがロックを取得するのを避けるために、サービスの開始を遅らせ、少なくとも最長のロック TTL まで待機することをお勧めします。

Tip

ヒント

Redis can be configured to persist items on disk, but this option would slow down writes on the service. This could go against other uses of the server.

Redis はディスク上の項目を永続化するように構成できますが、このオプションではサービスへの書き込みが遅くなります。これは、サーバーの他の用途に反する可能性があります。

When the Redis service is shared and used for multiple usages, locks could be removed by mistake.

Redis サービスを共有して複数の用途で使用すると、誤ってロックが解除される可能性があります。

Caution

注意

The command FLUSHDB must not be called, or locks should be stored in a dedicated Redis service away from Cache.

コマンド FLUSHDB を呼び出してはなりません。そうしないと、キャッシュから離れた専用の Redis サービスにロックを保存する必要があります。

CombinedStore

Combined stores allow the storage of locks across several backends. It's a common mistake to think that the lock mechanism will be more reliable. This is wrong. The CombinedStore will be, at best, as reliable as the least reliable of all managed stores. As soon as one managed store returns erroneous information, the CombinedStore won't be reliable.

結合されたストアにより、複数のバックエンドにわたってロックを保存できます。ロック機構の方が信頼性が高いと考えるのはよくある間違いです。これは間違っています。CombinedStore は、せいぜい、すべての管理されたストアの中で最も信頼性が低いものと同じくらい信頼できます。 1 つの管理ストアが誤った情報を返すとすぐに、CombinedStore は信頼できなくなります。

Caution

注意

All concurrent processes must use the same configuration, with the same amount of managed stored and the same endpoint.

すべての並行プロセスは、同じ構成を使用し、同じ量の管理ストレージと同じエンドポイントを使用する必要があります。

Tip

ヒント

Instead of using a cluster of Redis or Memcached servers, it's better to use a CombinedStore with a single server per managed store.

Redis または Memcached サーバーのクラスターを使用する代わりに、マネージド ストアごとに 1 つのサーバーで CombinedStore を使用することをお勧めします。

SemaphoreStore

Semaphores are handled by the Kernel level. In order to be reliable, processes must run on the same machine, virtual machine or container. Be careful when updating a Kubernetes or Swarm service because for a short period of time, there can be two running containers in parallel.

セマフォは、カーネル レベルで処理されます。信頼性を高めるために、プロセスは同じマシン、仮想マシン、またはコンテナーで実行する必要があります。 Kubernetes または Swarm サービスを更新するときは注意してください。短期間、2 つのコンテナーが並行して実行される可能性があるためです。

Caution

注意

All concurrent processes must use the same machine. Before starting a concurrent process on a new machine, check that other processes are stopped on the old one.

すべての並行プロセスは、同じマシンを使用する必要があります。新しいマシンで並行プロセスを開始する前に、古いマシンで他のプロセスが停止していることを確認してください。

Caution

注意

When running on systemd with non-system user and option RemoveIPC=yes (default value), locks are deleted by systemd when that user logs out. Check that process is run with a system user (UID <= SYS_UID_MAX) with SYS_UID_MAX defined in /etc/login.defs, or set the option RemoveIPC=off in /etc/systemd/logind.conf.

非システム ユーザーおよびオプション RemoveIPC=yes (デフォルト値) を使用して systemd で実行している場合、そのユーザーがログアウトすると、systemd によってロックが削除されます。プロセスがシステム ユーザー (UID) で実行されていることを確認します。

ZookeeperStore

The way ZookeeperStore works is by maintaining locks as ephemeral nodes on the server. That means that by using ZookeeperStore the locks will be automatically released at the end of the session in case the client cannot unlock for any reason.

ZookeeperStore が機能する方法は、ロックをサーバー上の一時的なノードとして維持することです。つまり、ZookeeperStore を使用すると、クライアントが何らかの理由でロックを解除できない場合に備えて、セッションの終了時にロックが自動的に解除されます。

If the ZooKeeper service or the machine hosting it restarts, every lock would be lost without notifying the running processes.

ZooKeeper サービスまたはそれをホストするマシンが再起動すると、実行中のプロセスに通知せずにすべてのロックが失われます。

Tip

ヒント

To use ZooKeeper's high-availability feature, you can setup a cluster of multiple servers so that in case one of the server goes down, the majority will still be up and serving the requests. All the available servers in the cluster will see the same state.

ZooKeeper の高可用性機能を使用するには、複数のサーバーのクラスターをセットアップして、サーバーの 1 つがダウンしても大多数のサーバーが引き続き稼働し、要求を処理できるようにします。クラスター内のすべての使用可能なサーバーは、同じ状態になります。

Note

ノート

As this store does not support multi-level node locks, since the clean up of intermediate nodes becomes an overhead, all locks are maintained at the root level.

このストアは複数レベルのノード ロックをサポートしていないため、中間ノードのクリーンアップがオーバーヘッドになるため、すべてのロックはルートレベルで維持されます。

Overall

Changing the configuration of stores should be done very carefully. For instance, during the deployment of a new version. Processes with new configuration must not be started while old processes with old configuration are still running.

ストアの構成の変更は、非常に慎重に行う必要があります。たとえば、新しいバージョンの展開中。古い構成の古いプロセスがまだ実行されている間は、新しい構成のプロセスを開始してはなりません。