【Salesforce】大量データを扱う際に発生するガバナ制限エラーの回避方法
2016.06.25
こんにちは、ユニトラストの大谷です。
先日、Salesforceを使用した開発に携わった際に、ガバナ制限にハマッて苦労したので、その時に起こったいくつかの問題と、その回避方法をまとめたいと思います。
そもそもガバナ制限とは?
Salesforceで開発を行う場合、ApexというSalesforce独自のプログラミング言語を使用して機能を実装し、それをSalesforceのプラットフォーム上で実行させることになります。
ガバナ制限とは、その実行時にプラットフォーム上にあるDBやメモリなどのリソースを、大きく占有するような処理が行われないように設けられている制限のことで、この制限に引っかかる処理を実行してしまうとエラーが発生してしまいます。
なぜこのような制限があるのかというと、Salesforceは複数の顧客ユーザがリソースを共有して利用する、マルチテナント環境で実行されているため、1つのテナントでリソースを独占させないためにあるようです。
どのような制限があるのかは、以下のページで確認が出来ます。
Execution Governors and Limits
日本語ページはこちら(英語ページよりも更新が遅いので、最新情報を確認する際は英語ページを参照するようにしてください。)
バッチ処理の利用
ガバナ制限に抵触しそうな大量データを扱う処理を実装する場合、まずは「Batch Apex」というSalesforceのバッチ機能を検討することをお勧めします。
バッチとしての実行が可能な処理を実装するであれば、この機能を使用してバッチ化してしまったほうが断然良いと思います。
バッチ化した場合、非同期での実行しか出来なくなる代わりに、ガバナ制限が大幅に緩和されるので、この記事に記載する回避方法を取らずとも大量データを扱う処理が実行できます。
「Batch Apex」を使用した場合の制限や、機能の使い方については以下のページを参照ください。
Apex の一括処理の使用
本題
ここでは「Batch Apex」を使用しない場合を想定し、次のガバナ制限エラーとその回避方法について書いていきます。
① 5万件を超えるレコードを取得した場合のエラー
② ヒープサイズエラー
① 5万件を超えるレコードを取得した場合のエラー
『Visualforce Remoting Exception: Too many query rows: 50001』
このエラーは以下のようなデータ取得処理で、取得レコード数が5万件を超えてしまった場合に発生します。
List<CustomObject__c> objList = [SELECT Id FROM CustomObject__c];
ちなみにこの制限は、次のようにレコード件数を取得するだけの場合でも引っかかってしまいます。
Integer cnt = [SELECT count() FROM CustomObject__c];
このエラーを回避する方法の一つに、ReadOnlyモードを使用するというものがあります。
これを使用すると、処理中にレコードの追加、更新、削除が出来なくなる代わりに、取得できるレコード数が『100万件』まで増えます。
ReadOnlyモードの使用方法は2通りあり、一つはVisualForceページの<apex:page>タグで「readOnly」パラメータにtrueを設定する方法です。
<apex:page docType="html-5.0" readOnly="true" controller="HogeController"> </apex:page>
この方法の場合、DBから取得できるレコードが増えるだけでなく、<apex:repeat>などの繰り返し処理を行うタグで使用するコレクションの上限も増えるようです(1000件 ⇒ 1万件)。
もう一つの方法は、Apexクラスのメソッドに「@ReadOnly」アノテーションを付ける方法です。
global class HogeController2 { @ReadOnly @RemoteAction global static Integer getCount() { return [SELECT count() FROM CustomObject__c]; } }
尚、「@ReadOnly」アノテーションを付けるには、メソッドが次の条件を満たさなくてはいけないので、使用する際はご注意ください。
・@RemoteActionが付いていること
・アクセス修飾子がglobalかpublicであること
・staticであること
② ヒープサイズエラー
『Visualforce Remoting Exception: Apex heap size too large: 606480』
大量データを処理していると、このエラーが発生することがあるかもしれません。
Salesforceでは同期処理で使用出来るヒープメモリサイズが6MBと決まっています。
このサイズは数万件分のレコードを変数に保持しようとしたら、すぐに到達してしまいます。
DBからデータを取得したら、それをループして1件ずつ処理をしていくことになると思いますが、その場合には以下のようにループ処理を書くことで、ヒープエラーを回避することが出来ます。
for(List<CustomObject__c> objList in [Select Id, Name From CustomObject__c]){ //200レコード単位で処理する for(CustomObject__c obj in objList){ //レコード1件に対しての処理をここに書く } }
この処理は「SOQL for ループ」というもので、クエリ発行によって取得するレコードを200件ずつ取り出してループ処理してくれます。
これにより、一度に保持しておくデータ量が大幅に減るので、ヒープサイズエラーが非常に起き難くなります。
ちなみに、このループ処理は以下のように動的SOQLを使用した書き方も出来るので、処理に応じて使い分けが可能です。
String sql = 'Select Id, Name From CustomObject__c'; for(List<CustomObject__c> objList in Database.Query(sql)){ //200レコード単位で処理する for(CustomObject__c obj in objList){ //レコード1件に対しての処理をここに書く } }
終わりに
ガバナ制限については、Salesforceで開発を行っている方はみんな苦労しているようで、色々な解決方法が模索されています。
私が困った際も色々とネット記事を参考にさせて頂き、とても助かりました。
今回この記事で紹介した問題と回避方法はその中のほんの一部ですが、この記事が同じような問題にぶつかってしまった方の一助となれば幸いです。
参考記事
・意外と知らないSalesforce Tips (8~10)
・Apex Heap Size Best Practices
CONTACT
お問い合わせ
あなたの「想い」に挑戦します。
どうぞお気軽にお問い合わせください。
受付時間:平日9:00〜18:00 日・祝日・弊社指定休業日は除く