Discourse は同期手段として Redis ロックを広く利用しています。以下に示す Discourse の Redis ロックの実装は、使用する Redis コマンド数を減らすことで改善でき、ラウンドトリップ時間の短縮や Redis 実行コストの削減が期待できます。
改善方法
lock と unlock の両方で Redis トランザクション が使用されていますが、lock プロセスは以下のコードのように簡略化できます。
WATCH key
GET key # キーの有効期限切れ判定
MULTI
SET key
EXPIRE key, expire_time + 1
Redis トランザクションを使用する理由(私の推測では)は、実際にキーを設定する前にそのキーの有効期限が切れていないかを確認するためだと思われます。
しかし、Redis が提供する SETEX コマンドを使用すれば、有効期限付きでキーを設定できるため、この手順は不要だと考えられます。実際、Redis SETEX ドキュメント でも、SET と EXPIRE を SETEX で置き換える例が示されています。
以下に、置き換えるべき理由を挙げます。
- キーを設定する前に値を有効期限として設定し、それを確認する必要はありません。TTL メカニズムだけで、キーが正しく期限切れになることを保証できます。
- 仮に有効期限を値として使用すると決めても、トランザクションは不要です。上記コードの 2 行目にある
GETとMULTI&EXECの間に原子性を提供しても意味がないからです。なぜなら、ロックが取得できなければリトライされるためです。
歴史的な注記
より詳しい情報を得るために、Git のコミット履歴を調査しました。Redis ロックが 導入 された当初は、Redis が提供する TTL は使用されていなかったようです。TTL 機能はその後 はるかに遅れて 追加されました。
したがって、一歩進んで Redis トランザクションを完全に削除することも可能だと考えられます。