Queueable Apex を使用したプロセスの制御

学習の目的

この単元を完了すると、次のことを理解できるようになります。
  • Queueable インターフェースを使用するケース。
  • queueable メソッドと future メソッドの違い。
  • Queueable Apex の構文。
  • queueable メソッドのベストプラクティス。

一緒にトレイルを進みましょう

インストラクターと一緒にこの手順を進めますか? 次の動画をご覧ください。これは Trailhead Live の「Trail Together」(一緒にトレイル) シリーズの一部です。

(この動画は 52:38 の時点から始まります。戻して手順の最初から見直す場合はご注意ください。)

Queueable Apex

Winter '15 でリリースされた Queueable Apex は、future メソッドのスーパーセットでちょっとした趣向が凝らされています。future メソッドの簡便性と Apex 一括処理の機能性に着目して、この 2 つを合体させた Queueable Apex を編み出しました。この Apex は、プラットフォームによって逐次化されるクラス構造で、start と finish メソッドを使用しない簡潔なユーザインターフェースを備え、プリミティブ引数以外も利用できます。シンプルな System.enqueueJob() メソッドでコールされ、監視可能なジョブ ID を返します。これに勝るものはありません。

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

queueable と future の比較

queueable メソッドは機能的に future メソッドと同等であるため、ほとんどの場合は future メソッドの代わりに queueable メソッドを使用できます。ただし、すべての future メソッドに戻ってすぐにリファクタリングする必要があるわけではありません。

queueable メソッドではなく future メソッドを使用する理由は、機能が同期実行されることもあれば、非同期実行されることもある場合です。queueable クラスに変換するよりも、この方法でメソッドをリファクタリングするほうがはるかに簡単です。既存のコードの一部を非同期実行に移す必要があることに気付いた場合は、この方法が便利です。次のように、同期メソッドをラップする同様の future メソッドを作成するだけすみます。

@future
static void myFutureMethod(List<String> params) {
    // call synchronous method
    mySyncMethod(params);
}

キュー可能構文

Queueable Apex は、Queueable インターフェースを実装するだけで使用できます。

public class SomeClass implements Queueable {
    public void execute(QueueableContext context) {
        // awesome code here
    }
}

サンプルコード

一般的なシナリオは、いくつかの sObject レコードを取り、外部の REST エンドポイントへのコールアウトの実行など一定の処理を実行するか、何らかの計算を実行してからデータベースで非同期で更新します。@future メソッドはプリミティブデータ型 (またはプリミティブの配列あるいはコレクション) に限定されているため、Queueable Apex が理想的な選択肢となります。次のコードは、取引先レコードのコレクションを取り、各レコードに parentId を設定してから、データベースのレコードを更新します。

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);

キュー可能クラスを実行のために送信すると、ジョブはキューに追加され、システムリソースが使用可能になると処理されます。

新しいジョブ ID を使用して、[Apex Job (Apex ジョブ)] ページから、またはプログラムで AsyncApexJob を照会する方法で、進行状況を監視できます。

SELECT Id, Status, NumberOfErrors FROM AsyncApexJob WHERE Id = :jobID

Queueable Apex のテスト

次のサンプルコードは、テストメソッドでキュー可能ジョブの実行をテストする方法を示しています。Apex 一括処理のテストとよく似ています。キュー可能プロセスがテストメソッド内で実行されるようにするには、ジョブを Test.startTest から Test.stopTest のブロック内のキューに送信する必要があります。システムは、テストメソッドで開始されたすべての非同期プロセスを、Test.startTest ステートメントの後に同期して実行します。次に、テストメソッドが、ジョブで更新された取引先レコードを照会して、キュー可能ジョブの結果を検証します。

@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]);
    }
}

ジョブのチェーニング

Queueable Apex の利点の 1 つがジョブのチェーニングです。ジョブを連続して実行する必要が生じた場合、Queueable Apex を使用すると大幅に手間を省けます。ジョブを別のジョブにチェーニングするには、キュー可能クラスの execute() メソッドから 2 つ目のジョブを送信します。実行中のジョブから追加できるジョブは 1 つのみです。つまり、親ジョブごとに 1 つの子ジョブしか存在できません。たとえば、2 つ目のクラスが Queueable インターフェースを実装する SecondJob という名前である場合、このクラスを execute() メソッドのキューに次のように追加できます。

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());
    }
}

この場合も、テストのパターンが若干異なります。Apex テストでキュー可能ジョブをチェーニングすることはできません。実行するとエラーが発生します。厄介なエラーを回避するために、ジョブをチェーニングする前に Test.isRunningTest() をコールすると、Apex がテストコンテキストで実行されているかどうかを確認できます。

留意事項

Queueable Apex は最新の優れたツールですが、いくつかの注意点があります。
  • キュー内のジョブを実行すると、非同期 Apex メソッド実行の共有制限値に 1 回カウントされます。
  • 1 つのトランザクションで System.enqueueJob を使用してキューに追加できるのは、最大 50 ジョブです。
  • ジョブをチェーニングするとき、実行中のジョブから System.enqueueJob で追加できるジョブは 1 つのみです。つまり、親キュー可能ジョブごとに 1 つの子ジョブしか存在できません。同じキュー可能ジョブからの複数の子ジョブを開始することはできません。
  • チェーニングされたジョブの深度に制限はありません。つまり、1 つのジョブから別のジョブにチェーニングし、このプロセスを新しい子ジョブごとに繰り返して新しい子ジョブにリンクできます。ただし、Developer Edition 組織やトライアル組織の場合は、チェーニングされたジョブの最大スタック深度は 5 です。つまり、ジョブのチェーニングを 4 回行うことができ、チェーン内のジョブ数は最初の親キュー可能ジョブを含めて最大 5 個です。

リソース

メモ

このモジュールは Salesforce Classic 向けです。ハンズオン組織を起動するときには、Salesforce Classic に切り替えてから、この Challenge を実行してください。

無料で学習を続けましょう!
続けるにはアカウントにサインアップしてください。
サインアップすると次のような機能が利用できるようになります。
  • 各自のキャリア目標に合わせてパーソナライズされたおすすめが表示される
  • ハンズオン Challenge やテストでスキルを練習できる
  • 進捗状況を追跡して上司と共有できる
  • メンターやキャリアチャンスと繋がることができる