Node-REDのはまりどころと対策

Node-REDに良い意味ではまっていますが、プログラム中には悪い意味で何度かはまっています。具体的には以下の様なNode-REDに特有のバグです。

* ファンクションノードから期待するmsgオブジェクトが出力されない
* メモリー不足
* CPU不足

# ファンクションノードから期待するmsgオブジェクトが出力されない
javascriptを書かなくても実装できるNode-REDはですが、ちょっと複雑な処理になるとファンクションノードにjavascriptで書いた方が簡単な場合があります。しかし、気を付けないと思い通りのmsgオブジェクトが出力されない場合があります。
## returnがない
ファンクションノードを配置すると、何もしなくても「return msg;」と書かれていて、受け取ったメッセージオブジェクトを次のノードに渡すようになっています。
手打ちをしていると誤って消すことは少ないと思いますが、コピー＆ペーストした際にreturnがなくなってしまうことがあります。
このバグは編集直後に動かすと気づきやすいですが、色々なノードを触っていると、デバッグノードを色々なところにつけてmsgがどこまで出ているかを確認する必要があり、結構はまります。
## 処理の前にreturnしてしまう

```
オブジェクト.非同期メソッド(params, function(err, data) {
    if(err){ // an error occurred
        msg.payload = "Error"
        msg.err = err;
        node.send(msg);
    }
    else { // successful response
        msg.payload = "OK";
        node.send(msg);
    }
});
return msg;
```

非同期処理の前にmsgオブジェクトを返してしまいます。時間のかかる非同期メソッドや、この後のエラー処理で止まるようにしていると、処理が一切されないように見えるので、戸惑ってしまいます。

## コールバックにreturn

```
オブジェクト.非同期メソッド(params, function(err, data) {
    if(err){ // an error occurred
        msg.payload = "Error"
        msg.err = err;
        return msg;
    }
    else { // successful response
        msg.payload = "OK";
        return msg;
    }
});
```

コールバックメソッドのreturnは呼び出し元の非同期メソッドに返るだけです。returnを書くと次のノードにオブジェクトを渡してくれると思い込んでいると、気づきにくいバグです。

## 参照渡しで破壊
出力端子が複数ある場合や、出力が二股以上になって複数のノードにつながっている場合、Node-REDがmsgオブジェクトの複製（クローン）をしてくれるので、移行の処理で更新しても安全に使うことができます。
しかし、node.send(msg)(Node-RED1.0以降はnode.send(msg,false))とすると、msgオブジェクトの参照がわたります。このため、複数の処理がnode.send()されたオブジェクトを変更すると、別の処理で扱われる値を修正することになります。このバグは結構厄介で、処理のタイミングによってうまくいくことがあったり、入力を表示しようとデバッグノードを挟んでも実際と異なる値が出力されることがあります。注意深くコードを読むか、delayノードとdebugノードを挟んで確認する以外に解決策はありません（受け取ったmsgオブジェクトを渡す必要がないなら、新しいオブジェクトを渡す方法もありますが、これはこれで意外とはまります）。
# メモリー不足
大規模なシステムや、大規模データを扱う場合、クラウドなど仮想環境でメモリーが少ない場合はメモリー不足でシステムの停止や再起動が生じる場合があります。
## ファンアウト
上に書いたように一つのノードから複数のノードをつなぐ場合、Node-REDがオブジェクトをコピーしてくれる場合があります。線の数だけオブジェクトがコピーされるのでなるべく1対多の接続をやめてください。
## デバッグノード
1対多の接続が避けられない場合の典型例は出力のないノードを使う場合です。デバッグノードはとても便利ですが、出力がないので必ず2股になりオブジェクトがコピーされ、表示用に文字列に変換しますので、使用するメモリーが2倍以上になってしまいます。
単に通過することを知りたければdelayノードを挟んだり、[functionノードでステータスを表示する](https://nodered.jp/docs/user-guide/writing-functions#%E3%82%B9%E3%83%86%E3%83%BC%E3%82%BF%E3%82%B9%E3%81%AE%E8%BF%BD%E5%8A%A0 "公式ページのユーザガイド")と良いでしょう。（参考：[ ファンクションノードのデバッグどうしてる？: ソフトウェアさかば](http://sakaba.cocolog-nifty.com/sakaba/2019/12/post-e83964.html)）
## メモリー不足が予想される場合の対処法
まずは状況を把握してください。環境に応じてメモリーの状態を監視してください。ヒープメモリの状態は[node-red-contrib-gc](https://flows.nodered.org/node/node-red-contrib-gc "公式ページのflows")で確認できます。
フローの修正が可能なら、デバッグノードの線を可能な限り切ってみてください。状況が変わるならメモリー不足です。ファンアウト（複数への接続）を減らしてなるべく一筆書きにしたり、デバッグノードを減らしてください。
処理量が少ない時は問題ないのに、処理が多い場合だけメモリが不足する場合は、ガベージコレクションが追い付いていない場合があります。この場合はディレイノードで確認や改善ができます。
# CPU不足
クラウドの場合、CPUを使い切って落ちてしまう場合があります。
## 無限ループ
Node-REDに限らず無限ループはCPUを使い切ってしまうので、要注意です。終了条件をきちんと確認してください。特にエラー時のリトライはカウントできているか、終了判定が＝なので計算誤差が生じるとおかしくならないか（不等号を使うと安全です）、など注意してコードを確認してください。
## キャッチノード
例外をキャッチノードで受けると処理が終了しなくなるので注意してください。単にデバッグノードをつないでいる場合でも、大量データをsplitノードで分けて非同期に処理すると、デバッグ出力を一気にしようとしてCPUが不足したことがあります。
## CPU不足が予想される場合の対処法
まずは状況を把握してください。環境に応じてCPUの状態を監視してください。次にキャッチノードがあれば無効にして例外が起きていないか確認してください。それで分からない場合は、特定のタイミングやデータで発生しないか、どこまで動くか、など順に切り分けていってください。