Skip to main content
Register now for TDX! Join the must-attend event to experience what’s next and learn how to build it.

Apex 公開コールバックで非同期の公開結果を取得する

学習の目的

この単元を完了すると、次のことができるようになります。

  • 失敗したイベントを処理する Apex 公開コールバッククラスを記述する。
  • コールバックインスタンスを指定してイベントを公開する。
  • Apex 公開コールバッククラスのテストクラスを記述する。
メモ

メモ

日本語で受講されている方へ
Challenge は日本語の Trailhead Playground で開始し、かっこ内の翻訳を参照しながら進めていってください。Challenge での評価は英語データを対象に行われるため、英語の値のみをコピーして貼り付けるようにしてください。日本語の組織で Challenge が不合格だった場合は、(1) この手順に従って [Locale (地域)] を [United States (米国)] に切り替え、(2) [Language (言語)] を [English (英語)] に切り替えてから、(3) [Check Challenge (Challenge を確認)] ボタンをクリックしてみることをお勧めします。

翻訳版 Trailhead を活用する方法の詳細は、自分の言語の Trailhead バッジを参照してください。

公開コールバッククラスを使用して最終結果を確認する

Cloud Kicks の Vijay は Apex 公開コールバックを使用して非同期の公開エラーを捕捉することにしました。公開に失敗したイベントを捕捉して再公開するコールバックを実装します。

また、EventBus.EventPublishFailureCallback インターフェースを実装して Apex 公開コールバッククラスを記述します。実装した onFailure メソッドには、公開に失敗したイベントを取得して再公開するロジックが含まれています。カスタムコードでは、イベントの再公開を 2 回までに制限しています。マップには、公開した各イベントの UUID 値が保持され、その値が注文レコード ID に対応付けられます。この対応付けは、イベントの Order_Id__c 項目に値を設定するために使用されます。

Note

メモ

カスタムコードの onFailure メソッドでイベントを再公開する回数の上限を指定しないと、コールバックインスタンスを使用した再公開が 10 回までに制限されます。詳細は、『プラットフォームイベント開発者ガイド』の「Apex Publish Callback Limits (Apex の公開コールバックの制限)」を参照してください。

開発者コンソールにクラスを追加する

  1. 開発者コンソールで、[New (新規)] | [Apex Class (Apex クラス)] をクリックします。
  2. クラス名として、FailureCallbackWithCorrelation と入力します。
  3. デフォルトのコードを、次のクラス本文に置き換えます。
public class FailureCallbackWithCorrelation implements EventBus.EventPublishFailureCallback {
    public static final Integer MAX_RETRIES = 2;
    private Integer retryCounter = 0;
    private Map<String,String> uuidMap;
    

    // Callback constructor
    public FailureCallbackWithCorrelation(Map<String,String> uuidMap) {
        this.uuidMap = uuidMap;
    }
    

    public void onFailure(EventBus.FailureResult result) {
        List<String> eventUuids = result.getEventUuids();
        Map<String,String> newUuidMap = new Map<String,String>();
        

        if (retryCounter < MAX_RETRIES) {
            // Try to re-publish the failed events
            List<Order_Event__e> events = new List<Order_Event__e>();
            for (String uuid : eventUuids) {
                // Create a new event with the contents of the failed event
                Order_Event__e event = (Order_Event__e)
                    Order_Event__e.sObjectType.newSObject(null, true);
                // Fill event with the right order record Id
                event.Order_Id__c = uuidMap.get(uuid);
                events.add(event);
                

                // Use a new map since the new event will have a different uuid
                newUuidMap.put(event.EventUuid, event.Order_Id__c);
            }
            // Replace old uuid map because we no longer need its contents
            uuidMap = newUuidMap;
            

            // Republish with the same callback passed in again as 'this'
            System.debug('Republish ' + eventUuids.size() + ' failed event(s).');
            EventBus.publish(events, this);
            System.debug('Republish event for Order with Ids: ' +
                         String.join(uuidMap.values(), ', '));
            

            // Increase counter
            retryCounter++;
        } else {
            // Retry exhausted, log an error instead
            System.debug(eventUuids.size() + ' event(s) failed to publish after ' +
                         MAX_RETRIES + ' retries ' +
                         'for Order with Ids: ' + String.join(uuidMap.values(), ', '));
        }
    }
    

