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

Newer Version Available

This content describes an older version of this product. View Latest

Transaction Finalizers (パイロット)

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

TransactionFinalizers 機能は、パイロットプログラムとしてスクラッチ組織でのみ使用できます。組織では作成時にこの機能を有効にしている必要があります。この機能は変更される可能性があり、パイロット期間中は本番組織で使用できません。パイロットプログラムは変更される場合があります。この機能は、Salesforce がドキュメント、プレスリリース、または公式声明で正式リリースを発表しない限り、正式リリースされません。特定期間内の正式リリースあるいはリリースの有無は保証できません。正式リリースされた製品および機能に基づいてのみ購入をご決定くださ��。この機能に関するフィードバックや提案は、Trailblazer Community「TransactionFinalizers」グループに投稿してください。

メモ

Finalizer は、現在パイロットとして提供されており、この機能は有効化されたスクラッチ組織でしか使用できないため、パッケージ化しないでください。

メモ

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

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 の実装ではコールアウトを使用できます。

メモ

1public class SampleQueueable999 implements Queueable, IRetryTracker {
2
3   // Tracks number of consecutive times this is re-enqueued
4   protected integer enqueueRetryCount;
5
6   public SampleQueueable999() {
7      enqueueRetryCount = 0;
8   }
9
10   public void setRetryCount(integer retryCount) {
11      enqueueRetryCount = retryCount;
12   }
13
14   public void execute(QueueableContext ctx) {
15      RequeueLoggingFinalizer f = new RequeueLoggingFinalizer(
16                                                'SampleQueueable999', // Pass (fully qualified) current type name
17                                                enqueueRetryCount
18                                             );
19      System.attachFinalizer(f);
20
21      // While executing the job, log using RequeueLoggingFinalizer.addLog(String)
22      DateTime start = DateTime.now();
23      f.addLog('About to do some work...');
24
25      // Do some work here 
26      doSomeWork();
27
28      Long msElapsedTime = (DateTime.now().getTime() - start.getTime());
29      f.addLog('work completed in, ' + String.valueOf(msElapsedTime)  + 'ms');
30
31      // Uncomment below line to see retry logic (in finalizer) in action
32      // Throw new SomeXQueueableException('Deliberate Exception Message');
33   }
34
35   private void doSomeWork() {
36      // Do a call out
37      // Do some data proccessing
38      Integer delay = 1 * 10 * 1000;
39      Long begintime = DateTime.now().getTime();
40      while(DateTime.now().getTime() - begintime < delay);
41   }
42
43   // Custom exception
44   public class SomeXQueueableException extends Exception { }
45}
1public interface IRetryTracker {
2   void setRetryCount(integer retryCount);
3}
1/**
2 * RequeueLoggingFinalizer is a finalizer that commits log buffer from its parent queueable job.
3 * If the parent queueable job failed, it will re-enqueue a new instance of parent queueable job.
4 * If the parent queueable job failed for 3 consecutive re-enqueues... then it will stop further enqueue.
5 *
6 * NOTE: The parent queueable job can be re-enqueued iff it implements IRetryTracker interface (in addition
7 *       to implementing System.Queueable interface)
8 */
9public class RequeueLoggingFinalizer implements Finalizer {
10
11    protected String sourcename = 'RequeueLoggingFinalizer'; // For log records
12
13    // Tracks number of consecutive times the parent queueable is requeued on failure
14    protected Integer enqueueRetryCount;
15
16    // Parent queueable class type name
17    protected String concreteQueueableTypeName;
18
19    // Internal log buffer
20    // LogMessage__c is a custom object with datetime, log message (long text area of max length 131,072), source (text 255), request (text 255) fields
21    private List<LogMessage__c> logRecords = new List<LogMessage__c>();
22
23    public RequeueLoggingFinalizer(String queueableTypeNameStr, Integer retryCount) {
24        enqueueRetryCount = retryCount;
25        concreteQueueableTypeName = queueableTypeNameStr;
26    }
27
28    /**
29     * Logs a message that will be committed to db even in conditions when Queueable's transaction is rolled back
30     */
31    public void addLog(String message) {
32      // Append the log message to the buffer
33      logRecords.add(new LogMessage__c(
34                           datetime__c = DateTime.now(),
35                           message__c = message,
36                           request__c = '', // set this before commit
37                           source__c = this.sourcename
38         ));
39    }
40
41    /**
42     * Commits the log buffer and re-enqueues failed queueable (iff type is IRetryTracker)
43     */
44    public void execute(FinalizerContext ctx) {
45
46        String reqId = ctx.getRequestId();
47        String jobId = Id.valueOf(ctx.getAsyncApexJobId());
48        System.debug('Executing Finalizer that was attached to Queueable job (job id: ' + jobId + ', request id: ' + reqId + ')');
49
50        // Fix the getRequestId id in the log records
51        System.debug('Updating request id on ' + logRecords.size() + ' log records');
52        for (LogMessage__c log : logRecords) {
53            log.Request__c = reqId;
54        }
55
56        // Commit the log buffer
57        System.debug('committing log records to database');
58        Database.insert(logRecords, false);
59
60        // Debug log the status of the parent queueable job and
61        // if it failed, and implementing IRetryTracker interface, retry (re-enqueue) up to 3 times
62
63        if (ctx.getResult() == ParentJobResult.SUCCESS) {
64            System.debug('Parent Queueable (job id: ' + jobId + '): completed successfully!');
65
66        } else { // Queueable failed
67            System.debug('Parent Queueable (job id: ' + jobId + '): FAILED!');
68            System.debug('Parent Queueable Exception: ' + ctx.getException().getMessage());
69
70            Type systemQueueableType = Type.forName('System.Queueable');
71            Type retryTrackerType = Type.forName('IRetryTracker');
72            Type concreteQueueableType = Type.forName(concreteQueueableTypeName);
73
74            if (systemQueueableType.isAssignableFrom(concreteQueueableType) &&
75                retryTrackerType.isAssignableFrom(concreteQueueableType)) {
76
77                enqueueRetryCount++;
78                if (enqueueRetryCount <= 3 /* Maximum retry limit */) {
79
80                    // Create a new queueable for retry
81                    Object obj = concreteQueueableType.newInstance();
82
83                    // Pass the current retry count to the queueable so it can
84                    // pass that information to the new instance of the
85                    // finalizer that it will attach
86                    IRetryTracker retryTracker = (IRetryTracker) obj;
87                    retryTracker.setRetryCount( enqueueRetryCount );
88
89                    System.Queueable queueableObj = (System.Queueable) obj;
90                    ID newJobId = System.enqueueJob( queueableObj );
91                    System.debug('Re-enqueued a new instance of queueable \'' +
92                      concreteQueueableTypeName + '\' with job ID: ' + newJobId);
93                }
94            }
95        }
96    }
97}