はじめに
Salesforce にてレコードの取得や更新に関する開発をしていると、ときどきガバナ制限に遭遇します。
ガバナ制限とは何か、どのような場合にどう回避するかを解説します。
ガバナ制限とは
Salesforce はマルチテナント環境で動作しており、複数の組織が同一のサーバーリソースを共有しています。ガバナ制限とは、ある組織のコードや処理が共有リソースを占有しないよう Salesforce ランタイムが強制する上限値のことです。
制限を超えると、System.LimitException がスローされ、処理が強制終了します。
Apex ガバナ制限
開発中によく遭遇する主要な制限です。同期処理と非同期処理(Batch Apex など)で上限が異なります。
DML(Data Manipulation Language)とは、レコードの挿入・更新・削除・アップサート操作のことを指します。
| 説明 | 同期の制限 | 非同期の制限 |
|---|---|---|
| SOQL クエリの発行数 | 100 | 200 |
| SOQL で取得できる総レコード数 | 50,000 | 50,000 |
| DML ステートメント数 | 150 | 150 |
| DML で処理される総レコード数 | 10,000 | 10,000 |
| CPU 時間 | 10,000 ミリ秒 | 60,000 ミリ秒 |
| ヒープサイズ | 6 MB | 12 MB |
参考: Apex ガバナ制限 | Salesforce Developer の制限および割り当てクイックリファレンス
SOQL の制限
SOQL クエリの結果件数は実行する文脈によって上限が異なります。
| 文脈 | 上限 |
|---|---|
| API 経由(REST/SOAP) | 1 リクエストあたり 2,000 件 |
| Apex 内(トランザクション合計) | 50,000 件 |
参考: SOQL および SOSL の検索クエリの制限 | Salesforce Developers
フロー(Flow)のガバナ制限
公式ドキュメントには次のように記載されています。
Apex で適用されるトランザクション単位の制限により、フローが制限されます。
フローも Apex と同じトランザクション単位の制限が適用されます。
| 説明 | 制限値 |
|---|---|
| 発行された SOQL クエリの総数 | 100 |
| DML ステートメントの合計数 | 150 |
| DML で処理されるレコードの合計数 | 10,000 |
| Salesforce サーバーの最大 CPU 時間 | 10,000 ミリ秒 |
| ヒープサイズ | 6 MB |
参考: トランザクション単位のフローの制限 | Salesforce Help
Apex での対応案
SOQL for ループで大量レコードを逐次処理する
通常のコレクション代入では、すべてのレコードをヒープに読み込みます。大量データを扱う場合、ヒープサイズ制限を超えることがあります。
Database.QueryLocator を用いた SOQL for ループでは、レコードをチャンク単位で逐次処理するため、ヒープの消費を抑えられます。
for (Account acc : [SELECT Id, Name FROM Account]) {
// レコードをまとめてヒープに持たずに逐次処理できる
}
たとえば、Lightning Web Components から呼び出す Apex メソッド内でこのパターンを活用できます。
参考: コンポーネントへの Apex メソッドの公開 | Salesforce Developers
ただし、1 トランザクションあたり 50,000 件の上限は変わりません。50,000 件を超えるデータを扱う場合は、Batch Apex または Data Loader を検討してください。
Apex Batch を使う
一度に大量のレコードを処理する必要がある場合は Batch Apex を使います。
Batch Apex は Database.Batchable インターフェースを実装することで利用できます。execute の 1 チャンクあたりのデフォルトは 200 件で、最大 2,000 件まで指定可能です。
public class MyBatchJob implements Database.Batchable<SObject> {
public Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator('SELECT Id, Name FROM Account');
}
public void execute(Database.BatchableContext bc, List<SObject> scope) {
List<Account> accounts = (List<Account>) scope;
for (Account acc : accounts) {
acc.Name = acc.Name + '_updated';
}
update accounts;
}
public void finish(Database.BatchableContext bc) {
// 全チャンク完了後の後処理(メール通知・後続ジョブの起動など)
}
}
start メソッドで使用する Database.getQueryLocator は、通常の SOQL が持つ 50,000 件制限の対象外で、最大 5,000 万件まで取得できます。大量レコードの一括処理に適しています。
Batch Apex は非同期処理のため、ガバナ制限が緩和されます。具体的には、SOQL 発行数が 100 から 200 に、CPU 時間が 10,000 ms から 60,000 ms に拡大されます。
MyBatchJob job = new MyBatchJob();
Database.executeBatch(job, 200); // 第2引数でチャンクサイズを指定
大量データ操作への対応案
Data Loader を使う
Apex トランザクションの 50,000 件上限を超える大量データを操作する場合は Data Loader の利用を検討してください。
Data Loader は Salesforce が提供する公式クライアントツールで、CSV ファイルをもとにレコードの一括挿入・更新・削除・エクスポートができます。Apex トランザクションを経由しないため、Data Loader はトランザクション単位のガバナ制限の対象外です。
参考: Data Loader | Salesforce Developers
主なユースケース
- 初期データ投入(数十万〜数百万件規模の一括 insert)
- 定期的な外部システムとのデータ同期
- 大量レコードの一括削除
1 回の操作で処理できる上限は 5,000,000 件です。5,000,000 件を超える場合はファイルを分割して複数回実行します。
API 経由で自動化したい場合は Bulk API 2.0 も有効です。バックグラウンドで非同期処理されるため、大量レコードのインポート・エクスポートに適しています。
参考: Bulk API および Bulk API 2.0 の制限および割り当て | Salesforce Developers
フローでの対応案
DML をループ外に出す(一括処理)
レコードトリガーフローなどでは、ループ内の「レコードを作成/更新」要素をループ外に移動することで、DML の消費を大幅に削減できます。
スケジュールトリガーフローでは、条件に合致したレコードに対する処理を記述するだけでよい場合が多いため、ループ等の複雑な処理を書く必要がないかもしれません。
参考: トランザクションでのフローの一括処理 | Salesforce Help
参考: スケジュールトリガーフローに関する考慮事項 | Salesforce Help
おわりに
ガバナ制限はマルチテナント環境を健全に保つための仕組みです。制限に達したときは設計を見直すと、より堅牢な実装に近づけられます。