    // Getter methods so we can validate this in the unit test
    public Integer getRetryCounter() {
        return retryCounter;
    }
    public Map<String,String> getUuidMap() {
        return uuidMap;
    }
}

いくつかの注文イベントを公開して、公開コールバックを実行します。その前に、自動化プロセスユーザーのデバッグログが収集されることを確認しましょう。

Note

メモ

FailureCallbackWithCorrelation クラスは、onFailure メソッドでイベントを再公開します。簡素化するため、イベントの Order_Id__c 項目だけを設定します。注文 ID は、EventUuid 項目を注文 ID に対応付けるマップから取得します。通常、イベントにはさらに多くの項目が含まれます。残りの項目に値を設定する場合は、ID を使用して関連する Order (注文) レコードに対する効率的な SOQL クエリを記述し、追加の項目を取得することをお勧めします。効率的な SOQL クエリの詳細は、『プラットフォームイベント開発者ガイド』の「Running Apex within Governor Execution Limits (ガバナ実行制限内での Apex の実行)」を参照してください。それらの項目をマップに保存すると、コールバックインスタンスのサイズが大きくなり、パフォーマンスが低下する可能性があるため、お勧めしません。

デバッグログ用に自動化プロセスのトレースフラグを追加する

公開コールバックは、自動化プロセスユーザーで実行されます。コールバックの実行に対するデバッグログを収集するには、自動化プロセスのトレースフラグを追加します。

自動化プロセスユーザーのトレースフラグエントリを追加します。

  1. [Setup (設定)] の [Quick Find (クイック検索)] ボックスに Debug Logs (デバッグログ) と入力し、[Debug Logs (デバッグログ)] をクリックします。
  2. [新規] をクリックします。
  3. [追跡対象エンティティ種別] で、[自動化プロセス] を選択します。
  4. ログを収集する期間を選択します。開始日と終了日は、デフォルトで現在の日時に設定されます。終了日入力ボックスをクリックし、カレンダーから翌日を選択して、終了日を延長します。
  5. [デバッグレベル] で、[新しいデバッグレベル] をクリックします。CustomDebugLevel などの名前を入力し、デフォルト設定のまま使用します。
  6. 作業内容を保存します。

コールバックが呼び出されると、その内容はデバッグログに記録されます。コールバックのログ記録では、System のデバッグログレベルを少なくとも Info に設定する必要があります。デフォルトの System のデバッグログレベルは Debug で、Info より詳細であるため、そのままで使用できます。コールバックが呼び出されると、デバッグログの行は次のようになります。

CODE_UNIT_STARTED [EXTERNAL]|platform.event.publish.callbacks.tasks.apex.ApexCallbackMethodInvoker
Note

メモ

コールバックで作成されたすべてのレコードでは、CreatedByIdOwnerId などのシステムユーザー項目が「Automated Process」(自動化プロセス) に設定されます。OwnerId は明示的に別の値に設定できます。たとえば、タスクを特定のユーザーに割り当てるには、そのユーザーの ID をタスクの OwnerId に設定します。

EventUuid 項目値を含むイベントを作成する

イベントを公開する前に、あらかじめ EventUuid 項目値が設定されたイベントを作成しましょう。EventUuid 項目は、イベントメッセージを一意に識別します。EventUuid 項目を使用して、公開コールバックで返されたイベントと、EventBus.publish コールで公開したイベントを照合できます。

次のように SObjectType.newSObject(recordTypeId, loadDefaults) Apex メソッドを使用してイベントオブジェクトを作成することで、各イベントオブジェクトで EventUuid 項目値をシステムに生成させることができます。

Order_Event__e event = (Order_Event__e)Order_Event__e.sObjectType.newSObject(null, true);
// The EventUuid value is returned after object creation
System.debug('EventUuid: ' + event.EventUuid);
// Debug output
// EventUuid: 19bd382e-8964-43de-ac01-d5d82dd0bf78

