Data Cloudクレジット消費量の変動監視FlowをCard別にする ~集計方法の意外な落とし穴~

こんにちは。エンジニアの和田です。

以前本ブログにて、Data Cloud のクレジット消費量を Flow で監視する という記事が投稿されております。 これは、Data Cloudのクレジット消費量に応じてFlowで通知するというものでした。

当該記事では、以下の2つのFlowを作成していました。

  1. クレジット消費量の変動を監視する Flow
  2. クレジット残高の逼迫を監視する Flow

今回は、1のFlowにさらに手を加える話になります。

1のFlowでは、全体の大まかな傾向がわかれば十分として、Cardと呼ばれる種別(Einstein Requests、Conversationsなど)を区別せず、トータルのクレジット消費量で通知するようにしていましたが、 今回は、これをCard別に集計して通知するように手を加えていきます。 (なお、2のFlowはすでにCard別になっています)

「単にCard別に分類して集計するだけでは?」と思った方もいるかもしれませんが、意外な落とし穴もありましたので、ぜひ続きをお読みいただけると幸いです。

なお、Data Cloud のクレジット消費量を Flow で監視する に書かれている内容を前提に話を進めますので、まだ読んでいない方は先にそちらをお読みいただくとスムーズにご理解いただけるかと思います。

なぜCard別にするのか

今回扱うFlowは、「クレジット消費量の変動を監視する Flow」です。 詳細は Data Cloud のクレジット消費量を Flow で監視する に記載されていますが、このFlowの処理を簡単に振り返ると以下のようになります。

  1. Schedule-Triggered Flowにより毎日決まった時刻にトリガーされる
  2. 過去一定日分の消費量の平均(日数で割ったもの)を算出する
  3. 当日のクレジット消費量の合計を算出する
  4. 2に適当な乗数(例えば1.25)を掛けた値より、3の値が大きければ、クレジット消費量の変動が大きいとみなしてメールで通知する

これまでは、Cardを区別せず、全てのCardのクレジット消費量を合計して集計していました。

これだけでも、全体の大まかな傾向を把握することはできます。

しかし、これには以下の課題があります。

  • いざアラートが来ても、全体の傾向しかわからないため、具体的にどのCardについて注意が必要なのかがわからない
  • Cardによってクレジット消費量の1単位が持つ意味が異なる。例えば、Conversationsの1とEinstein Requestsの1では意味が違う。ConversationsはEinstein Requestsに比べて通常小さい値を取るので、Conversationsで重大な変化があったとしても、Einstein Requestsなど他のCardの数値に埋もれてしまい、全体の傾向だけではうまく検出できない恐れがある

そこで今回は、Card別に集計して通知するようにしました。これにより、上記の課題は次のように解消されます。

  • Card別にアラートが届くため、どのCardについて対処すべきかが明確になり、対応しやすくなる
  • ConversationsならConversations単体で集計されるため、絶対的な値が小さくても、過去の値と比較して大きな変動があればアラートが来る。他のCardの値に影響されることがなくなる

Card別にするにはどうすればよいか

ここからは、Card別に集計する方法を紹介します。

このFlowでは、DMO TenantDailyEntitlementConsumptionのレコードを集計してクレジット消費量を算出しています。このDMOには、Card名が書かれているCard Definition Developer Nameという項目があります*1。 よって、あらかじめCard名のCollectionを用意しておき、そのCollectionでループ処理を行い、Card Definition Developer Nameが当該Card名に一致したレコードについて集計すれば、Card別に集計できることになります。

では、そのCard名のCollectionはどのように用意すればよいでしょうか。 手動でCard名を入力してCollectionを作成しても構いませんが、今回は最新のTenantDailyEntitlementConsumptionのレコードから抽出して作成することにしました。

具体的には、当日のTenantDailyEntitlementConsumptionレコード群からCard Definition Developer Nameのみを抽出し、Collectionを作成します。このままでは重複が含まれるため、重複を除去して、重複なしのCard名のCollectionを作成します(詳細は後述のFlowのスクリーンショットをご参照ください)。

Cardの種類に応じて集計方法を変える

Card名のCollectionのループ内に既存のFlow処理を組み込み、ループごとに各Card名に一致するレコードを集計すれば完了...と思いきや、そこには落とし穴がありました。

Data Storage Allocation*2の当日の値が、本来の値の約3倍になってしまうのです。

TenantDailyEntitlementConsumptionを確認してみると、Data Storage Allocationに該当するレコードは1日に3件作成されていることがわかりました。

