分散ジョブスケジューリングのためのPostgreSQLアドバイザリーロック

ジョブスケジューリングのためだけに、RedisやSQSをスタックに追加するのはやめましょう。

代わりにPostgreSQLのアドバイザリーロックを使用できます。このアプローチにより、新しいインフラを導入する必要がなくなります。

私のテストでは、この構成により単一のデータベースインスタンスで毎分10,000件のジョブを処理できました。ワーカーがすでにデータベース接続を保持しているため、レイテンシの面ではRedisを上回ることもよくあります。余計なネットワークホップを回避できるからです。

実装方法:

• ジョブの確保には pg_try_advisory_xact_lock を使用します。 • 行の競合を処理するには FOR UPDATE SKIP LOCKED を使用します。 • ロックリークを防ぐために、トランザクション版を使用します。

なぜトランザクションロックが重要なのか:

トランザクションロックは、トランザクションがコミットまたはロールバックされると自動的に解放されます。これにより、アプリケーションがクラッシュした場合でも、孤立したロック(orphaned locks)が発生するのを防げます。また、トランザクションモードのPgBouncerとも安全に動作します。

PgBouncerを使用している場合は、セッションロックを避けてください。PgBouncerはトランザクション間で接続を再割り当てします。これによりセッションレベルのロックが壊れ、サイレントな失敗を引き起こします。

セッションロックが必要な場合は、ワーカー用に別のコネクションプールを作成してください。Webトラフィックとジョブワーカーのトラフィックを同じプールに混ぜないようにしましょう。

キーによるスケーリング:

アドバイザリーロックは、bigintまたは2つの整数を使用します。2つの整数を使用することで、自然な名前空間(namespacing)を提供できます。テキストキーをbigintにハッシュ化する場合は、衝突(collision)に注意してください。衝突は、一意のロックIDが100,000個に達するまでは低く抑えられます。それを超える場合は、安全のために2つの整数形式を使用してください。

トレードオフ:

• アドバイザリーロックは、シンプルさと運用コストの低さで勝ります。 • Redisは、生の処理能力(スループット)と水平スケーリングで勝ります。

ほとんどのチームにとって、毎分10,000件を超えるジョブは必要ありません。その制限を下回っている場合は、PostgreSQLを使い続けましょう。それにより、アーキテクチャをクリーンに保ち、コストを低く抑えることができます。

出典: https://dev.to/software_mvp-factory/postgresql-advisory-locks-for-distributed-job-scheduling-15kp