Node-RED: split-joinのリソース消費対策

Node-REDのsplitノードとjoinノード(split-join)は簡単に非同期処理を実行できて便利です。しかし、msgオブジェクトに大量データを載せているとmsg.payloadの配列要素数だけmsgオブジェクトのクローン（ディープコピー）が作成されるので、リソースを大量に消費してしまいます。
split-joinを使わずないで順次処理にする方法もありますが、それでは処理効率が落ちてしまいますので、多段でsplit-joinを使う方法を紹介します。

# やりたいこと（順次処理の例）

以下のような順次処理を考えてみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147007/6ddd3248-e034-1b16-fb96-16054ccf1e21.png)
set dataでは以下のような処理をします。
```
msg.original = {a:1, bigData:[1,2,3]}
msg.payload = msg.original.bigData;
return msg;
```
元データをmsg.originalに残しておいて、msg.payloadのデータを処理します。しかしこれはうまく動きません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147007/e9398ebd-9eee-6ce7-c86e-aadf7870e01d.png)
ディープコピーされていないので、msg.originalも更新されてしまいました。
# 非同期処理の例
順次処理でもmsg.original.bigDataをディープコピーすれば良いのですが、ここではsplit-joinを使います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147007/901410d6-98b2-0957-7e9f-09f7e1710e87.png)
これはうまく動作します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147007/6c28f3b1-1286-4932-b207-27d1aef5f47b.png)
splitノードではpayloadの配列を分解して複数のmsgオブジェクトを作ります。その際にmsgオブジェクトをディープコピーしてクローンを作るからです。
この例のようなデータ量であれば問題にならないのですが、処理すべきデータが何千何万と大量の場合は、msg.originalにあるデータが何千倍何万倍に増えますから、メモリを大量消費するのはもちろん、オブジェクト生成やガベージコレクションでCPUも消費します。この例の場合はmsg.originalをあきらめればよいのですが、後続処理で使う場合はそうもいきません。
# 対策
そこでmsg.originalを含むmsgのクローンを別経路で渡す方法を考えました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147007/91943ff4-3b8e-ecd9-6403-005c7545fbe4.png)
clone & deleteでは受け取ったメッセージのクローンを作成した後、各データの処理に不要でリソースを消費してしまうmsg.originalを消します。
そして元のmsg.payloadと作成したクローンを配列にしてmsg.payloadにセットします。
```
let tmp = RED.util.cloneMessage(msg);
msg.payload = [msg.payload, tmp]
delete　msg.original;
return msg;
```
splitではmsgオブジェクトがクローンされますが、すでにmsg.originalはdeleteされているのでリソースを消費しません。
処理の後はmake answerでmsg.originalのあるオブジェクトを元に処理結果を統合してmsgオブジェクトとします。
```
let newMsg = msg.payload.pop();
newMsg.payload = msg.payload[0];
return newMsg;
```
いかがでしょう？
split-joinのリソース消費対策としてsplit-joinを使うというお話でした。

