Salesforceのフローで起きたUNABLE_TO_LOCK_ROWをループ処理で解決してみた


この記事はなに?

Salesforceのフローにて、レコードを更新する処理を走らせた際に、UNABLE_TO_LOCK_ROWのエラーが発生したため、その原因と対処方法を記載した記事になります。

元々どんな処理を組んでいたか

スケジュールトリガフローにて、デイリーでキャンペーンメンバーのレコードを起点に、リードとキャンペーンメンバーのレコードを更新する処理を組んでいました。(イメージ画像は、わかりやすくするために簡略化しています)

エラー内容と発生原因

公式のヘルプによると、

UNABLE_TO_LOCK_ROW エラーは、更新処理Aが更新処理を実行しようとした対象レコードが別の更新処理Bから更新処理を実行中であり、データの整合性を保つ為にロックされているために更新処理 A がロックの解放を待機する状態となり、待機時間の上限値を超過して待機してしまった際に発生するエラーとなります。

と書かれています。
今回の事例だと、リードとキャンペーンメンバーが1:Nの関係になっており、キャンペーンメンバーAがリードAを更新している途中で、キャンペーンメンバーBもリードAを更新しようとしてエラーが発生していました。(このエラーが起きるまで、レコードの更新処理は1レコードずつ処理されると思っていたのですが、実は並列で実行されていることを知りました)

対処方法

最初に更新対象のキャンペーンメンバーのコレクションを作った上で、ループ処理で1レコードずつ処理する方式に変更しました。

これにより、同時にリードが更新されることがなくなったため、無事UNABLE_TO_LOCK_ROWのエラーを解決することができました。

補足情報

当初、トリガーの起点オブジェクトをリードにすれば解決できると思っていたのですが、キャンペーンメンバーは特殊なオブジェクトのようで、他のオブジェクトからの参照はできない仕様になっていたため、リードを起点にすることを断念しました。
公式リファレンス

おわりに

ここまで読んでくださりありがとうございました。
今回のエラー対応を通して、レコード更新の仕組みとループ処理の使い所を理解することができました。
フローは便利なのですが、如何せん処理部分がブラックボックス化されていたり、リファレンスの日本語が難解だったりと躓く箇所がいくつもあるので、今後も新しい発見があれば記事に起こしていこうと思います。