コールバックインスタンスを指定してイベントを公開する

newSObject メソッドを使用してイベントを作成した後は、コールバックインスタンスを指定してイベントを公開できます。

このコードスニペットでは、2 件のイベントを作成して公開します。EventBus.publish コールでは、前に定義したコールバッククラス FailureCallbackWithCorrelation のインスタンスを参照します。その後、公開コールから返された即時エラーがあるかどうかを確認します。非同期エラーが発生した場合は、そのエラーによって FailureCallbackWithCorrelation クラスの onFailure メソッドが実行されます。

List<Order_Event__e> eventList = new List<Order_Event__e>();
Map<String,String> uuidMap = new Map<String,String>();
// Create event objects with prepopulated EventUuid fields.
Order_Event__e event1 = (Order_Event__e)Order_Event__e.sObjectType.newSObject(null, true);
event1.Order_Id__c='Order1 ID';
System.debug('event1 EventUuid: ' + event1.EventUuid);
// Map event UUID -> Order Id so we can look up later
uuidMap.put(event1.EventUuid, event1.Order_Id__c);
Order_Event__e event2 = (Order_Event__e)Order_Event__e.sObjectType.newSObject(null, true);
event2.Order_Id__c='Order2 ID';
System.debug('event2 EventUuid: ' + event2.EventUuid);
// Map event UUID -> Order Id so we can look up later
uuidMap.put(event2.EventUuid, event2.Order_Id__c);
// Add event objects to the list.
eventList.add(event1);
eventList.add(event2);
// Publish events with an instance of the failure callback.
FailureCallbackWithCorrelation cb = new FailureCallbackWithCorrelation(uuidMap);
List<Database.SaveResult> results = EventBus.publish(eventList, cb);
// Inspect synchronous publishing result for each event.
for (Database.SaveResult sr : results) {
    if (sr.isSuccess()) {
        System.debug('Successfully published event.');
    } else {
        for(Database.Error err : sr.getErrors()) {
            System.debug('Error returned: ' +
                        err.getStatusCode() +
                        ' - ' +
                        err.getMessage());
        }
    }
}

上記のコードスニペットを開発者コンソールで実行すると、コードスニペットのログ出力が表示されます。トリガーのログ出力は、[Setup (設定)] の自動化プロセスユーザーの [Debug Logs (デバッグログ)] ページにあります。ですが、この場合は公開が成功する可能性が高いため、onFailure メソッドは呼び出されず、コールバック実行のログは表示されません。

システムエラーはシミュレーションできないため、onFailure メソッドを実行させることはできません。ですが、テストクラスを使用すると、エラーをシミュレーションして onFailure をテストできます。テストクラスは次のセクションにあります。

公開コールバッククラスをテストする

Vijay は、テストイベントの公開失敗をシミュレーションするテストを記述します。これにより、onFailure コールバックメソッドの実行がトリガーされます。

このメソッドは、Apex テストでテストイベントまたはテストイベントのバッチの公開失敗をシミュレーションします。

Test.getEventBus().fail();

Apex テストでは、イベントメッセージはテストイベントバスで同期的に公開されます。Test.getEventBus().fail() メソッドを使用すると、そのコールの直後にイベントの公開が失敗し、イベントメッセージはテストイベントバスから削除されます。このメソッドにより、コールバッククラスの onFailure メソッドが呼び出されます。イベントメッセージの公開が失敗した場合、その失敗したイベントをプラットフォームイベントで定義された Apex トリガーが受信することはありません。

イベント配信の成功をシミュレーションするには、Test.getEventBus().deliver(); メソッドをコールするか、Test.stopTest() の後にイベントを配信します。イベントメッセージは、これらの各ステートメントの直後に配信されます。イベントが正常に配信されると、コールバッククラスの onSuccess メソッドが実行されます。この Trailhead モジュールでは onSuccess メソッドは扱いませんが、『プラットフォームイベント開発者ガイド』の「Deliver Test Event Messages (テストイベントメッセージを配信する)」で詳しく学習できます。

テストクラスの例

前にコールバッククラスを追加したときと同じ方法で、このクラスを Trailhead Playground に追加します。

