この文章は Salesforce 機械翻訳システムを使用して翻訳されました。詳細はこちらをご参照ください。
英語に切り替える

Transaction Finalizers (ベータ)

Transaction Finalizers 機能により、System.Finalizer インターフェースを使用して、キュー可能フレームワークを使う非同期 Apex ジョブにアクションを関連付けることができます。具体的な使用事例として、キュー可能ジョブが失敗した場合の回復アクションの設計が挙げられます。

Transaction Finalizers はベータ機能としてのプレビュー版であり、Salesforce とのマスターサブスクリプション契約における「サービス」には含まれません。この機能はお客様各自の裁量で使用し、購入するときは、現在正式にリリースされている製品および機能に基づいて判断してください。Salesforce はこの機能の特定期間内の正式リリースまたはリリースの有無を保証しません。また、いかなる時点でもこの機能を終了できるものとします。この機能は、評価のみを目的としており、本番環境で使用するものではありません。この機能は、そのままの状態で提供され、サポートされておらず、ここから生じる、またはこれに関連する被害または損害に対して、Salesforce はいかなる責任も負いません。すべての制約、Salesforce の権利の保留、本サービスに関する義務、および関連する Salesforce 以外のアプリケーションならびにコンテンツの条件は、この機能の使用に等しく適用されます。この機能に関するフィードバックや提案は、Trailblazer Community「TransactionFinalizers」グループに投稿してください。

メモ

このベータ機能を使用するために、スクラッチ組織で「Transaction Finalizers」機能を有効にする必要はありません。Transaction Finalizers 機能は、スクラッチ組織に制限されておらず、本番組織や Sandbox で使用したり、管理パッケージに含めたりできます。

メモ

非同期ジョブが成功または失敗したときに実行されるアクションを Transaction Finalizers よりも前に直接指定する方法はありません。SOQL クエリを使用して AsyncApexJob の状況をポーリングし、ジョブが失敗している場合にジョブを再度キューに追加することしかできません。Transaction Finalizers を使用すると、後処理アクションシーケンスをキュー可能ジョブに関連付け、ジョブの実行結果に基づいて関連するアクションを実行できます。

未対応の例外が原因で失敗したキュー可能ジョブは、Transaction Finalizer によって連続して 5 回再度キューに追加できます。この制限は、キュー可能ジョブの一連の連続した失敗に適用されます。未対応の例外が発生せずにキュー可能ジョブが完了すると、カウンタはリセットされます。

Finalizers は内部クラスとして実装できます。また、同じクラスで Queueable インターフェースと Finalizer インターフェースの両方を実装できます。

System.Finalizer インターフェース

System.Finalizer インターフェースには execute メソッドがあります。
1global void execute(System.FinalizerContext ctx) {}
このメソッドは、指定した FinalizerContext インスタンスが関連付けられているキューに入れられたジョブごとにコールされます。execute メソッド内で、キュー可能ジョブの終了時に実行されるアクションを定義できます。System.FinalizerContext のインスタンスは、Apex ランタイムエンジンによって引数として execute メソッドに挿入されます。

System.FinalizerContext インターフェース

System.FinalizerContext インターフェースには 4 つのメソッドがあります。
  • getAsyncApexJobId メソッド
    1global Id getAsyncApexJobId {}
    この Finalizer が定義されているキュー可能ジョブの ID を返します。
  • getRequestId メソッド
    1global String getRequestId {}
    要求を一意に識別する文字列である要求 ID を返し、イベントモニタリングログと関連付けることができます。AsyncApexJob テーブルと関連付けるには、代わりに getAsyncApexJobId メソッドを使用します。キュー可能ジョブと Finalizer の実行の両方が (同じ) 要求 ID を使用します。
  • getResult メソッド
    1global System.ParentJobResult getResult {}
    Finalizer が関連付けられている親非同期 Apex キュー可能ジョブの結果を表す System.ParentJobResult 列挙を返します。この列挙は、SUCCESSUNHANDLED_EXCEPTION の値を取ります。
  • getException メソッド
    1global System.Exception getException {}
    getResultUNHANDLED_EXCEPTION の場合はキュー可能ジョブが失敗し、それ以外の場合は null になる例外を返します。
アクションをキュー可能ジョブに関連付けるには、次のように FinalizerContext インターフェースを実装します。
  1. System.FinalizerContext インターフェースを実装するクラスを定義します。
  2. キュー可能ジョブの execute メソッド内で Finalizer を関連付けます。Finalizer を関連付けるには、System.FinalizerContext インターフェースを実装するインスタンス化されたクラスの引数として System.attachFinalizer メソッド使用して、このメソッドを呼び出します。
    1global void attachFinalizer(Finalizer finalizer) {}

1 つの Finalizer インスタンスのみを任意のキュー可能ジョブに関連付けることができます。execute メソッドの Finalizer の実装では、1 つの非同期 Apex ジョブ (キュー可能ジョブ、実行予定ジョブ、一括処理ジョブ) をキュ��に追加できます。Finalizer の実装ではコールアウトを使用できます。

メモ

Finalizer のログ記録の例