この「3」という共通点が妙に気になります。

ここでふと思うのです。Data Storage Allocationは果たしてクレジットなのだろうかと。

他のCard、例えばEinstein Requestsでは、TenantDailyEntitlementConsumptionの消費量は一定期間に消費したクレジット量を表しているはずです。したがって、1日分の消費量を求めるために合計することには意味があります。 一方、Data Storage Allocationは、その瞬間でのストレージ占有量を表すはずで、一定期間に消費したクレジット量という概念とは異なると考えられます。そのため、1日分の値を合計しても意味がないはずです。

ここで、先ほどの誤りの原因がわかりました。Data Storage Allocationの値を単純に合計してしまったために誤っていたのでした。 1日に3件のレコードがありましたから、それらを単純に合計すると、本来の値の約3倍になってしまうわけです。

つまり、同じDMO TenantDailyEntitlementConsumptionのレコードであっても、Data Storage Allocationと、Einstein Requestsなどでは、数値の意味合いが異なるのです。

では、どのように解決すればいいのでしょうか。また、どのCardがどちらの意味合いに分類されるかは、どう判断すればよいのでしょうか。

実は、DMO TenantDailyEntitlementConsumptionの項目に、Usage Aggregation Typeというものがあります。

Data Storage AllocationではUsage Aggregation TypeはMAX、Einstein RequestsではSUMとなっていました。

ここから、以下のことが推測できます。

  • Usage Aggregation TypeがMAXのものは、一定期間における値の最大値を表す
  • Usage Aggregation TypeがSUMのものは、一定期間における値の合計値を表す

つまり、TenantDailyEntitlementConsumptionのData Storage Allocationのレコードは、一定期間(1日に3件のレコードがあるため、8時間かもしれません)の最大値を表していると推測されます*3。 したがって、Data Storage Allocationの1日の代表値としては、その日のTenantDailyEntitlementConsumptionレコードの最大値を取るのが適切と考えられます。

まとめると、1日の代表値として用いるべき値は、次のようになります。

  • Usage Aggregation typeがMAXのものは、対象日のレコードの最大値
  • Usage Aggregation typeがSUMのものは、対象日のレコードの合計値

そこで、アラートの実装においては、Usage Aggregation Typeによって場合分けをし、以下の方針としました。

  • Usage Aggregation TypeがMAXの場合
    • 当日のレコードから最大値を取得する
    • 前日のレコードから最大値を取得する
    • 前日の最大値に乗数(例:1.25)を掛けた値より、当日の最大値が大きければ通知する
  • Usage Aggregation TypeがSUMの場合(従来通り)
    • 当日のレコードの合計値を算出する
    • 過去一定日数のレコードを合計し、日数で割って平均値を算出する
    • 過去の平均値に乗数(例:1.25)を掛けた値より、当日の値が大きければ通知する

Usage Aggregation TypeがMAXの場合に過去分を前日のみに限定したのは、何日も前の最大値と比較することにあまり意味がないだろうと判断したためです。

実際のFlow

Flowの全体像は以下の通りです。

変動監視Flowの全体像

順に解説します。

  1. Schedule-Triggered Flowにより、毎日決まった時刻にトリガーされます。
  2. 次のDMO取得のため待機要素を入れます。これは以前の記事の通り、Schedule-Triggered Flowではコールアウト(DMO取得も含む)の前に待機要素を入れる必要があるためです。公式ドキュメントを参考に$Flow.CuurentDateTimeを用いる方法も考えましたが、通常1分未満の待機時間とされるところ、1時間ほどかかったケースもあったため、1分待機に設定しました。
  3. 当日のTenantDailyEntitlementConsumptionのレコードを取得します。これは、Card名一覧を抽出する目的での取得です。
  4. 3から、Card名のみのCollectionをTransform要素で抽出します。
  5. 4をcardCollectionWithDuplicatesという変数に割り当てます。
  6. cardCollectionWithDuplicatesには重複が含まれるため、サブフローを呼び出して重複を除去し、cardCollectionWithoutDuplicatesに格納します。この部分はそこまで複雑な操作ではなく、今回のテーマの本質ではないのですが、念のため本記事の末尾で解説します。
  7. ここまでで、重複なしのCard名一覧CollectionであるcardCollectionWithoutDuplicatesができました。ここからはこれをループにして、各種操作を行います。
  8. 次のDMO取得のため待機要素を入れます。
  9. 当該CardのUsage Aggregation Typeを確認するため、TenantDailyEntitlementConsumptionから当該Cardに一致するレコードを1つ取得し、そのUsage Aggregation Typeを抽出します。
  10. 抽出したUsage Aggregation TypeがSUMかMAXかを判断し、それに応じてサブフローを呼び出します。

