Salesforceスケジューラ


TLドクター
ポストは、大規模なデータセットを扱うために、スケジュール可能なバッチ可能な待ち行列構造を採用する利点を説明しようとします.スケジューリングに関するスケジュール可能な焦点、Batchableは分割します、そして、Queueは実行します.

導入

We usually need to schedule some jobs within our Salesforce orgs to execute some specific piece of logic at some given time.

The basic use case for this might be deleting custom logs on schedule to release space to have new ones inserted into the database. Say, we only keep the very near 30 days logs which means we will execute deletion every day to remove logs generated 30 days ago.

Let’s presume what we have now is a small start-up business org where we have thousands of logs daily. We can delete all of them within a standalone scheduler.

スタンドアロンスケジューラ

Here we have a very simple scheduler with only one line in the block of the main method of a scheduler.

public with sharing class LogDeletionScheduler implements Schedulable {
    public void execute(SchedulableContext objSC) {
        delete [SELECT Id FROM Log__c LIMIT 10000];
    }
}
We use System.schedule to have a new job scheduled. For the Cron 1 我々が仕事を予定する前に、若干のオンライン資源は、このサイトのようにそれのために助けることができます2 .
global class /*System.*/System 
{
  // ...
  global static String schedule(String jobName, String cronExp, Schedulable schedulable)
  {
  }
  // ...
}
今、我々は、下のスクリプトを使用して真夜中にそれをスケジュールします.
System.schedule('LogDeletionScheduler', '0 0 0 * * ?', new LogDeletionScheduler());
テストクラスでテストしましょう.
@IsTest
private class LogDeletionSchedulerTest {
    @TestSetup
    static void init() {
        Test.startTest();
        List<Log __c> lstLog = new List<Log__ c>();
        for (Integer intI = 0; intI < 100; intI++) {
            lstLog.add(new Log__c());
        }
        insert lstLog;
        Test.stopTest();
    }
    @IsTest
    static void testDeletingLogs() {
        Test.startTest();
        System.assert([SELECT COUNT() FROM Log__c LIMIT 200] == 100);
        System.schedule('test', '0 0 12 1 * ?', new LogDeletionScheduler());
        Test.stopTest();
        System.assert([SELECT COUNT() FROM Log__c LIMIT 200] == 0);
    }
}
予想通り、我々はパスを得た.

この例では、私たちは、日常的に扱うための数千のログしか持っていないことを確認しました.スケジューラでLIMIT DMLの制限を防ぐために追加されます.

私たちのビジネスの拡大とともに、我々は毎日丸太の数万人に対処する必要があります.おそらく、私たちは1時間ごとに短いベースでそれらを削除することができます.このポストでは、我々はビジネス上でreckonのログの量について懸念したくない.私達はちょうど丸太の量が何であれ、毎日それらのすべてを扱う予定の仕事を望む.また、1時間ごとにスケジュールされた仕事は、私たちがそれらをスケジュールしようとするとき、毎日のものより洗練されるかもしれません.
それで、ログのサイズが1日10、000より大きいとき、我々が我々のコードを変えないならば、何が起こるでしょうか?ありがとうLIMIT SOQLでは、ジョブは失敗しません.しかし、ジョブ自体は10、000は今のログを削除することができます.除去LIMIT Salesforceによって定義された制限のために仕事をクラッシュさせる3 . 導入についてORDER BY CreatedDate ASC 以前LIMIT 10000 ? さて、それは最古の10、000ログを削除しますが、質問はまだ残っている.この時点ですべて削除できません.

救助にバッタブル

Salesforce has a built-in batchable interface 4 バッチでデータを扱う.すなわち、我々が扱っているデータはバッチに分割され、最後の1レコードが処理されるまで、同じ論理の下で実行される.

何が打撲の内部ですか?

We have to implement three methods inside a batch class. execute is the main part for data handling. finish is usually used in chained jobs where we can invoke jobs one after another. start is used for splitting which returns an Iterable to iterate through all of the queried data.

global interface /*Database.*/Batchable 
{
    void execute(Database.BatchableContext param1, List<Object> param2);

    void finish(Database.BatchableContext param1);

    Iterable start(Database.BatchableContext param1);
}

Talk is cheap. Show me the code.

スケジューラからバッチへ移行する

public with sharing class LogDeletionBatch implements Database.Batchable<SObject> {
    public Database.QueryLocator start(Database.BatchableContext objBC) {
        return Database.getQueryLocator([SELECT Id FROM Log__c]);
    }
    public void execute(Database.BatchableContext objBC, List<Log__c> lstLog) {
        delete lstLog;
    }
    public void finish(Database.BatchableContext objBC) {
    }
}

The scheduler was updated accordingly as below. The only thing we need in a scheduler is to execute the batch job with a given batch size. In this example, a default value is specified.

public with sharing class LogDeletionScheduler implements Schedulable {
    public void execute(SchedulableContext objSC) {
        Database.executeBatch(new LogDeletionBatch(), 200);
    }
}

便益はどこですか。

Under the above structure, we don’t need the LIMIT in the SOQL. The batchable will split Logs into many smaller lists and execute the same logic onto each and every one of them until the last is finished.


十分良い?

It seems the Log deleting job works perfect so far. But what will happen with the further expansion of the business? Some other schedulers and batch jobs will be included into the very same org. There could be a scenario where many batch jobs are working at the same time. Each of them handles their own business, of course. For example, some jobs will be executed insanely during midnight when no end user is accessing the site. Another issue comes up again with Salesforce limitations.

You’ve exceeded the limit of 100 jobs in the flex queue for org 00D5f000006tSmL. Wait for some of your batch jobs to finish before adding more. To monitor and reorder jobs, use the Apex Flex Queue page in Setup.


必要に応じてバッチジョブを実行することはできません.リソースは限られています.そして、それはマルチテナント建築の下で重要な点です.上記の3つの制限の中で最も厳しいのは、範囲内のバッチジョブがキューで待つ代わりに失敗するためです.

処理用キュー

We all have different parts to play.

With all said above around a batch, it does well in data splitting but not handling. Here comes another important interface, Queueable. As the name implies, it queues jobs. Compared with two stated interfaces, the new one is more flexible. The limitation of a Queueable is only counting against the whole asynchronous calls. Beyond that, we can usually enqueue jobs with Queueable freely.


public with sharing class LogDeletionQueue implements Queueable {
    private List<Log__c> lstLog;
    public LogDeletionQueue(List<Log__c> lstLog) {
        this.lstLog = lstLog;
    }
    public void execute(QueueableContext objQC) {
        if (this.lstLog == null || this.lstLog.isEmpty()) {
            return;
        }
        delete this.lstLog;
    }
}
したがって、バッチは更新されるべきである.
public with sharing class LogDeletionBatch implements Database.Batchable<SObject> {
    public Database.QueryLocator start(Database.BatchableContext objBC) {
        return Database.getQueryLocator([SELECT Id FROM Log__c]);
    }
    public void execute(Database.BatchableContext objBC, List<Log__c> lstLog) {
        System.enqueueJob(new LogDeletionQueue(lstLog));
    }
    public void finish(Database.BatchableContext objBC) {
    }
}

結論

With all above, we come into a conclusion that the three interfaces woking together will unleash the power of asynchronous solution. By doing so, we can reduce some future code refactoring and diminish some unexpected limitation issues.


cron - Wikipedia ↩︎
Free Online Cron Expression Generator and Describer - FreeFormatter.com ↩︎
Execution Governors and Limits | Apex Developer Guide | Salesforce Developers ↩︎
Apex Developer Guide | Using Batch Apex (salesforce.com) ↩︎