次の例では、Transaction Finalizers を使用して、ジョブが成功したかどうかに関係なくキュー可能ジョブからメッセージをログ記録する方法を示します。この LoggingFinalizer クラスでは、Queueable インターフェースと Finalizer インターフェースの両方を実装します。キュー可能実装では、Finalizer がインスタンス化され、Finalizer が関連付けられた後、ログメッセージをバッファリングするための addLog() メソッドが呼び出されます。LoggingFinalizer の Finalizer 実装には、キュー可能ジョブからのメッセージを Fnalizer の状態にバッファリングできる addLog(message, source) メソッドが含まれます。キュー可能ジョブが完了すると、Finalizer インスタンスは、バッファリングされたログをコミットします。Finalizer の状態は、キュー可能ジョブが失敗しても保持され、Finalizer の実装または実行内の DML で使用できます。

1public class LoggingFinalizer implements Finalizer, Queueable {
2
3  // Queueable implementation
4  // A queueable job that uses LoggingFinalizer to buffer the log
5  // and commit upon exit, even if the queueable execution fails
6
7    public void execute(QueueableContext ctx) {
8        String jobId = '' + ctx.getJobId();
9        System.debug('Begin: executing queueable job: ' + jobId);
10        try {
11            // Create an instance of LoggingFinalizer and attach it
12            LoggingFinalizer f = new LoggingFinalizer();
13            System.attachFinalizer(f);
14
15            // While executing the job, log using LoggingFinalizer.addLog()
16            DateTime start = DateTime.now();
17            f.addLog('About to do some work...', jobId);
18
19            while (true) {
20              // Results in limit error
21            }
22        } catch (Exception e) {
23            System.debug('Error executing the job [' + jobId + ']: ' + e.getMessage());
24        } finally {
25            System.debug('Completed: execution of queueable job: ' + jobId);
26        }
27    }
28
29  // Finalizer implementation
30  // Logging finalizer provides a public method addLog(message,source) that allows buffering log lines from the Queueable job.
31  // When the Queueable job completes, regardless of success or failure, the LoggingFinalizer instance commits this buffered log.
32  // Custom object LogMessage__c has four custom fields-see addLog() method.
33
34    // internal log buffer
35    private List<LogMessage__c> logRecords = new List<LogMessage__c>();
36
37    public void execute(FinalizerContext ctx) {
38        String parentJobId = '' + ctx.getAsyncApexJobId();
39        System.debug('Begin: executing finalizer attached to queueable job: ' + parentJobId);
40
41        // Update the log records with the parent queueable job id
42        System.Debug('Updating job id on ' + logRecords.size() + ' log records');
43        for (LogMessage__c log : logRecords) {
44            log.Request__c = parentJobId; // or could be ctx.getRequestId()
45        }
46        // Commit the buffer
47        System.Debug('committing log records to database');
48        Database.insert(logRecords, false);
49
50        if (ctx.getResult() == ParentJobResult.SUCCESS) {
51            System.debug('Parent queueable job [' + parentJobId + '] completed successfully.');
52        } else {
53            System.debug('Parent queueable job [' + parentJobId + '] failed due to unhandled exception: ' + ctx.getException().getMessage());
54            System.debug('Enqueueing another instance of the queueable...');
55        }
56        System.debug('Completed: execution of finalizer attached to queueable job: ' + parentJobId);
57    }
58
59    public void addLog(String message, String source) {
60        // append the log message to the buffer
61        logRecords.add(new LogMessage__c(
62            DateTime__c = DateTime.now(),
63            Message__c = message,
64            Request__c = 'setbeforecommit',
65            Source__c = source
66        ));
67    }
68}

キュー可能ジョブの再試行の例

次の例では、Finalizer で失敗したキュー可能ジョブを再度キューに追加する方法を示します。また、キュー可能チェーニング制限の 5 回までジョブを再度キューに追加できることを示します。

1public class RetryLimitDemo implements Finalizer, Queueable {
2
3  // Queueable implementation
4  public void execute(QueueableContext ctx) {
5    String jobId = '' + ctx.getJobId();
6    System.debug('Begin: executing queueable job: ' + jobId);
7    try {
8        Finalizer finalizer = new RetryLimitDemo();
9        System.attachFinalizer(finalizer);
10        System.debug('Attached finalizer');
11        Integer accountNumber = 1;
12        while (true) { // results in limit error
13          Account a = new Account();
14          a.Name = 'Account-Number-' + accountNumber;
15          insert a;
16          accountNumber++;
17        }
18    } catch (Exception e) {
19        System.debug('Error executing the job [' + jobId + ']: ' + e.getMessage());
20    } finally {
21        System.debug('Completed: execution of queueable job: ' + jobId);
22    }
23  }
24
25  // Finalizer implementation
26  public void execute(FinalizerContext ctx) {
27    String parentJobId = '' + ctx.getAsyncApexJobId();
28    System.debug('Begin: executing finalizer attached to queueable job: ' + parentJobId);
29    if (ctx.getResult() == ParentJobResult.SUCCESS) {
30        System.debug('Parent queueable job [' + parentJobId + '] completed successfully.');
31    } else {
32        System.debug('Parent queueable job [' + parentJobId + '] failed due to unhandled exception: ' + ctx.getException().getMessage());
33        System.debug('Enqueueing another instance of the queueable...');
34        String newJobId = '' + System.enqueueJob(new RetryLimitDemo()); // This call fails after 5 times when it hits the chaining limit
35        System.debug('Enqueued new job: ' + newJobId);
36    }
37    System.debug('Completed: execution of finalizer attached to queueable job: ' + parentJobId);
38  }
39}