Usage Aggregation TypeがSUMの場合のサブフローは以下の通りです。

SUMの場合のサブフロー

  1. 次のDMO取得のため待機要素を入れます。このFlow自体はSchedule-Triggered Flowではありませんが、Schedule-Triggered Flowから呼ばれる場合はやはり待機要素が必要になります。
  2. 当該Cardの当日のTenantDailyEntitlementConsumptionのレコードを取得します。
  3. 2から、Transform要素を用いてUnits Consumed(今回集計に用いる数値項目)のみのCollectionを作成します。
  4. 3の合計を算出します。ループを用いる方法もありますが、Transform要素でも合計を算出できるため、今回はその方法を採用しています。詳細は公式ドキュメントをご確認ください。
  5. 4で算出した合計を、変数sumCuurentDailyComsumptionsに割り当てます。
  6. 次のDMO取得のため待機要素を入れます。
  7. 当該Cardの過去一定日数分のTenantDailyEntitlementConsumptionのレコードを取得します。
  8. 7から、Transform要素を用いてUnits ConsumedのみのCollectionを作成します。
  9. Transform要素を用いて、8の合計を算出します。
  10. 9で算出した合計を、変数sumPastDailyConsumptionsに割り当てます。
  11. 当日の合計値(5で割り当てた変数の値)が、過去の平均値(10で割り当てた変数を日数分で割った値)に乗数(例:1.25、これはinput変数としてフローの外から指定可能なようにしておく)を掛けた値より大きい場合は、メールとSlackで通知します。(前回記事ではメール通知のみでしたが、今回はSlack通知も追加しました)

Usage Aggregation TypeがMAXの場合のサブフローは以下の通りです。

MAXの場合のサブフロー

  1. 次のDMO取得のため待機要素を入れます。
  2. 当該Cardの当日のTenantDailyEntitlementConsumptionのレコードの中から、Units Consumedが最大のものを取得します。最大のものを取得するには、Units Consumedがnullでないという検索条件で、Units Consumedを降順に並べ替え、「Only the first record」を選択します。
  3. 次のDMO取得のため待機要素を入れます。
  4. 当該Cardの前日のTenantDailyEntitlementConsumptionのレコードの中から、Units Consumedが最大のものを取得します。
  5. 当日の最大値が、前日の最大値に乗数(例:1.25、これはinput変数としてフローの外から指定可能なようにしておく)を掛けた値より大きい場合は、メールとSlackで通知します。

おわりに

以上、変動アラートでCard別に集計して通知する方法について解説しました。単にCard別に分類するだけであれば複雑ではありませんが、Cardの種類によって値の意味が異なるため、場合分けして集計方法を変える必要がある、というのがポイントです。 本記事が、Data Cloudのクレジット消費を通知するFlowを作成する際の参考になれば幸いです。

最後までお読みいただきありがとうございました。

おまけ: 重複を除去するサブフロー

一般的なアルゴリズムの話になります。

重複を除去するサブフロー

  1. TextのCollection変数をinput変数とします(用途に応じて、Text以外の指定も可能です)。
  2. 与えられたCollectionを昇順に並べ替えます。
  3. 並べ替えたCollectionでループ処理を行います。
  4. 現在の値がPreviousElement(初期値は空文字列)と同じでない場合に限り、新しいCollectionに追加します。
  5. 次のループに進む前に、現在の値をPreviousElementに割り当てます。

今回は重複ありのCard名Collectionから重複なしのCollectionを作成するためにこのサブフローを通しましたが、Card名Collectionを求める目的にしては少々大げさかもしれません。動的にCard名一覧を求めることにこだわらなければ、手動でCard名を直接書いてCollectionを作成しても構いません。

*1:ただし、ここでの名前はConversationsやEinstein Requestsといった名前ではなく、DigitalEngagementConversationやEinsteinAI_Einstein1といった名前になります。詳しくは Data Cloud のクレジット消費量を Flow で監視する の「おまけ:Cardの種別の対応関係」をご参照ください。

*2:Card Definition Developer NameではDataStorageCard

*3:したがって、先ほど「その瞬間でのストレージ占有量を表しているだけのはず」と書きましたが、正確には「一定期間内でのストレージ占有量の最大値を表している」となるはずです。