Azure StreamAnalytics ユーザー定義関数の実行で嵌った話

こんにちは、GxPの千野(@mchino-gxp)です。  
この記事は[グロースエクスパートナーズ アドベントカレンダー](https://qiita.com/advent-calendar/2020/gxp)の4日目の記事となります。

今回はAzure StreamAnalyticsのユーザー定義関数を使用して開発していた時、javascriptの関数定義の仕方が原因で嵌ったのでそのことについて話を書こうと思います。

## 問題が起きたときの状況

- Azure IoTEdgeから情報を収集しAzure IoTHub(以下IoTHub)、StreamAnalytics(以下SA)を経由してCosmosDBにIoTEdgeから収集した情報を登録する構成です。
![09_azure_architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906135/67e7d7e9-cafb-1e8a-3f1a-cadf57362f82.png)

- CosmosDBに登録する前にIoTEdgeから収集したデータを利用してCosmosDBに登録する新しいデータを生成する必要が出てきました。
- ちょうどSAを経由してCosmosDBにデータの登録を行っていたのでSAのユーザー定義関数を利用してデータの生成を行うことにしました。

## 何が起こったのか

以下のようにSAにユーザー定義関数を設定している状態の時SAのクエリからユーザー定義関数Aが実行されるとユーザー定義関数Bに定義されているhuga関数が実行されてしまう現象が発生しました。本来ならユーザー定義関数Aに記載されている関数を実行してほしかったのですが…
![01_udf.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906135/5c6fd9e9-27c1-448f-631d-fa4daf64885d.png)

```javascript
// hogeA
// ユーザー定義関数A
function hogeA(arg1) {
    return huga(arg1)
}

function huga(arg1) {
    return 'success hogeA'
}
```

```javascript
// hogeB
// ユーザー定義関数B
function hogeB(arg1) {
    return huga(arg1)
}

function huga(arg1) {
    return 'success hogeB'
}
```

**ユーザー定義関数Aの実行結果**

テスト結果欄の表示からユーザー定義関数Bに定義した関数が実行されたことが確認できます。
![02_udf_action.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906135/7a575859-ea84-fda5-333b-612efd5b6d96.png)

Microsoftが公開しているSAのドキュメントを確認してみても異なるユーザー定義関数に同一名の関数を定義した場合の挙動について記載はなかったのでとりあえず動かして確認してみることにしました。

## 調査結果

以下のような条件で実際にユーザー定義関数を動作させて確認してみることにしました。

| 関数実行パターン | 期待結果             |
|------------------|--------------------------|
| ユーザー定義関数A,B作成 => Aを実行        | **ユーザー定義関数Aの関数**が呼び出される |
| ユーザー定義関数A,B作成 => Bを実行        | **ユーザー定義関数Bの関数**が呼び出される |
| ユーザー定義関数A,B,C作成 => Aを実行        | **ユーザー定義関数Aの関数**が呼び出される |
| ユーザー定義関数A,B,C作成 => Bを実行        | **ユーザー定義関数Bの関数**が呼び出される |
| ユーザー定義関数A,B,C作成 => Cを実行        | **ユーザー定義関数Cの関数**が呼び出される |

５つのパターンでユーザー定義関数を実行してみて結果は以下のようになりました。

| 関数実行パターン | 呼び出し結果             |
|------------------|--------------------------|
| ユーザー定義関数A,B作成 => Aを実行        | <font color="red">**ユーザー定義関数Bの関数**</font>が呼び出される |
| ユーザー定義関数A,B作成 => Bを実行        | ユーザー定義関数Bの関数が呼び出される |
| ユーザー定義関数A,B,C作成 => Aを実行        | <font color="red">**ユーザー定義関数Cの関数**</font>が呼び出される |
| ユーザー定義関数A,B,C作成 => Bを実行        | <font color="red">**ユーザー定義関数Cの関数**</font>が呼び出される |
| ユーザー定義関数A,B,C作成 => Cを実行        | ユーザー定義関数Cの関数が呼び出される |

驚いたことに当初期待していた結果と異なりユーザー定義関数BもしくはCに定義されている関数のみが実行される結果となりました。
この事から複数のユーザー定義関数で同じ関数名の関数を定義してかつ呼び出しを行うと<font color="red">**ユーザー定義関数の管理画面に表示されている中で一番最後のユーザー定義関数に定義されている関数が呼び出される**</font>ようです。

以下に実際にSAで実行した結果を載せておきます。

### ユーザー定義関数A,B作成 => Aを実行した結果

ユーザー定義関数Bに定義されている関数が呼び出されていることが確認できます。
![02_udf_action.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906135/7a575859-ea84-fda5-333b-612efd5b6d96.png)

### ユーザー定義関数A,B作成 => Bを実行した結果

ユーザー定義関数Bに定義されている関数が呼び出されていることが確認できます。
![04_udf_action.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906135/f71f1504-6d3a-75d2-dd05-e8d260b7beaa.png)

### ユーザー定義関数A,B,C作成 => Aを実行した結果

ユーザー定義関数Cに定義されている関数が呼び出されていることが確認できます。
![05_udf_action.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906135/49007c46-b1f3-91c1-5a87-7db56f64e79d.png)

### ユーザー定義関数A,B,C作成 => Bを実行した結果

ユーザー定義関数Cに定義されている関数が呼び出されていることが確認できます。
![06_udf_action.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906135/d426c085-79d5-7c2b-0a61-9aefc71b2ddb.png)

### ユーザー定義関数A,B,C作成 => Cを実行した結果

ユーザー定義関数Cに定義されている関数が呼び出されていることが確認できます。
![07_udf_action.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906135/3500a3ee-0387-a9ef-d4ed-9247e2d76f70.png)

## 解決方法

同一関数名にしていると起きる現象なので単純に関数名をユニークにすることで対応できます。
ユーザー定義関数Aの関数定義を以下のように変更することで問題なく動作します。

```javascript
// hogeA
// ユーザー定義関数A
function hogeA(arg1) {
    return hugaA(arg1)
}

function hugaA(arg1) {
    return 'success hogeA'
}
```

**ユーザー定義関数Aの実行結果**

ユーザー定義関数Aに定義した関数が実行されていることが確認できます。
![08_udf_action_complete.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906135/86c16c3b-cf90-225a-5cb3-be510584e824.png)


## まとめ

以上が私がSAの開発中に遭遇した嵌った話になります。Azureでは何かとドキュメントに乗っていない仕様があったりそもそもドキュメントが分かりづらかったりしますが、自分で検証できるところはどんどん検証して共有していこうと思います。