An advisory lock is a mutex used to ensure no two processes run some process at the same time. When the advisory lock is powered by your database server, your mutex spans hosts.
参照: https://github.com/ClosureTree/with_advisory_lock
同じ処理が同時に実行されないようにするためのmutexである。advisory lockがデータベースサーバーによって管理されている場合、そのmutexはホストをまたいで機能する。

同時に一つのタスクのみがクリティカルセクション(単一の計算資源に対して複数処理が同時期に実行されると整合性がとれなくなる箇所)に入ることを許可する仕組み(ここで言うタスクとは、スレッドまたはプロセスを指す)
参照: https://ja.wikipedia.org/wiki/%E3%83%9F%E3%83%A5%E3%83%BC%E3%83%86%E3%83%83%E3%82%AF%E3%82%B9
アプリケーション側で定義可能なロック作成の仕組み。PostgreSQLの他のロックとは異なり、使用を強制するものではないことからAdvisory Lockという名前がつけられている。
flat file型という悲観的ロックの手法(ファイルを作成し存在する間は他のプロセスが同じリソースにアクセスしないように制御する)がある。
この手法を用いたロックの実現において、同じようにテーブルでフラグを管理することがあるが、それと比較して
という利点がある。 Advisory LockはこのようなMVCCアーキテクチャと適合しづらい部分での使用が想定されている。
PostgreSQLでは、Advisory Lockを取得するために2つの方法が用意されている。
明示的に解放するかセッションが終了するまでAdvisory Lockが保持される。 また、この時トランザクションの挙動から影響を受けない(トランザクション中に取得されたロックは、そのトランザクションが後でロールバックされたとしても保持され続ける。同じくその後でトランザクションが失敗してもロックを解放することができる)
同一セッションでは、同じロックを複数回取得することができる。この場合、ロックを完全に解放するためには、取得した回数と同じだけロックを解放する必要がある。
トランザクションの終了時に自動的に解放され、明示的にアンロックする必要がない。
あるセッションがすでに特定のアドバイザリロックを保持している場合、そのセッションからの追加のロック要求は、他のセッションが待機していたとしても常に成功する。
max_locks_per_transactionやmax_connectionsに設定されたサイズの共有メモリに保持される。 共有メモリを使い切らないように注意する必要がある。
Advisory Lockを取得するための関数は以下を参照。 Advisory Lock Functions
session(connection) A
SELECT pg_advisory_lock(12345);
session(connection) B
SELECT pg_advisory_lock(12345); -- session Aのロックが解放されるまで待たされる
session(connection) A
BEGIN;
SELECT pg_advisory_xact_lock(12345);
-- COMMIT;
session(connection) B
BEGIN;
SELECT pg_advisory_xact_lock(12345); -- session Aのロックが解放されるまで待たされる
COMMIT; -- session AのCOMMIT;後に実行される
session(connection) A
COMMIT; -- ロックが解放され、session Bのtransaction内でロックが取得される
ActiveRecordにはAdvisory Lock用のAPIは存在しない。with_advisory_lockというgemを使うと、ActiveRecordのモデルにAdvisory Lock用のAPIを追加することができる。
例: session levelのAdvisory Lock (デフォルト)
User.with_advisory_lock(12345) do
# ...
end
例: transaction levelのAdvisory Lock
User.transaction do
User.with_advisory_lock("lock_name", transaction: true) do
# ...
end
end
参照: https://github.com/ClosureTree/with_advisory_lock?tab=readme-ov-file#transactions-and-advisory-locks