Open1

【対応ログ】CannotAcquireLockException

ふじしろふじしろ

前提

  • Kotlin
  • Spring
  • Doma2

事象
毎日ある特定の時間帯に、定期的に叩かれるAPIがCannotAcquireLockExceptionを吐いて失敗している

調査
ますCannotAcquireLockException について検索
https://spring.pleiades.io/spring-framework/docs/current/javadoc-api/org/springframework/dao/CannotAcquireLockException.html

更新中(「更新の選択」ステートメントなど)にロックを取得できなかった場合にスローされる例外。

この時間帯にロックが取得できていない。
->近い時間帯のバッチ処理を確認し怪しい処理を発見

対処方針を把握するためGPTに一般的な対応がどんなもんか質問(ぶん投げ)

Q:
一般的なCannotAcquireLockExceptionの発生原因と改善策(最大5個)を検討して提案せよ。

A:

  1. 同時アクセスによる競合: 他のトランザクションによってすでにロックされていると、この例外が発生する可能性があります。その場合、データベースがリクエストされたロックを取得できるまで指定したタイムアウト期間が経過すれば例外が発生します。この問題に対処するために、トランザクションの分離レベルを調整するか、タイムアウト期間を延ばすことができます。
  2. デッドロック: デッドロックは、複数のトランザクションが同じリソースのロックを待っている状態です。デッドロックが発生した場合、データベースはデッドロックを解消するためにいずれかのトランザクションをロールバックします。デッドロックを解消するためには、アクセスするリソースの順序や、行ロックの適用範囲を検討し直すことができます。
  3. ロック対象のテーブル設計: ロック対象のテーブルが適切に設計されていないと、ロックの競合が発生しやすくなります。例えば、NULL値を許可するカラムが多い場合、ロックの解除が遅くなる場合があります。適切なインデックスを設定することで、ロックの競合を軽減することができます。
  4. ロック粒度の調整: データベースシステムは、テーブル、行、ページなど、異なるレベルでロックを行うことができます。ロック粒度が大きすぎると、リソースの競合が発生しやすくなりますが、ロック粒度が小さすぎると、オーバーヘッドが大きくなる恐れがあります。適切なロック粒度を検討することで、パフォーマンスと同時アクセス制御のバランスを取ることができます。
  5. バッチ処理のスケジューリング: ゲストユーザー削除バッチの実行タイミングが、他の処理と競合している可能性があります。バッチ処理の実行タイミングを調整することで、この問題を軽減することができます。例えば、バッチ処理を実行する時間帯を変更するか、他の処理が実行されていない時間帯にバッチ処理を実行することで、競合を回避できます。