Rustのtokio::select! macroの使い方を今更理解した初心者

経緯

mysticdoll.hatenablog.jp

これで使ってるtc2vvというアプリケーションが、Twitch側のチャットのWebSocketのハンドラのスレッドが死んでいるっぽいがプロセス自体は生きている半死状態だったのを直したかった。

github.com

結論から言うと、 tokio::join! macroを使っていたが、これは指定した Future (正確には async expression) が全て終了するまで待つという処理をするらしく、チャットのハンドラのThreadとWebSocketをホストするサーバのハンドラのスレッドの両方を待っていたのでちゃんと死んでくれなかった。今回はこれをちゃんと殺すために tokio::select! を使った。

なんで殺したいか

一言で言えばkubernetes上で動いているから、になる。

kubernetesがPodの死活監視をして勝手にreplica数を調整してくれるが、対象のPodのプロセスが半死状態だとそれが叶わない。なのでしっかりと死んでもらう必要があるわけである。

本来は ReadinessProbe や LivenessProbe のようなProbeを設定すべきではあるのだが、WebSocketの死活状態のProbeをどうやってとるのかすぐに思いつかなかったのと、別にステートレスなものではあるのでしっかり死んでもらって再起動してもらった方が健全な気がしたのでしっかり殺すことにした。

tokio::select!

自分は JavaScriptのPromiseの方で例えると理解しやすい気がしたのだが、tokio::join! は どちらかというと Promise.all に近く、tokio::select!Promise.race に近いのかなと思った。

というかドキュメントをちゃんと読んでいなかったということが普通に判明した。

tokio::join!

Waits on multiple concurrent branches, returning when all branches complete.

→ 全部の並列処理が終わるまで待つよ

tokio::select!

Waits on multiple concurrent branches, returning when the first branch completes, cancelling the remaining branches.

→ 平行処理の内最初のものが終わったら他をキャンセルするよ。

読もうね。

今後

とりあえずこれでチャットかWebSocketサーバのどちらかのThreadが予期せず死んだ場合にちゃんとプロセス毎死んでくれるようになったので、kubernetesがAuto healingしてくれるだろうと信じている。

一番厄介なのは実は死んでいなくてWebSocket接続が生きているパターンで、そうだった場合はチャットのハンドラをまた弄らないといけなさそうな気がする…

そういう感じだったらもう通知用のchannel生やしてそこから親Threadからプロセスを殺すことになりそうかな…