Apexの開発時に毎度毎度気をつけなければならないのがガバナ制限。
うまく実装できたと思ってもデータ量が多かったりするとガバナ制限に引っかかってエラーに。。。なんてこともしばしば。
そして割と高頻度でその内容を忘れてしまいます。
今回はそろそろ頭に叩き込もうということで備忘録もかねてガバナ制限を可能な限り検証しながら確認していこうと思います。
ガバナ制限とは
ガバナ制限とは、処理実行時にSalesforceにあるDBやメモリなどのリソースが大きく占領されることを防ぎ、他のユーザの使用に影響を与えないための制限のことです。
例えばある1人のユーザがSalesforceを操作しており、Apex処理が実行されたとします。
その際、その1人の操作のせいで処理に大きな時間がかかり他のユーザが思うようにシステムを使用できないなんてことがあってはこまりますよね。
ガバナ制限はこのような事象を回避するために設けられています。
それでは具体的なガバナ制限をみていきましょう。
参考:Apexガバナ制限
① 発行される SOQL クエリの合計数
1度の処理(以下1トランザクション)で発行される SOQL クエリの合計数の上限は100です。(非同期処理では200)
以下のコードを匿名実行コンソールで実行し、SOQLを101回以上発行するとエラーがでます。
for(Integer i = 0; i < 101; i++){
List<Account> accList = [SELECT Id FROM Account];
}
表示されるエラーは以下の通りです。
System.LimitException: Too many SOQL queries: 101
エラー回避方法は以下の記事を参考にしてみてください。
② SOQL クエリによって取得されるレコードの合計数
1トランザクションで SOQL クエリによって取得されるレコード合計数の上限は50,000です。(非同期処理も50,000)
以下のコードを匿名実行コンソールで実行し、取得レコード合計数が50,000を超えるとエラーがでます。
//Accountに50001件以上レコードがある状態で実施
List<Account> accList = [SELECT Id FROM Account];
表示されるエラーは以下の通りです。
System.LimitException: Too many query rows: 50001
ちなみに以下のように50,000件を超えるレコード件数を取得する場合でもエラーででます。
Integer count = [SELECT count() FROM Account];
エラー回避方法は以下の記事を参考にしてみてください。
③ Database.getQueryLocator によって取得されるレコードの合計数
1トランザクションでDatabase.getQueryLocator によって取得されるレコード合計数の上限は10,000です。(非同期処理も10,000)
以下のコードを匿名実行コンソールで実行し、取得レコード合計数が10,000を超えるとエラーがでます。
//Accountに10001件以上レコードがある状態で実施
String query = 'SELECT Id FROM Account';
Database.QueryLocator result = Database.getQueryLocator(query);
表示されるエラーは以下の通りです。
System.LimitException: Too many query locator rows: 10001
④ 発行される SOSL クエリの合計数
1トランザクションで発行される SOSL クエリの合計数の上限は20です。(非同期処理でも20)
以下のコードを匿名実行コンソールで実行し、SOSLを21回以上発行するとエラーがでます。
for(Integer i = 0; i < 21; i++){
List<List<SObject>> searchList = [FIND 'Sample' IN ALL FIELDS RETURNING Account(Name)];
}
表示されるエラーは以下の通りです。
System.LimitException: Too many SOSL queries: 21
⑤ 1 つの SOSL クエリによって取得されるレコードの合計数
1つの SOSL クエリによって取得されるレコード合計数の上限は2,000です。(非同期処理も2,000)
このエラーはどのようにはっせいさせるのでしょうか、、、
検証中です。
⑥ 発行される DML ステートメントの合計数
1トランザクションで発行される DML ステートメントの合計数の上限は150です。(非同期処理も150)
DML ステートメントとは、以下のようにDBのデータを操作するような操作言語を指します。
- insert
- update
- delete
- upsert
- undelete
- merge
参考:DMLの操作
以下のコードを匿名実行コンソールで実行し、 DML ステートメントの合計が151以上になるとエラーがでます。
for(integer i=0; i<151;i++){
Account acc = new Account();
acc.Name='test'+i;
insert acc;
}
表示されるエラーは以下の通りです。
System.LimitException: Too many DML statements: 151
エラー回避方法は以下の記事を参考にしてみてください。
⑦ DML ステートメント、Approval.process または database.emptyRecycleBin の結果として処理されるレコードの合計数
ApprovalクラスのprocessメソッドとDatabaseクラスのemptyRecycleBinメソッドもDML制限に含まれるようです。
この2つのメソッドの結果として処理されるレコード数の上限は10,000です。(非同期処理も150)
mptyRecycleBinメソッドについては以下のコードを匿名実行コンソールで実行し、ごみ箱にあるデータを10001件以上削除しようとするとエラーが発生します。
List<Account> accList = [Select Id from Account Where IsDeleted = true ALL ROWS];
Database.emptyRecycleBin(accList);
表示されるエラーは以下の通りです。
System.LimitException: Too many DML rows: 10001
⑧ insert、update、または delete ステートメントによって繰り返しトリガする Apex 呼び出しのスタックの深さの合計数
繰り返しでのApexトリガの呼び出しの上限は16回です。(非同期処理も16)
これはどういうことかというと、
例えば、オブジェクトAのレコードを更新した際にオブジェクトBのレコードを更新するトリガがあるとします。
さらにオブジェクトBのレコードが更新された際にオブジェクトCのレコードを更新するトリガがあるとします。
この場合、オブジェクトAのレコードを更新するだけで連鎖的に2つのトリガが起動することになります。
このようなトリガの連続起動の上限が16回ということです。
⑨ トランザクション内のコールアウト (HTTP 要求または Web サービスコール) の合計数
トランザクション内のコールアウト (HTTP 要求または Web サービスコール) の合計数の上限は100です。(非同期処理も100)
以下のコードを匿名実行コンソールで実行し、 HTTPコールアウトの合計が101以上になるとエラーがでます。
for(Integer i = 0; i < 101 ; i++){
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://th-apex-http-callout.herokuapp.com/sample');
request.setMethod('GET');
HttpResponse response = http.send(request);
}
表示されるエラーは以下の通りです。
System.LimitException: Too many callouts: 101
⑩ トランザクション内のすべてのコールアウト (HTTP 要求または Web サービスコール) のタイムアウトの最大累積値
トランザクション内のすべてのコールアウト (HTTP 要求または Web サービスコール) のタイムアウトの最大累積値は120秒です。(非同期処理も120秒)
こちらは⑨と関連していますのでセットで押さえておきたいですね。
⑪ Apex 呼び出し 1 回につき許可される future アノテーションを持つメソッドの最大数
Apex 呼び出し 1 回につき許可されるfutureアノテーションを持つメソッドの最大数は50です。(batch および future のコンテキストの場合 0、queueable コンテキストの場合 1)
以下のようなトリガを作成し、取引先レコードがinsertされた際にfutureメソッドが動くようにしてみました。
取引先レコードを51件insertするとfutureメソッドが51回動き、ガバナ制限にかかります。
trigger HttpCalloutTrigger on Account (after insert) {
if(Trigger.isInsert && Trigger.isAfter){
for(Account acc : Trigger.New) {
HttpCallout.callouttest();
}
}
}
public class HttpCallout {
@future(callout=true)
public static void callouttest() {
//HTTPリクエストの作成
HttpRequest req = new HttpRequest();
req.setEndpoint('https://th-apex-http-callout.herokuapp.com/sample');
req.setMethod('GET');
//HTTPリクエストの送信
Http http = new Http();
HttpResponse res = http.send(req);
//レスポンスチェック
if (res.getStatusCode() == 200) {
System.debug('Success');
} else {
System.debug('Callout failed: ' + res);
}
}
}
表示されるエラーは以下の通りです。
System.LimitException: Too many future calls: 51
⑫ System.enqueueJob によってキューに追加される Apex ジョブの最大数
System.enqueueJob によってキューに追加される Apex ジョブの最大数は50です。(非同期処理では1)
⑬ 許可される sendEmail メソッドの合計数
許可されるsendEmailメソッドの合計数の上限は10です。(非同期処理も10)
以下のコードを匿名実行コンソールで実行し、 メールメッセージの送信件数が11件以上になるとエラーがでます。
for(Integer i = 0; i < 11 ; i++){
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
mail.setToAddresses(new String[]{'sample@gmail.com'});
mail.setSubject('件名');
mail.setPlainTextBody('本文');
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}
表示されるエラーは以下の通りです。
System.LimitException: Too many Email Invocations: 11
⑭ ヒープの合計サイズ
ヒープサイズとは処理実行時に保持されるメモリサイズのことです。
ヒープサイズは6MBです。(非同期処理では12MB)
大量データを変数に保持しようとすると上限に達してしまいます。
以下のコードを匿名実行コンソールで実行し、ヒープサイズが上限を超えるとエラーがでます。
String tStr = 'aaaaa bbbbb ccccc ddddd eeeeee fffff ggggg 11111 22222 33333 44444';
List<String> bigList = tStr.split(' ');
Map<integer, List<String>> SampleMap = new Map<integer, List<String>>();
SampleMap.put(1, bigList);
for (integer i=0; i<50; i++) {
List<String> tempList = new List<String>();
tempList = SampleMap.get(1);
bigList.addAll(tempList);
}
表示されるエラーは以下の通りです。
System.LimitException: Apex heap size too large: 40996469
⑮ Salesforce サーバの最大 CPU 時間
こちらは処理に時間がかかりすぎてエラーが発生するガバナ制限です。
Salesforce サーバの最大 CPU 時間は10,000 ミリ秒です。(非同期処理も60,000 ミリ秒)
以下のコードを匿名実行コンソールで実行し、 CPU 時間の上限を超えるとエラーがでます。
Integer i = 0;
While(True){
i = i + 1;
}
表示されるエラーは以下の通りです。
System.LimitException: Apex CPU time limit exceeded
まとめ
今回記載しているもの以外にもガバナ制限はありますが、とりあえずこのあたりで切り上げようと思います。
あまり遭遇する機会がないものも多いので(笑)
個人的に特に気を付けているものでいうと、
- 発行される SOQL クエリの合計数
- SOQL クエリによって取得されるレコードの合計数
- Database.getQueryLocator によって取得されるレコードの合計数
- 発行される DML ステートメントの合計数
- Salesforce サーバの最大 CPU 時間
あたりでしょうか。
このあたりは基本的といえば基本的な内容ですが、たまにうっかりガバナにかかることもあるので引き続き気をつけていきたいです。
そのほかの制限についても常に頭の片隅において開発を進められるようになりたいですね。
今回は以上です。
コメント