フローが増えたら、合わせてクエリ回数が爆増したので調べた

# はじめに
現在携わっている案件で、環境の大幅整備が始まりました。
ざっくり書くと、下記の作業をしています。

- 親オブジェクトがほぼすべての項目を持っているので、項目を持つオブジェクトを適切に親子に割り振る
（親が400項目、子が60項目ほどでした・・）
- ワークフロールールをフローに変換する

この作業に伴い、ひいこら言いながら諸々修正したのですが・・。
いざテストクラスを実行すると`Too Many 101 Error`が発生。

結論としては**フローが増えたせい**だったのですが・・。

せっかくなので、増え方について調べました。

# 前提
オブジェクトA、オブジェクトBを作成。  
それぞれにトリガーやらフローやらを作成し、クエリ回数がどう増えていくかを検証する。

# 検証
## トリガーのみ
オブジェクトAのトリガーを下記の通り作成した。

```java:ObjectATrigger.trigger
trigger ObjectATrigger on ObjectA__c (before insert, before update, after insert, after update) {

	List<User> uList = [SELECT Id FROM User];
}
```

そのまま、1件レコードをインサートしたときに結果がこんな感じ。  
```java
System.debug(Limits.getQueries()); // 0
insert new ObjectA__c();
System.debug(Limits.getQueries()); // 2
```

beforeで1回、afterで1回なので想定通り。

## フローを1つ作成
beforeのタイミングで動作する、ユーザーを取得するだけのフローを作成。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2603116/608fca0a-73c4-9325-161c-3f5f41c9c26a.png)

結果は下記の通り。
```java
System.debug(Limits.getQueries()); // 0
insert new ObjectA__c();
System.debug(Limits.getQueries()); // 3
```

取得クエリが1つ増えたわけだから、まあこれも想定通り。

## フローを2つに増やす
上と全く同じ動きのフローをもう一つ作成する。

結果は下記の通り。
```java
System.debug(Limits.getQueries()); // 0
insert new ObjectA__c();
System.debug(Limits.getQueries()); // 4
```

取得クエリが更に1つ増えたわけだから、これも想定通り。

## フローを、afterのタイミングでオブジェクトBをinsertするフロー1つのみにする
上で作成したフローは一旦無効化し、afterのタイミングでオブジェクトBをinsertするフローを用意。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2603116/1e4ad743-f6ec-2b79-0185-d013ed05848e.png)

```java
System.debug(Limits.getQueries()); // 0
insert new ObjectA__c();
System.debug(Limits.getQueries()); // 4
```
ObjectAトリガーの、before/afterで2回  
同じくObjectBトリガーのbefore/afterで2回の計4回。

## afterのタイミングで同一のオブジェクトBをupdateするフロー2つにする
ちょっと飛んで、トリガーとは別の同一レコードに対して、2回更新処理をさせてみる。  
同一のレコード更新を保証するため、オブジェクトAにオブジェクトBへの参照項目を追加した。  
（何も考えず登録したら、だめな例の典型みたいな名前になった）

更新内容はどちらも同じく`所有者 = User.Id`

```java
System.debug(Limits.getQueries()); // 0
insert new ObjectA__c(
    B__c = b.Id
);
System.debug(Limits.getQueries()); // 8
```

- オブジェクトAのbefore/afterで2回
- オブジェクトBのbefore/afterで2回 * 更新が2回 = 4回

の合計6回だと思っていた・・が、実際には**8回**。

### 推測
今回作成したフローがこちら。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2603116/43f71136-a6d4-3cff-c600-2713f094a57f.png)

コンポーネントとしては更新一つだが、おそらく内部的には`SOQLで更新対象を取得` → `update`という流れなのだと思う。  
なので

- オブジェクトAのbefore/afterで2回
- オブジェクトAのフローで2回
- オブジェクトBのbefore/afterで2回 * 更新が2回 = 4回

の計8回になったものと思われる。

## おまけ：無限ループっぽくするとどうなる？
上のフローはそのままに、オブジェクトBのafterでオブジェクトAを更新するフローを作成する。  
作成/更新時に自分に紐づくオブジェクトAを更新するだけの、シンプルなフロー。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2603116/cc3fce16-1ed7-0a8a-fcbf-3f9071722821.png)

```java
System.debug(Limits.getQueries()); // 0
insert new ObjectA__c(
    B__c = b.Id
);
System.debug(Limits.getQueries()); // 14
```

無限ループとはならず、途中で止まった。  
ログを見る限りだと、こんな感じ。

1. オブジェクトAが登録される
1. 1つめのフローによって、Bが更新される
1. 上のフローでの更新をトリガーに、Aが更新される  
このとき、Bは更新されない
1. 2つめのフローによって、Bが更新される
1. 上のフローでの更新をトリガーに、Aが更新される  
このとき、Bは更新されない

ということで、**更新処理が自分に帰ってきた場合、更新せずに止まる**ようになってるっぽい。  
Salesforceくんは賢いねぇ。

## おまけのおまけ：トリガーも絡ませるとどうなる？
上の状態と合わせて、トリガー側でもオブジェクトAを更新させてみる。  
```java:ObjectBTrigger.trigger
trigger ObjectBTrigger on ObjectB__c (before insert, before update, after insert, after update) {

	System.debug('ObjectBTrigger:start ' + Limits.getQueries());
	List<User> uList = [SELECT Id FROM User];

	if (Trigger.isAfter) {
		update [SELECT Id FROM ObjectA__c WHERE B__c IN :Trigger.new];
	}
}
```

```java
System.debug(Limits.getQueries()); // 0
insert new ObjectA__c(
    B__c = b.Id
);
System.debug(Limits.getQueries()); // 20
```

フローと同様に、帰ってきた分の更新がかかっていないみたい。  
なので、増分としては予想通り。  

# おわりに

- 更新処理を複数フローに用意した場合、まとめて一回にするような処理はない
- 「レコードを更新」でトリガー以外のレコードを更新すると、実際には一度SOQLを実行しているため、カウントが増える点に注意
    - よく考えると当たり前ではある・・。
- 相互で更新させても無限ループにはならないけど、カウントはもりもり増えるから気をつけよう

上に書いたものは、すべてトリガーでイメージしたら当たり前ではあるんですが・・。
それを踏まえて考えると、フローをたくさん用意するのってリスクがかなり大きい気がします。

じゃあもう、やっぱトリガーが良い・・。