DEV Community

loading...

#state{} が太る話

voluntas profile image voluntas ・1 min read

結論

別に太っても良い、定期的にダイエットするなら。

前提

Erlang でプロセスの状態を保存する場合は #state{} というレコードを作ってそこに状態を持たせるというのが良くやるパターンだ。プロセスの管理方法はいろいろなパターンがあるのだが、自分が良くやるパターンは中央集権方式を取る。

1 つの TCP や UDP の接続に対して複数のプロセスを上げるという実装をよくすることになる。処理毎にプロセスを分ける、といった方式だ。

ただ、それらの処理するプロセスが状態を持つこともあるのだが、あまり状態を持たせないようにしている。
コアとなるプロセスが他のプロセスの状態もできる限り管理するという方法だ。ただし、完全にそのプロセスに閉じている場合は別。

中央集権型について

1 つの WebSocket 接続に対していくつかの処理があり、 1 つ接続がある毎にプロセスが 3 つ上がるとする。

  • ws プロセス
    • 通知プロセス
    • データベースプロセス
    • ログプロセス

このような構成を取る場合、教科書通りだと simple_one_for_one で supervisor をぶら下げて、その下に one_for_all でぶら下げるといった方式だと思うのだが、自分はその選択は取らない。

ws プロセスから自前で link を全てのプロセスにはるようにしている。つまり再起動処理などを自前で実装するようにしている。supervisor は大変便利なのだが、グレイスフルシャットダウンをする場合、色々トリッキーな処理をいれることになり、コードがわけわからなくなる。

そのため supervisor でそれぞれのプロセスを simple_one_for_one の temporary でぶら下げるようにし、 supervisor:start_child/2 と link や monitor を使う事で自前でのプロセス管理を実現できるようになる。

今回の構成はデータベースプロセスや通知プロセスが死んだとしても ws プロセスはしなないが、ログプロセスが死んだら ws も切断するといった構成にする。

データベースや通知のプロセスはそこがぶら下がっている無限再起動にすると良いだろう、そして monitor で上げる事で、一方向の通知で、再度上げる処理を ws 側で実装することができる。ログプロセスが死んだら、ws プロセスも死んで欲しいので、そこは link を利用しておく。

そんなの supervisor を上手く使えよという話になりそうだが、自分の経験上シンプルな場合は supervisor は素晴らしいのだが、細々したシャットダウン戦略をとる場合はかなり苦しい、というか無理。

そのため、monitor/link を利用した自前でのプロセス管理戦略をできるようにすると良い。

太る理由

中央集権型にした場合、ログプロセスとデータベースプロセスで利用したい情報が同じ場合は ws プロセスで持っておく必要がある。という話が増えていくと、 ws プロセスの #state{} には他のぶら下がってるプロセスの #state{} に持たせる値を管理する必要があるため、太っていく。

また、 データベースプロセスが何かしらの影響で切り替わった事を ws プロセスの #state{} でも持っておきたいとかがある場合もあり、そうすると重複することになる。

太らせて大丈夫なのか

もちろん常にダイエットは意識しなければならない。太らせたくて太らせているわけでは無く、イヤイヤ太っているのだし。

太ったなぁと思ったらダイエット時期なので、ソースを見直すと良い。

状態の中央集権は楽か?

かなり楽。そもそもそこに置いておけば良い。最悪ぶら下がってるのは取りに行ったり、送られてきたりする。重要なのは ws プロセス以外のプロセスは自分以外には ws プロセスしかしらないという状態を作る。

つまり親しかしらない。他の子を知らない状態にする。そうすると子は親を通してしか状態を変更できなくなるので管理がとても楽になる。

個人的には好きな構成、ただ複雑になるのでお勧めはしない。

Discussion

pic
Editor guide