Go Conferenceでクラウドゲーミングな発表をした

Go Conference 2021 Autumn で Cloud Gaming Platform with Goという発表をした。

speakerdeck.com

OOParts のインフラコストが問題になり、2021年に新たにクラウドゲーミングのエンジンを作り直した。 くわしい経緯は以下の資料にまとまっている。

この2つの発表では全体的な経緯とインフラの構築をどうやったかが語られている。 では、アプリケーション部分についても発表したいよねって話になり、Go ConferenceにCFPを送ってこの発表に至った。

内容はスライドを見てもらえればいいとして、この記事では発表内容には入らなかった補足、余談をすこししてみる。

Goで実装して苦労した点

発表ではなぜGoを選んだかを中心に良かった点を並べたが、もちろん苦労した点もある。 GoroutinesとChannelは非同期処理を扱いやすいとはいっても、何も考えずに使うと罠にかかる。

まずはGoroutineの寿命管理。goroutineは関数がreturnしない限り終了することはない。 よって適切な寿命管理ができていないと無駄なgoroutineが裏で増え続けることがある。この問題はgoroutine leakと呼ばれる。

特にクラウドゲーミングのように長時間続くセッションを扱っているとgoroutine leakが発生しやすい。 実際にひとつのゲームセッション中に何度も切断接続を繰り返すとgoroutineが増えていく問題が起こったこともある。 pprofのgoroutine dumpを使えば原因特定は容易だが、気をつけているつもりでもうっかりgoroutine leakをやってしまう。 静的解析とかで早めに検出できたらいいのにな〜と感じた。

次に、Channelの扱い。channelは異なるgoroutine間でデータのやり取りをするのに便利だが、問題も起きやすい。 channelのバッファサイズが0の場合、誰かがchannelからデータを受信するまで送信がブロックされる。 よってこのchannelはどのgoroutineが送信しようとしていて、どのgoroutineが受信しようとしているのかを把握しておかないと、 アプリケーション全体が止まってしまった、しかしどこで止まっているのかわからない…という問題に陥りやすい。 単純にバッファサイズを増やせばいいのでは?と考えそうになるが、それでは根本解決にならない(使用メモリを増やして問題を先延ばししているだけだ)。 さらに、channelのclose処理はブロックされないという点や、同じchannelを2回closeするとpanicが発生するといった仕様があるので理解した上で扱う必要がある。

また、pub/subのように1つのデータを複数の受信者に配りたい場合が面倒だった。1つのinput channelと複数のoutput channelからなる構造体を作ったのだが、現時点のGoはgenericsに対応していないためchannelに流すデータの型ごとに構造体を分けるか、interface{} を使って型情報を曖昧にするしかない。

Go Conferenceの運営について

オンラインでの勉強会登壇というのは初でちゃんとできるだろうかと不安だったのだが、Go Conferenceの運営の方はSlackへの案内、そして事前のリハーサルまで丁寧にしてくれておかげで当日は何もトラブルなく発表ができたので大変感謝している。

Ask the speakerについて

発表後はremoというサービスで発表者に質問できる形式になっているが、このサービスはアカウントを作らないと入室できない上に、仮想空間のような場所で発表者の近くまで移動して通話を開始しないと話しかけられない。よって発表を聞いた人が質問を投げに行くのはハードルが高いのではと感じた。

そのせいか、質問に来た人はほぼいなくて最終的に発表者同士で雑談していたりしてAsk the speakerの時間をすごした。 あまり発表者が集まりすぎても今度はまたその輪の中にいきなり入って質問するというのも勇気がいるし、気楽に聞けるAsk the speakerというのはどうあるべきなのかなぁと考えていた(答えは出てないが…)。

発表中にあった質問について

発表後にTwitterなどで見かけた質問らしい投稿がいくつかあったので自分なりの回答を書いておく。

koはbuildpacksと比べてどうなのか

buildpacksもkoと同様にDockerfile不要でコンテナイメージを作成できるツールだが、さまざまな言語に対応しているbuildpacksと違ってkoはGoのみに特化しているツールで、buildpacksよりコンパクトでビルド速度が速いのが特徴かと思われる。

Google CloudのブログでDockerやbuildpacksとの速度比較が載っているのでこの記事が参考になる。

Ship your Go applications faster to Cloud Run with ko | Google Cloud Blog

gocloudにおいて特定サービス固有の機能を使いたくなったらどうするのか

OOPartsの実装範囲内では特定サービスに依存した機能を使わざるを得ないケースはなかったのだが、 gocloudのドキュメントを見る限り、 As() 関数を使って特定サービスの構造体にキャストするしかないらしい。

Using provider-specific APIs · Go CDK