' P '

whatever I will forget

Queueable Apex

Queueable Apex とは

  • future メソッドと同様に非同期処理のジョブを送信でき、加えて下記の利点がある
  • Queueable クラスには、sObject 型やカスタム Apex 型など、プリミティブ以外のデータ型のメンバー変数を含めることができる
  • System.enqueueJob メソッドを呼び出してジョブを送信すると、メソッドが AsyncApexJob レコードの ID を返す。
    • この ID を使用して、Salesforce ユーザーインターフェースの [Apex ジョブ] ページから、またはプログラムで AsyncApexJob のレコードを照会する方法で、ジョブを識別してその進行状況を監視できる.
  • 実行中のジョブから 2 つ目のジョブを開始することで、2 つのジョブを連鎖的に実行することができる

queueable と future の比較

  • ほとんどの場合futureを使用せずにqueueableにreplaceできる
  • futureメソッドを使用する場合は、機能が同期実行されることもあれば、非同期実行されることもある場合

Queueable Apexの構文

  • Queueableインターフェースを実装するだけ
public class SomeClass implements Queueable {
    public void execute(QueueableContext context) {
        // awesome code here
    }
}
public class UpdateParentAccount implements Queueable {
    private List<Account> accounts;
    private ID parent;
    public UpdateParentAccount(List<Account> records, ID id) {
        this.accounts = records;
        this.parent = id;
    }
    public void execute(QueueableContext context) {
        for (Account account : accounts) {
          account.parentId = parent;
          // perform other processing or callout
        }
        update accounts;
    }
}
  • 上記のクラスをジョブとしてキューに追加するには:
// find all accounts in ‘NY’
List<Account> accounts = [select id from account where billingstate = ‘NY’];
// find a specific parent account for all records
Id parentId = [select id from account where name = 'ACME Corp'][0].Id;
// instantiate a new instance of the Queueable class
UpdateParentAccount updateJob = new UpdateParentAccount(accounts, parentId);
// enqueue the job for processing
ID jobID = System.enqueueJob(updateJob);
  • ジョブがキューに追加され、システムリソースが使用可能になると処理されるので、取得したjobIDを使用して[Apex Job (Apex ジョブ)] ページから、またはプログラムで AsyncApexJob を照会する方法で、進行状況を監視できる
SELECT Id, Status, NumberOfErrors FROM AsyncApexJob WHERE Id = :jobID

Queueable Apex のテスト

@isTest
public class UpdateParentAccountTest {
    @testSetup
    static void setup() {
        List<Account> accounts = new List<Account>();
        // add a parent account
        accounts.add(new Account(name='Parent'));
        // add 100 child accounts
        for (Integer i = 0; i < 100; i++) {
            accounts.add(new Account(
                name='Test Account'+i
            ));
        }
        insert accounts;
    }
    static testmethod void testQueueable() {
        // query for test data to pass to queueable class
        Id parentId = [select id from account where name = 'Parent'][0].Id;
        List<Account> accounts = [select id, name from account where name like 'Test Account%'];
        // Create our Queueable instance
        UpdateParentAccount updater = new UpdateParentAccount(accounts, parentId);
        // startTest/stopTest block to force async processes to run
        Test.startTest();
        System.enqueueJob(updater);
        Test.stopTest();
        // Validate the job ran. Check if record have correct parentId now
        System.assertEquals(100, [select count() from account where parentId = :parentId]);
    }
}

ジョブのチューニング

  • ジョブを連続して実行する場合は、execute()メソッドから2つ目のジョブを送信する
  • 1ジョブにつきチューニングできるのは1子ジョブのみ
  • しかし階層については制限はないので、子ジョブからさらに子ジョブを呼び出すようにすれば良い。
  • 1ジョブから複数の子ジョブを呼び出すことはできない。
public class FirstJob implements Queueable {
    public void execute(QueueableContext context) {
        // Awesome processing logic here
        // Chain this job to next job by submitting the next job
        System.enqueueJob(new SecondJob());
    }
}