2つのFor文、繰り返し処理の使い分け

今回はフロー処理の基本となる繰り返し処理について、2つにFor文を扱っていきたいと思います。

スポンサーリンク

今回使うコンポーネント

コンポーネントアイコン用途
For Each渡されたリストに対し、バッチサイズ単位で分割し繰り返し順次処理を行う
Parallel For Each渡されたリストに対し、バッチサイズ単位で分割し繰り返し並列処理を行う

繰り返し処理を行う2つのFor文

2つのFor文の違いを把握するために、実際に動作確認をしていきたいと思います。今回は47都道府県のリストを渡して、それぞれのFor文で処理してその違いを見ていきたいと思います。

For Each

渡されたリストに対し、バッチサイズに応じて分割し、順次処理をしていきます。
For Eachを使ったフローを作成していきたいと思います。フローをこのように配置します。

For Eachのフロー

①コード値でソートした都道府県のリストをPayload値に設定します。Valueには以下のように設定します。

%dw 2.0
output application/json
---
[
    "01:北海道",
    "02:青森",
    "03:岩手",
    "04:宮城",
    "05:秋田",
    "06:山形",
    "07:福島",
    "08:茨城",
    "09:栃木",
    "10:群馬",
    "11:埼玉",
    "12:千葉",
    "13:東京",
    "14:神奈川",
    "15:新潟",
    "16:富山",
    "17:石川",
    "18:福井",
    "19:山梨",
    "20:長野",
    "21:岐阜",
    "22:静岡",
    "23:愛知",
    "24:三重",
    "25:滋賀",
    "26:京都",
    "27:大阪",
    "28:兵庫",
    "29:奈良",
    "30:和歌山",
    "31:鳥取",
    "32:島根",
    "33:岡山",
    "34:広島",
    "35:山口",
    "36:徳島",
    "37:香川",
    "38:愛媛",
    "39:高知",
    "40:福岡",
    "41:佐賀",
    "42:長崎",
    "43:熊本",
    "44:大分",
    "45:宮崎",
    "46:鹿児島",
    "47:沖縄"
]

②For Eachを配置し、中にPayload値を出力するLoggerを追加します。For Eachのプロパティは以下のように設定します。今回はCollectionとBach Sizeが設定されていればOKです。今回は使用しませんか、ループカウンタの値変数を指定して使用することもできます。

For Eachの設定

上記のようにFor Eachを設定することで、Payload値に設定済みの都道府県のリストを1つずつ分割して、順次処理していくようになります。

③最後にFor Eachを抜けた先のPayload値をJSON値に変換します。この内容が最終的にレスポンスとして返されます。Valueには以下のように設定します。

%dw 2.0
output application/json
---
payload

実際のフロー実行結果は以下のようになります。

For Eachの確認結果

そして、こちらがfor文の中で出力されたログになります。ログの出力から、リストの中身が順番に処理されていることが分かります。

For文後のPayloadについては、For文開始前のPayload値と同じになります。

Parallel For Each

次はFor EachをParallel For Eachに置き換えたもので実施してみたいと思います。フローは以下のように配置します。

Parallel For Eachのフロー

フローの①と③は上記For Eachで設定した①と③のSet Payloadと内容は同じです。②のParalled For EachのプロパティはCollectionをPayloadとし、それ以外はデフォルト設定とします。

Parallel For Eachの設定

こちらはバッチサイズが指定できないため、必ず要素1つずつの処理となります。

こちらもフローを実行します。実行結果は以下のようになりました。

Parallel For Eachの確認結果

そして、こちらがログです。よく見ると、一部順番がずれていることが分かります。これは1つ1つの要素に対し、並列処理がされているためで、順番が保障されていないことが分かります。

Paralled For Eachのログ

For文後のPayload値については分割処理された分の結果のリストが返却されます。以下のような形式となり、それぞれのPayload値に各ループ処理のPayload値が入る形となります。

[
  {
    "inboundAttachmentNames": [],
    "exceptionPayload": null,
    "inboundPropertyNames": [],
    "outboundAttachmentNames": [],
    "payload": "01:北海道", (←※ここにループ処理のPayload値が入る)
    "outboundPropertyNames": [],
    "attributes": null
  },
  〜中略〜
  {
    "inboundAttachmentNames": [],
    "exceptionPayload": null,
    "inboundPropertyNames": [],
    "outboundAttachmentNames": [],
    "payload": "47:沖縄", (←※ここにループ処理のPayload値が入る)
    "outboundPropertyNames": [],
    "attributes": null
  }
]

Paralled For Eachのループ処理自体は並列処理ですが、Paralled For Each後のPayload値についてはリスト順番通りの結果が格納されていました。Muleのリファレンスには言及がなかったため真意は不明ですが、何回か確認しても順番は崩れていなかったので、おそらく順番通りにリスティングされているんじゃないかと思います。

使い分けはどうする?

For EachとParallel For Eachの動きが分かったところで、両者をどう使い分けるかがカギです。

上記の結果から、今回は処理速度という点ではFor Eachの方が早かったのですが、For文内の処理によりこのあたりは変わる可能性もあるので、いつか処理測定を実施してみようかと思います。おそらく、並列処理をしているParallel For Eachの方が最終的には高速化が見込めるのではないかと思います。

For Eachを使用する場合は、リスト内の順番が保障されているため、順番を担保したい場合にはFor Eachを使うべきかと思います。また、Salesforce系の操作はガバナ制限(Createなどは200レコード上限あり)があるため、For Eachでバッチサイズ分割してSalesforceへの通信量を減らすというのも主な使い方になります。

一方のParallel For Eachについては、高速化が一番のメリットになるかと思われます。API処理だとタイムアウト時間なども限られてくるため、できる限りのところを並列処理するところで処理時間を減らすことが主な使い方になりそうです。

それ以外で言及する点としては、エラー処理の仕方がちょっと違ってくるかと思います。For Eachは直接で処理されるため、ループ処理中のエラーが発生した場合の中断が可能ですが、Parallel For Eachの場合はエラーが発生しても並行している他のループ処理が走っている関係上、中断させることができません。この辺はエラーを許容するかどうかでどちらのFor文を使うかの判断が分かれるかと思います。

MuleSoft
スポンサーリンク

コメント