# 今回のソース
<div class="highlight"><pre><code>
[{"id":"958470b8ecc39638","type":"tab","label":"split-joinのリソース消費対策","disabled":false,"info":"","env":[]},{"id":"2c373086b3fddeae","type":"group","z":"958470b8ecc39638","name":"main flow","style":{"label":true,"fill":"#bfdbef"},"nodes":["d7d55392242b9a9a","b6aabbb0b2f880ec","611750d412847cbb","4490b9bd936f8a3b"],"x":394,"y":159,"w":452,"h":182},{"id":"fc66bbd59f785bba","type":"group","z":"958470b8ecc39638","name":"main flow","style":{"label":true,"fill":"#bfdbef"},"nodes":["73014c3b0ff64176","e7a7df6990978b28","5c23a279fe16a849"],"x":154,"y":539,"w":452,"h":142},{"id":"844fc2129f6d3f47","type":"group","z":"958470b8ecc39638","name":"main flow","style":{"label":true,"fill":"#bfdbef"},"nodes":["1b04297374dd9e7d","72424f48fdfb6574","1561efd334fa288b"],"x":254,"y":839,"w":352,"h":162},{"id":"3ad2e415b7deed04","type":"inject","z":"958470b8ecc39638","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":100,"wires":[["099b0c295c62bc6b"]]},{"id":"28cc6a2709bea9dd","type":"debug","z":"958470b8ecc39638","name":"GOAL","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":770,"y":380,"wires":[]},{"id":"03cbd7df614ba4ae","type":"function","z":"958470b8ecc39638","name":"clone & delete","func":"let tmp = RED.util.cloneMessage(msg);\nmsg.payload = [msg.payload, tmp]\ndelete　msg.original;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":540,"y":100,"wires":[["9c2affcfaeceff25"]]},{"id":"099b0c295c62bc6b","type":"function","z":"958470b8ecc39638","name":"set data","func":"msg.original = {a:1, bigData:[1,2,3]}\nmsg.payload = msg.original.bigData;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":360,"y":100,"wires":[["03cbd7df614ba4ae"]]},{"id":"9c2affcfaeceff25","type":"split","z":"958470b8ecc39638","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":170,"y":160,"wires":[["d8f24cf6ae0fc3c6"]]},{"id":"d6a1b80651d629e7","type":"join","z":"958470b8ecc39638","name":"","mode":"auto","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":450,"y":380,"wires":[["45b6fe62ade125e3"]]},{"id":"45b6fe62ade125e3","type":"function","z":"958470b8ecc39638","name":"make answer","func":"let newMsg = msg.payload.pop();\nnewMsg.payload = msg.payload[0];\nreturn newMsg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":610,"y":380,"wires":[["28cc6a2709bea9dd"]]},{"id":"d7d55392242b9a9a","type":"split","z":"958470b8ecc39638","g":"2c373086b3fddeae","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":470,"y":240,"wires":[["611750d412847cbb","4490b9bd936f8a3b"]]},{"id":"b6aabbb0b2f880ec","type":"join","z":"958470b8ecc39638","g":"2c373086b3fddeae","name":"","mode":"auto","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":530,"y":300,"wires":[["d6a1b80651d629e7"]]},{"id":"611750d412847cbb","type":"debug","z":"958470b8ecc39638","g":"2c373086b3fddeae","name":"without msg.original","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":660,"y":200,"wires":[]},{"id":"d8f24cf6ae0fc3c6","type":"switch","z":"958470b8ecc39638","name":"msg.parts.index == 0?","property":"parts.index","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"num"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":220,"y":280,"wires":[["d7d55392242b9a9a"],["d6a1b80651d629e7"]]},{"id":"4490b9bd936f8a3b","type":"function","z":"958470b8ecc39638","g":"2c373086b3fddeae","name":"msg.payload = msg.payload * 2","func":"msg.payload = msg.payload * 2;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":690,"y":240,"wires":[["b6aabbb0b2f880ec"]]},{"id":"52e56e33b02a64a6","type":"inject","z":"958470b8ecc39638","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":160,"y":500,"wires":[["3ced34213b39f516"]]},{"id":"fa4f732f3d9e8f8f","type":"debug","z":"958470b8ecc39638","name":"GOAL","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":690,"y":640,"wires":[]},{"id":"3ced34213b39f516","type":"function","z":"958470b8ecc39638","name":"set data","func":"msg.original = {a:1, bigData:[1,2,3]}\nmsg.payload = msg.original.bigData;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":340,"y":500,"wires":[["73014c3b0ff64176"]]},{"id":"73014c3b0ff64176","type":"split","z":"958470b8ecc39638","g":"fc66bbd59f785bba","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":230,"y":580,"wires":[["5c23a279fe16a849"]]},{"id":"e7a7df6990978b28","type":"join","z":"958470b8ecc39638","g":"fc66bbd59f785bba","name":"","mode":"auto","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":290,"y":640,"wires":[["fa4f732f3d9e8f8f"]]},{"id":"5c23a279fe16a849","type":"function","z":"958470b8ecc39638","g":"fc66bbd59f785bba","name":"msg.payload = msg.payload * 2","func":"msg.payload = msg.payload * 2;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":450,"y":580,"wires":[["e7a7df6990978b28"]]},{"id":"09fe23de37bd79bf","type":"inject","z":"958470b8ecc39638","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":160,"y":800,"wires":[["1981800cb815e49d"]]},{"id":"decb81d9ee51f1eb","type":"debug","z":"958470b8ecc39638","name":"GOAL","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":690,"y":960,"wires":[]},{"id":"1981800cb815e49d","type":"function","z":"958470b8ecc39638","name":"set data","func":"msg.original = {a:1, bigData:[1,2,3]}\nmsg.payload = msg.original.bigData;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":340,"y":800,"wires":[["1b04297374dd9e7d"]]},{"id":"1b04297374dd9e7d","type":"function","z":"958470b8ecc39638","g":"844fc2129f6d3f47","name":"msg.payload[0] = msg.payload[0] * 2;","func":"msg.payload[0] = msg.payload[0] * 2;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":880,"wires":[["72424f48fdfb6574"]]},{"id":"72424f48fdfb6574","type":"function","z":"958470b8ecc39638","g":"844fc2129f6d3f47","name":"msg.payload[1] = msg.payload[1] * 2","func":"msg.payload[1] = msg.payload[1] * 2; \nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":920,"wires":[["1561efd334fa288b"]]},{"id":"1561efd334fa288b","type":"function","z":"958470b8ecc39638","g":"844fc2129f6d3f47","name":"msg.payload[2] = msg.payload[2] * 2","func":"msg.payload[2] = msg.payload[2] * 2;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":960,"wires":[["decb81d9ee51f1eb"]]},{"id":"4c9e32f9d4713738","type":"comment","z":"958470b8ecc39638","name":"順次処理","info":"","x":100,"y":740,"wires":[]},{"id":"c63214a17a7c7815","type":"comment","z":"958470b8ecc39638","name":"非同期処理","info":"","x":100,"y":440,"wires":[]},{"id":"369129de3c31c249","type":"comment","z":"958470b8ecc39638","name":"リソース対策済み非同期処理","info":"","x":180,"y":40,"wires":[]}]
</code></pre></div>