Node-RED で RangeError : Maximum call stack size exceeded エラーが一定時間経過すると発生することを解決したメモ

Node-RED で Maximum call stack size exceeded エラーが一定時間経過すると発生することを解決したメモです。

Node-RED 以外でも役に立つと思う

最初にお伝えしておくと、今回のナレッジは、最終的には JSON データの扱い方という話だったので、Node.js やブラウザで JavaScript で JSON データを扱う際も役立つと思います。

今回の状況

image

今回のフローは、Nature Remo のデータを集めて UI で表示する中で、現在のデータと過去のデータと比較するフローで起きていました。

状況としては、根が深そうな挙動。

  • フローをデプロイした直後はサクサク動く
  • 1時間程度放置していても問題はない
  • しかし数日放置していると、UIの表示も debug ノードの表示もどんどん重くなる

そして、最終的には

image

RangeError : Maximum call stack size exceeded が出てしまい、操作不能になって Node-RED が落ちたり、勝手に再起動がかかったりする状況でした。どうも RangeError: encodeObject Error: [Invalid string length] というのも起きるときがある。

いろいろと模索

  • フローを整理して少なくしても、起きるタイミングは変わらない。
  • JSONata の複雑な計算がまずいのかと思い削っても、起きるタイミングは変わらない。
  • debug ノードで検証した時にエラーが出るように見えたので、できるだけ OFF にしてみたが、起きるタイミングは変わらない。
  • Nature Remo の API が遅いので巻き込まれている気がしたが、濡れ衣。そうではなかった。
  • inject ノードで取得する頻度が早いんじゃないかと思い遅くしても変化なし。

などなど、やってみたんですが、変わらず。一番、疑心暗鬼になってたときは、 Node-RED そのもののバグも疑いました。

けど、ちがいました!

ようやく原因が分かった

原因は、前の値を payload.previous に入れ子でぶら下げて記録してたのが、入れ子を無限に深くしてデータを増殖させてしまいまずかったということが分かりました。

どういうことかというと、こんなことをしていました。

  • まず Nature Remo の API からデータを取得する
  • 使いやすいように JSONata で加工
  • UIに値を表示させる
  • 上記をやり終わったら、payload 全体を過去データをして flow.currentNatureRemoRoomEnvironments として保存する
  • inject ノードで次のタイマーが1分後に発動する
  • Nature Remo の API からデータを取得前に、change ノードで過去データ flow.currentNatureRemoRoomEnvironments を payload.previous に戻して比較しやすくする。
  • 以降、繰り返し。

これ、よく考えると分かるんですが、これ1つ前のデータ群を payload.previous に戻してしまうと、どんどん過去のデータが入れ子で貯まっていってしまうんですよね。

image

これを1度だけの流れで見てると問題なさそうなんですけど。

image

2回繰り返されるとよりイメージが湧きます。payload の値の中に previous を作っていくと無限に入れ子でデータがぶら下がっていってしまうんですね。

image

だんだん分かってきて、さらに 7 回繰り返した状況はこちら。やばいですね。なので、これが時間経過で何度も繰り返されるとデータがパンクしてしまう。(むしろ、今まで、数日間よく持ってくれてたなあ・・・)

結局どうしたかというと

payload の中ではない外の値( msg.payload.previous ではなく msg.previous )に、1つ前のデータだけ持ってくるようにしたら、無限にデータがぶら下がっていく状況が回避でき、ずっと稼働しても安定して動き続けるようになりました!

ただ、ここまでくると、単純に flow.currentNatureRemoRoomEnvironments を独立して見ればいい気もしますが、ひとつのフローの中で msg オブジェクトに全部入ってると、何かとやりやすいので悩ましいところです。このあたりは、また調整していこうと思っています。

とにもかくにも、自分の実装でヤバい展開になっていたことを、ちゃんと気づけて解決できてよかったです!