トラフィック費用は高い — JSONキーを短くするのは本当に効く
トラフィック費用は、多くの人が思っているよりずっと高い。
このサイトはもともと、友達と一緒に遊ぶために作ったものだった。最初から大勢の人が使うことを想定して設計していたわけではない。データ転送の構造も、人数が増えるほどコストがかなり急に膨らむ形だったが、そもそも3〜4人、多くても10人程度が遊ぶ想定だったので、当時としては十分に合理的な判断だった。
実際、そういう使われ方をすると思っていた。友達と少し遊んで、それ以外の時間はサーバーが静かに遊んでいる、そんな趣味プロジェクトだった。
ところが、その予想はあっという間に外れた。
友達が遊んでいる時間以外はサーバーが空いているので、誰か遊んでみたい人がいたらどうぞ、と思って自分が作ったゲームのURLをいくつかのサイトに貼ってみた。数日後、同時接続ユーザー数が1,000人を超えた。あいにくその日は一日中ミーティングで外に出ていた。午前中に「同接が1,000を超えた」という通知が来たときは、一時的に跳ねただけだろうと思っていた。たぶんすぐに落ち着くだろうと。
ところが、午後になるとその数字は倍になっていた。
そのあたりからサーバーが遅くなり始め、トラフィック費用も一緒に跳ね上がっていった。もともと趣味用に軽く使っていたサーバーに載せていたので、慌てて大きなサーバーに移す必要があった。サーバーインスタンス費用だけでも、自分が普段趣味に使っている一ヶ月分の予算を超え、トラフィック費用はそれ以上だった。広告を一つも載せていないサイトでは、とても支えきれないレベルだった。効率を大きく上げなければ、運用を続けるのは無理だと感じた。
今はECSの上で複数のマイクロサービスに分けてデプロイしている。それぞれにスケジュールベースのオートスケーリングを設定している。ただ、これも実際に運用してみると思っていたより難しい。自分のサイトのトラフィックは、きれいな曲線でゆっくり上がったりはしない。ある瞬間に突然ピークを打つ。だからオートスケーリングだけに頼っていると、気づいた時にはもう遅い、ということが起きる。結局、ある程度は余裕を持ってバッファを走らせておく必要がある。
構成を変え、サーバーを大きくし、オートスケーリングも入れたが、問題はそこで終わらなかった。毎日何万人というユーザーが生み出すゲームデータのトラフィックは、想像よりずっと多かった。そしてそのトラフィックは、想像よりずっと高かった。
以前、自分はリアルタイムの金融データを扱う仕事をしていた。WebSocketやFIXで、数十〜数百マイクロ秒単位のデータを処理する仕事だった。その頃はクライアント側で超高速のデータストリームを受け取って処理する立場だった。今回はその真逆だ。今回はサーバー側からデータを送る立場になった。
以前その仕事をしていたときは、データフォーマットがとても攻撃的に最適化されていた。キー名はできるだけ短くし、一文字のキーも普通にあった。時には t と T を別の意味のキーとして使うことすらあった。正直、当時は「ここまでやる必要あるのか?」と思っていた。読みづらいし、初めて見る人にとっては紛らわしいだけに見えた。
ところが、今回自分がトラフィック費用を払う側になってみて、その理由をすぐに理解することになった。
コミュニティやLLMにこの手の話を聞くと、たいてい「早すぎる最適化」という答えが返ってくる。もちろん多くの場合それは正しい。ボトルネックがまだ確認できていない段階で、先回りして複雑な最適化を入れるのは、普通はよい選択ではない。ただ、JSONキー名を短くするのは、自分が運用しているタイプのサービスでは少し話が違った。リスクはほとんどなく、効果はかなり明確だった。
開発をしていると「たった5%」と軽く言われることが多い。5%減らしたところで意味がない、と切り捨てやすい。だが製造業で製造原価を5%下げると考えれば、それは決して小さな数字ではない。サービス運用も同じだ。特にトラフィックのように毎日積み上がる費用では、その重みはもっと大きい。
そして実際にやってみると、結果は想像以上だった。キー名を短くしただけで、転送量が20%以上減った。自分でもここまで体感できるとは思っていなかった。
もちろんトレードオフがないわけではない。人間が読むときに直観的でなくなることがあるし、デバッグ中に混乱した瞬間も確かにあった。timestamp のような名前に比べて t が不親切なのは事実だ。でも、実際の運用コストが目に見えて下がっていくのを見ると、少なくとも自分の状況では十分に許容できる不便さだった。
面白いのは、以前やっていた最適化と今やっている最適化が、ほぼ真逆だという点だ。
以前は、10マイクロ秒を削るためにより多くのコストと非効率を飲み込んでいた。ハイパースレッディングを切ってコアを半分しか使わないようにしたり、キャッシュラインを揃えるためにデータ構造の周りにパディングを入れたり、そういう作業をしていた。その頃は速度こそが最も重要な問題だったからだ。
今は逆方向に考える。本音を言えば、一緒にプレイしているユーザーがお互いの状態を即時に受け取れるのが理想だ。でも実際の運用では、「どのくらい遅らせてもユーザーが大きな不便を感じないか」を実験することになる。とにかく速ければ正解、というわけでもないし、とにかくきれいな構造が正解、というわけでもない。状況によっては、人間が読みやすい構造よりネットワーク費用のほうが重要な問題になる。
結局、銀の弾丸はなく、状況に合った方法があるだけだ。昔は10マイクロ秒を削るためにより多くの費用をかけるのが正しかったし、今は数バイトを削るために少しの可読性を犠牲にするのが正しい。自分はそういうところが、開発の面白さの一部だと思っている。
投稿一覧