こんにちは、ロジカル・アーツの町川です。
「今日以降の最も今日に近い日付」を表示してほしいという要件があり、チェックボックス項目とスケジュールトリガフローと積み上げ集計項目にて構築しようとしたところ、"UNABLE_TO_LOCK_ROW"というエラーが表示されました。
積み上げ集計項目を使用せず、スケジュールトリガフローを工夫することによって回避することができたので、今回は、"UNABLE_TO_LOCK_ROW"というエラーの回避方法について紹介します。
はじめに、どうしてこのエラーが発生したか
要件
親オブジェクトであるAに対して、数件の紐づくBレコードの中から、積み上げ集計項目にて、「今日以降の最も今日に近い日付」を表示してほしいという要件でした。
以下、簡単に(オブジェクト名を伏せて)構成図を掲載します。
構築内容
調査したところ、現状の段階(2024年4月)では積み上げ集計項目ではそこまでの条件指定ができかねると判断したため、スケジュールトリガフローでチェックボックス項目である「集計対象項目フラグ」をTRUEにし、そのフラグがTRUEのものだけ積み上げ集計に含めるという手法を取りました。
エラー発生原因
公式ヘルプ(後述)を見ると、"トランザクションに集計項目が含まれている場合、親オブジェクトがロックされるため、このようなトランザクションでは特にデッドロックが発生しやすくなります。" とあり、こちらが起こったようです。
"UNABLE_TO_LOCK_ROW"とは
Salesforce SOAP API 開発者ガイドには以下のように記載されています。
デッドロックまたはタイムアウト条件が検出されました。
デッドロックには、重なり合うオブジェクトセットを更新しようとする最低 2 つのトランザクションが関係しています。トランザクションに集計項目が含まれている場合、親オブジェクトがロックされるため、このようなトランザクションでは特にデッドロックが発生しやすくなります。デバッグするには、コード内のデッドロックを確認し修正してください。通常デッドロックは Salesforce 操作の問題から引き起こされる結果ではありません。
タイムアウトは、選択リストの値の置き換えや、カスタム項目定義の変更など、完了までに時間がかかりすぎるトランザクションで発生します。タイムアウト状態は一時的です。修正アクションは不要です。
バッチに含まれるオブジェクトをロックできない場合、バッチ全体でこのエラーが発生し失敗します。この状況コードのエラーでは、エラーメッセージにロックできないレコードの ID が含まれます (使用可能な場合)。
引用元:Salesforce SOAP API 開発者ガイド
また、Salesforce ヘルプページには以下のように記載されています。
更新処理Aが更新処理を実行しようとした対象レコードが別の更新処理Bから更新処理を実行中であり、データの整合性を保つ為にロックされているために更新処理 A がロックの解放を待機する状態となり、待機時間の上限値を超過して待機してしまった際に発生するエラーとなります。
引用元:Salesforce ヘルプページ
今回の場合、簡潔にまとめると、積み上げ集計項目の条件として「集計対象項目フラグ」を使用しており、それをスケジュールトリガフローにて更新しようとしたことが良くなかったようです。
回避方法
積み上げ集計項目を使用せず、スケジュールトリガフロー内にて取得方法を工夫することにしました。
具体的には、コンポーネント「レコードの取得」にて、
該当日付項目(CreatedDate) 以上 {!$Flow.CurrentDate}(現在の項目)
と設定し、該当日付項目を昇順に並び替え、
保存するレコード数のところを「最初のレコードのみ」と選択しました。
これで、「今日以降の最も今日に近い日付」を取得できました。
(具体的なオブジェクト名は伏せています)
そして、コンポーネント「レコードを更新」にて、取ってきた日付で該当日付項目を更新します。
終わりに
思いがけず"UNABLE_TO_LOCK_ROW"が起こってしまいましたが、調査することで、積み上げ集計項目についての知見が深まりました。
その回避方法を考えるなかで、フローでも積み上げ集計に近いことができるとわかり、条件を変えたりループを使うことで、さらにいろいろな形の集計ができることに気づきました。
この記事がどなたかの一助になれば幸いです。