このクラス例 FailureCallbackWithCorrelationTest は、FailureCallbackWithCorrelation クラスのテストクラスです。このテストクラスでは、テストイベントバスでテストイベントメッセージの公開失敗をテストする方法を示します。このテストでは、最初にイベントを作成し、コールバックを指定して公開します。次に、ループ内でそのイベントの配信を 2 回失敗させます。このテストでは、retryCounter 変数が増加したことを確認することで、コールバックがイベントの公開を 2 回再試行したことを検証します。

@isTest
public class FailureCallbackWithCorrelationTest {
   

    @isTest
    static void testFailedEventsWithFail() {
        // Create test event
        Order_Event__e event = (Order_Event__e)Order_Event__e.sObjectType.newSObject(
            null, true);
        event.Order_Id__c='dummyOrderId';
        

        // Populate map
        Map<String,String> uuidMap = new Map<String,String>();
        uuidMap.put(event.EventUuid, 'dummyOrderId');
        

        // Create callback
        FailureCallbackWithCorrelation cb = new FailureCallbackWithCorrelation(uuidMap);
        

        // Make sure retry counter is 0
        Assert.areEqual(0, cb.getRetryCounter(),
            'Newly created callback should have retry counter at 0');
        

        // Publish an event with callback
        EventBus.publish(event, cb);
		

        // If we fail all publish attempts, callback should run MAX_RETRIES times.
        // For each attempt, the callback should republish the event,
        //    increase the counter, and update the map
        String prevUuid = event.EventUuid;
        for (Integer i = 1; i <= FailureCallbackWithCorrelation.MAX_RETRIES; i++) {
            Test.getEventBus().fail();
            Assert.areEqual(i, cb.getRetryCounter(), 'Retry counter should be ' + i);
            Assert.areEqual(1, cb.getUuidMap().size(), 'Map size should be 1');
            String currUuid = (new List<String>(cb.getUuidMap().keySet())).get(0);
            Assert.areNotEqual(prevUuid, currUuid,
                'Map should be updated with newly created event Uuid');
            Assert.areEqual('dummyOrderId', cb.getUuidMap().get(currUuid),
                'Map value should be the original Order Id');
            prevUuid = currUuid;
        }
        

        // If we publish another failed event, callback should not retry.
        Order_Event__e event2 = (Order_Event__e)Order_Event__e.sObjectType.newSObject(
            null, true);
        event2.Order_Id__c='dummyOrderId';
        EventBus.publish(event, cb);
        Test.getEventBus().fail();
        Assert.areEqual(FailureCallbackWithCorrelation.MAX_RETRIES, cb.getRetryCounter(),
                        'Retry counter should still be ' +
                         FailureCallbackWithCorrelation.MAX_RETRIES);
    }
}

開発者コンソールでテストクラスを実行する

  1. 開発者コンソールで、[Test (テスト)] | [New Run (新規実行)] をクリックします。
  2. [Test Classes (テストクラス)] 列で、FailureCallbackWithCorrelationTest を選択します。
  3. テストを選択して、[Run (実行)] をクリックします。
  4. 完了したテスト実行をダブルクリックして、結果を詳細ビューで開きます。テストが成功したことを確認できます。
  5. コールバックの onFailure メソッドが呼び出されたことを確認するには、デバッグログを開きます。[Logs (ログ)] タブをクリックして、最新のログエントリをダブルクリックします。デバッグログには、イベントが 2 回再公開され、その公開が失敗したことが表示されます。では、デバッグログに記録された操作の一部を見てみましょう。
