Apex 公開コールバックで非同期の公開結果を取得する
学習の目的
この単元を完了すると、次のことができるようになります。
- 失敗したイベントを処理する Apex 公開コールバッククラスを記述する。
- コールバックインスタンスを指定してイベントを公開する。
- Apex 公開コールバッククラスのテストクラスを記述する。
公開コールバッククラスを使用して最終結果を確認する
Cloud Kicks の Vijay は Apex 公開コールバックを使用して非同期の公開エラーを捕捉することにしました。公開に失敗したイベントを捕捉して再公開するコールバックを実装します。
また、EventBus.EventPublishFailureCallback インターフェースを実装して Apex 公開コールバッククラスを記述します。実装した onFailure メソッドには、公開に失敗したイベントを取得して再公開するロジックが含まれています。カスタムコードでは、イベントの再公開を 2 回までに制限しています。マップには、公開した各イベントの UUID 値が保持され、その値が注文レコード ID に対応付けられます。この対応付けは、イベントの Order_Id__c 項目に値を設定するために使用されます。
開発者コンソールにクラスを追加する
- 開発者コンソールで、[New (新規)] | [Apex Class (Apex クラス)] をクリックします。
- クラス名として、
FailureCallbackWithCorrelationと入力します。 - デフォルトのコードを、次のクラス本文に置き換えます。
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;
}
}いくつかの注文イベントを公開して、公開コールバックを実行します。その前に、自動化プロセスユーザーのデバッグログが収集されることを確認しましょう。
デバッグログ用に自動化プロセスのトレースフラグを追加する
公開コールバックは、自動化プロセスユーザーで実行されます。コールバックの実行に対するデバッグログを収集するには、自動化プロセスのトレースフラグを追加します。
自動化プロセスユーザーのトレースフラグエントリを追加します。
- [Setup (設定)] の [Quick Find (クイック検索)] ボックスに
Debug Logs(デバッグログ) と入力し、[Debug Logs (デバッグログ)] をクリックします。 - [新規] をクリックします。
- [追跡対象エンティティ種別] で、[自動化プロセス] を選択します。
- ログを収集する期間を選択します。開始日と終了日は、デフォルトで現在の日時に設定されます。終了日入力ボックスをクリックし、カレンダーから翌日を選択して、終了日を延長します。
- [デバッグレベル] で、[新しいデバッグレベル] をクリックします。
CustomDebugLevelなどの名前を入力し、デフォルト設定のまま使用します。 - 作業内容を保存します。
コールバックが呼び出されると、その内容はデバッグログに記録されます。コールバックのログ記録では、System のデバッグログレベルを少なくとも Info に設定する必要があります。デフォルトの System のデバッグログレベルは Debug で、Info より詳細であるため、そのままで使用できます。コールバックが呼び出されると、デバッグログの行は次のようになります。
CODE_UNIT_STARTED [EXTERNAL]|platform.event.publish.callbacks.tasks.apex.ApexCallbackMethodInvoker
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);
}
}開発者コンソールでテストクラスを実行する
- 開発者コンソールで、[Test (テスト)] | [New Run (新規実行)] をクリックします。
- [Test Classes (テストクラス)] 列で、FailureCallbackWithCorrelationTest を選択します。
- テストを選択して、[Run (実行)] をクリックします。
- 完了したテスト実行をダブルクリックして、結果を詳細ビューで開きます。テストが成功したことを確認できます。
- コールバックの
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 ヘルプ: 開発者コンソール
- Salesforce ヘルプ: テスト実行の作成
- プラットフォームイベント開発者ガイド: Get the Result of Asynchronous Platform Event Publishing with Apex Publish Callbacks (Apex 公開コールバックを使用して非同期プラットフォームイベント公開の結果を取得する)
- プラットフォームイベント開発者ガイド: Test Apex Publish Callbacks (Apex 公開コールバックをテストする)
- プラットフォームイベント開発者ガイド: Publish Callback Best Practices (公開コールバックのベストプラクティス)
- プラットフォームイベント開発者ガイド: Apex Publish Callback Limits (Apex 公開コールバックの制限)
