Erlang & Elixir Fest 2019 で任天堂さんの発表について、懇親会で話せる範囲でお教えていただいたことなどを交えて、Erlang/OTP を利用してパッケージ製品を開発しているプログラマー視点で見ていきます。残念ながら運用については未経験のため、その辺りは触れません。
また自分が XMPP というプロトコルはなんとなく知っているレベルということもあり、XMPP 自体には踏み込みません。
XMPP/ejabberd という選択
当たり前ですがプッシュ通知を送るには TCP/IP の接続を維持するという方針になります。その上、将来的には 1 億を目指して行くわけです。
TCP の常時接続が経験ない中で 1 億同時接続を目指す際の技術選定で、自作プロトコルに行かず、 枯れている XMPP という選択をしたのは本当に素晴らしいと思います。
また、そんな中で実績がある ejabberd を採用しています。ejabberd の有名な採用例は League of Legends で利用されているチャットサービスの同時接続数 7000 万でしょうか。
古いプロトコル、信頼のある実績から ejabberd を採用しているのは任天堂さんの技術選定力に恐れ入ります。
メモリ消費量を抑える
Erlang VM で TCP/IP を利用するとわかると思いますが、とにかくメモリを食います。ただ、それを積極的に改善していっています。
特に C 拡張で XML パーザであるexpat をプロセスが寝る前に一旦開放してしまい、起きたら再度確保することでメモリ消費を抑えています。
寝るというのは TCP/IP でキープアライブ以外の通信がない場合はやることがないので起きていてもしょうがないからです。
コレはプッシュ通知という「通知する時以外は接続を維持すること以外しない」という仕組みを熟知しています。
また、おそらくですがハード側でも色々改善がされているのだと思います。これは任天堂さんがスイッチというハードを自前で開発しているからできる技だなと感じました。
実際この C 拡張部分だけでメモリを 40% 削減できているということは、サーバ費用もかなり抑え込んでいるはずです。技術による工夫でサーバコストダウンできているわけです。
キープアライブ
これは教えてもらえなかったので、想像で書きますが … 。
プッシュ通知のための TCP/IP をはるってことは、ハード側が電源落としてるときとかどうしてるのでしょうか。
おそらくハード側で色々このプッシュ通知向けに接続周りをいじっているような気がします。キープアライブの間隔、電源を落とした時にどうするかなど。
この辺りが公開されることはないと思いますが、すごく気になります。特に電池の持ち辺り。
ejabberd 同士の通信
小さなクラスタを大量にあげているのと、ejabbered 同士の接続は Erlang の分散機能を利用しているというのを、質問したら教えていただきました。Mnesia はほぼ使っていないとのことです。
これで Erlang VM の分散機能を使ってどのくらい SQS から取り出したメッセージがスイッチに送るところまでの書き込み速度が気になります。
メッセージサイズと速度までは聞き逃したので、どこかの発表で聞けないかなと期待したりします。
自分が詳しくない分散機能はこの辺の性能が Erlang VM でボトルネックになっていないということだとは思うので、とても良い発見でした。
実はこの辺は OTP 22 ではかなり改善されているので、今よりも改善されていくのだと思います。
Erlang のホットコードデプロイ
さらっと書かれているのですが、実はこれはかなりすごい事をやっています。
code:soft_purge/1 というのは「すでに利用されている場合は古いのを利用し、新規に利用されたときだけ入れ替えた新しいモジュールを使う」という仕組みです。
Purges the code for Module, that is, removes code marked as old, but only if no processes linger in it.
つまり、これってすでに接続状態にあるものを一切切断することなく、新規での接続だけを新しいモジュールで置き換えることでユーザへの影響を一切なくしています。
さらに予行演習もやっており、以下にユーザへの影響を最小限にするかに注力しているのがわかります。
それこそ Erlang VM をとても良く理解されて、Elrang VM の機能うまく活用されているのです。
ただ落とさずに置き換えるのではなく、ユーザ影響なしにするための努力に頭が下がります。
プロセス辞書
プロセス辞書というのはそのプロセスだけで利用できる key value store です。とても高速ですが Dialyzer では検知できませんし、コードも突然 get とかされることもあり追いかけるのが難しくなります。
ただキャッシュとして利用するのはとても優秀です。プロセスが落ちれば開放されるというのも ets をどこかの gen_server にもたせて利用するのと比較して安心です。
このタイミングでプロセス辞書をうまく使い Redis へのアクセスを減らしているのは、 Redis がスケールアップが難しいことを考慮した改善だと思います。ボトルネックが Redis になることが多い中、 Erlang 側の工夫で改善しているのも Erlang VM をよく理解して対応されていることがわかります。
リモートシェルによる解析
何かあったらすぐにリモートシェルを使って Erlang VM へアクセスして解析するのが Erlang VM の最大のメリットだと思います。
VM 言語ならではなのですが、動いて問題が出ていないかどうかを直接確認でき、なんならその場で統計情報を取得するワンライナーを書くとかも可能です。
それを思いっきり活用されているように思えます。Recon の話が出ていたかは覚えていないですが、トレース機能使っているようなので Recon か redbug を利用しているのは間違いないでしょう。
メモリーリークや、メモリの使用量、動作のトレースが「今、問題でている動いている環境で直接見れる」のは本当に便利です。
任天堂さんのような大規模でも、リモートシェルが活躍できているという事実は Erlang VM の素晴らしさが実感できます。
OTP は最新ではない
利用している OTP が最新版ではないという話をされていました。バージョンまでは話せないとのことだったのですが、現時点で最新版である OTP 22 はかなり改善されています。
そのため、今後よりこの性能に改善がされるというのがわかります。つまりこの資料からわかる省エネっぷりは、今後より省エネになっていくはずです。
まとめ
発表者の方の Erlang 歴は教えてもらえませんでしたが、おそらくそんなには長くないようです。それでもここまでのものができるのは Erlang VM のおかげとおっしゃっていました。
実際 1000 万を大きく超える同時接続を維持しながら、世界中のスイッチに通知を送っているわけです。
本番での Erlang VM クラッシュは一度もないとのことなので、これだけの大規模でどれだけの安定感を、数人の小規模チームで実現しているというのです。1000 万以上の接続を数人で … 一人あたりの担当がおかしいです。
ejabberd を採用し、Erlang VM を活用し、 AWS を利用し、超大規模な自社開発の専用ハードへのプッシュ通知システムを作る。それも数名で。適材適所ここに極まれりという感じです。
この資料を公開してくれた任天堂さんに本当に感謝します。