Salesforceでは、DML操作(データベース操作)とHTTPコールアウトを同じトランザクション内で同期的に実行することは許可されていません。この制約は、主に以下の理由から生じます。
1. トランザクションの整合性の問題
DML操作(例:レコードの作成、更新、削除)はトランザクション内で実行されます。これに対して、HTTPコールアウトは外部システムと通信するため、レスポンスが遅延したり、外部システムの状態によっては応答が得られなかったりする場合があります。もしDML操作とコールアウトが同一トランザクション内で実行されると、外部システムの応答によってトランザクション全体が長時間ブロックされるリスクがあります。
このため、Salesforceではトランザクションが完了するまでの間、外部の依存関係がトランザクションの成功や失敗に影響を与えることを避けるために、DML操作と同期的なコールアウトの同時実行を禁止しています。
デフォルトでは、必ず同じトランザクション内で DML 操作の後にコールアウトを実行することは許可されません。これは DML 操作によって、コミットされていない待機中の作業が発生してコールアウトの実行が妨げられるためです。
参考:https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_classes_restful_http_testing_dml.htm
2. コールアウトの失敗がデータ整合性に与える影響
もし、after update
トリガー内で同期的なコールアウトを行い、そのコールアウトが失敗した場合、トリガー全体がエラーとなり、DML操作(この場合はレコードの更新)がロールバックされます。これにより、Salesforce内のデータが意図しない状態になる可能性があります。例えば、レコードの更新が完了していると思いきや、コールアウトが失敗したことでデータベース上の変更も取り消されてしまうという状況が発生します。
3. 非同期処理の推奨
Salesforceでは、DML操作とコールアウトを組み合わせる必要がある場合、非同期処理(例:@future
メソッドやQueueableクラス)を使用することが推奨されています。これにより、DML操作が完了した後に別のトランザクションとしてコールアウトを実行し、トランザクションの整合性を維持しつつ外部システムとの連携を実現できます。
4. ガバナ制限の問題
Salesforceは、プラットフォーム上でのリソース使用量を制限する「ガバナ制限」を設けています。DML操作と同期的なHTTPコールアウトを同じトランザクションで行うと、以下のようなガバナ制限に違反するリスクがあります。
- トランザクションあたりのコールアウト数: トランザクション内で許可されるコールアウトの数は同期Apex,非同期Apexどちらも100まで、トランザクション内のすべてのコールアウト (HTTP 要求または Web サービスコール) のタイムアウトの最大累積値は120秒です。これらを超過する可能性があります。
- DML操作の組み合わせ: あるトリガーで大量のレコード更新が行われる場合、さらにコールアウトが追加されるとリソース使用量が過剰になる可能性があります。
もし一度に大量のレコードを更新し、さらにそのトリガーの中で外部システムと通信(コールアウト)しようとすると、Salesforceのシステムが「リソースの使いすぎだよ!」と判断してしまうことがあります。
例えば、ある営業担当者が管理しているお客様データ(レコード)を一度に100件更新するとします。その際に、更新されたデータを外部のシステムに通知する必要があると考えて、トリガーの中でHTTPコールアウトを100回行った場合、DML操作ではガバナ制限に引っかからないのに、コールアウトのガバナ制限に引っかかってしまいます。
まとめ
「after update
トリガー内での同期的なHTTPコールアウトがSystem.CalloutExceptionを引き起こす」というのは、DML操作とコールアウトが同じトランザクション内で行われることをSalesforceが許可していないためです。この制限を回避するためには、非同期処理を用いることが推奨されます。これにより、データベースの整合性を保ちながら、外部システムとの通信を適切に行うことができます。