15:23:26.0 (253774507)|METHOD_ENTRY|[30]||eventbus.TestBroker.fail()
15:23:26.0 (303029804)|CODE_UNIT_STARTED|[EXTERNAL]|platform.event.publish.callbacks.tasks.apex.ApexCallbackMethodInvoker
…
15:23:26.0 (322897823)|USER_DEBUG|[34]|DEBUG|Republish 1 failed event(s).
…
15:23:26.0 (326264814)|DML_BEGIN|[35]|Op:Insert|Type:Order_Event__e|Rows:1
…
15:23:26.0 (337113675)|USER_DEBUG|[36]|DEBUG|Republish event for Order with Ids: dummyOrderId
…
15:23:26.0 (347183100)|CODE_UNIT_FINISHED|platform.event.publish.callbacks.tasks.apex.ApexCallbackMethodInvoker
…
15:23:26.0 (356601358)|METHOD_ENTRY|[30]||eventbus.TestBroker.fail()
15:23:26.0 (360100652)|CODE_UNIT_STARTED|[EXTERNAL]|platform.event.publish.callbacks.tasks.apex.ApexCallbackMethodInvoker
…
15:23:26.0 (364909736)|USER_DEBUG|[34]|DEBUG|Republish 1 failed event(s).
15:23:26.0 (366375150)|DML_BEGIN|[35]|Op:Insert|Type:Order_Event__e|Rows:1
…
15:23:26.0 (379841318)|USER_DEBUG|[36]|DEBUG|Republish event for Order with Ids: dummyOrderId
15:23:26.0 (379895350)|VARIABLE_ASSIGNMENT|[40]|this.retryCounter|2|0x361f18cc
15:23:26.0 (386397600)|CODE_UNIT_FINISHED|platform.event.publish.callbacks.tasks.apex.ApexCallbackMethodInvoker
…
15:23:26.0 (402851235)|USER_DEBUG|[43]|DEBUG|1 event(s) failed to publish after 2 retries for Order with Ids: dummyOrderId
15:23:26.0 (404102455)|CODE_UNIT_FINISHED|platform.event.publish.callbacks.tasks.apex.ApexCallbackMethodInvoker

詳細とコード例については、『プラットフォームイベント開発者ガイド』の「Test Apex Publish Callbacks (Apex 公開コールバックをテストする)」を参照してください。

公開コールバックのベストプラクティス

Apex 公開コールバックを実装する際は、次のベストプラクティスに留意してください。

  • SObjectType.newSObject で作成した同じイベントオブジェクトを再公開しないでください。
  • コールバックを使用する場合は、個々のイベントではなくイベントのリストを公開してください。
  • イベントのリストを公開するときに、SObject 汎用型を使用しないでください。その代わりに、リストのデータ型として特定のプラットフォームイベント型を使用してください。
  • コールバックインスタンスのサイズを小さくするために、コールバック内のイベント UUID のマップを小さくしてください。コールバックインスタンスのサイズを小さくすると、パフォーマンスが向上し、公開コールバック全体での使用量が累積して制限に達する事態も避けやすくなります。この単元の FailureCallbackWithCorrelation クラスでは、イベント UUID をレコード ID に対応付けています。このレコード ID を照会して、残りのイベント項目を設定できます。また、イベント全体をマップ値として保存したい場合は、そのイベントに多くの項目がなく、各項目サイズが小さいことを確認してください。
  • onSuccess メソッドで公開成功を追跡する場合は、慎重に判断してください。ほとんどの公開コールは通常成功するため、公開成功を追跡する必要はあまりありません。大量のイベントが公開される場合、公開成功の処理はパフォーマンスや Apex 制限に影響する可能性があります。公開成功の処理は、本当に必要な場合にだけ行うことをお勧めします。onFailure メソッドで公開失敗を処理する場合は、こうしたパフォーマンスへの影響はほとんどありません。公開失敗はまれにしか発生しないためです。

これらのベストプラクティスの詳細は、『プラットフォームイベント開発者ガイド』の「Publish Callback Best Practices (公開コールバックのベストプラクティス)」を参照してください。

これで、Apex 公開コールバックの使用方法とテスト方法を理解できました。イベントの公開はストーリーの半分でしかありません。残りの半分は、イベントを受信するサブスクライバーを記述することです。次の単元では、堅牢で回復力のあるプラットフォームイベントトリガーを記述するためのベストプラクティスを学習します。

リソース

Salesforce ヘルプで Trailhead のフィードバックを共有してください。

Trailhead についての感想をお聞かせください。[Salesforce ヘルプ] サイトから新しいフィードバックフォームにいつでもアクセスできるようになりました。

詳細はこちら フィードバックの共有に進む