Dumb Game Lab

ブログ · 開発の裏側

個人開発 失敗談

アラーム1つが1日の読み取り上限500万行を使い切った:Durable Objects 1101の追跡記録

Justin Lee, Dumb Game Lab ·

ドットクランの自動狩り画面。ピクセルアートのダンジョンでキャラクターがモンスターと戦い、上に金貨と宝石の数字が見える

11月7日、エラー収集サーバーが500エラーを返し始め、エラーコードは1101、メッセージは「Exceeded allowed rows read」でした。犯人は同じアカウントのゲームサーバー(ドットクラン)で5秒ごとに動くアラームです。このアラームがusersとfeedのテーブルをインデックスなしで全部読み、1日の上限500万行のうち498万行を1つで使っていました。上限はアカウント全体で共有なので、招待サーバーとエラー収集サーバーも一緒に止まりました。インデックス2つと10分のキャッシュで直し、今は毎時サーバーごとの読み取り行数を測って、60%を超えたら犯人の名前つきで通知します。

まず数字から

エラー収集サーバーが止まったと気づいて最初にやったのは、数字を集めることでした。上限がいくつで、誰がどれだけ読んだのかから見ました。

項目数字メモ
1日の読み取り上限(無料)500万行アカウント内の全サーバーで共有。00:00 UTC(韓国時間 09:00)にリセット
ドットクランのアラーム1つ498万行11月7日の1日分の合計
アラームが動く回数1日17,280回5秒ごと。86,400秒を5で割った値
1回に読んだ行約290行498万を17,280で割った計算値
エラー収集のリクエスト1日2〜3千件読み取り行数は1日140万〜514万
1日の上限500万行と、ドットクランのアラーム1つが読んだ498万行を並べた棒グラフ
アラーム1つで上限の99.6%を読んでいました。

最初は違うサーバーを疑った

症状はエラー収集サーバーに先に出ました。セッション一覧とイシュー一覧を呼ぶと500が返り、本文にはerror code: 1101とだけ書かれていました。同じ時間に、友達招待サーバーも同じコードで止まっていました。

緊急通知のバーには、招待サーバーが犯人として表示されました。止まったサーバーをそのまま犯人にしていたわけです。でも招待サーバーは1日のリクエストが数千件しかなく、それだけの読み取りは作れません。

エラー収集サーバーも同じでした。リクエストは1日2〜3千件なのに、読み取り行数は140万〜514万。リクエスト数と読み取り行数がここまで合わないなら、リクエスト以外の誰かが読んでいるということです。

犯人は5秒ごとのアラームだった

上限はアカウント全体で共有なので、サーバーごとの使用量を別々に見る必要がありました。Cloudflare APIでサーバー(Worker)ごとの読み取り行数を出すと、ほぼ全部が1か所でした。ドットクランのゲームサーバーです。

このサーバーは5秒ごとにアラームが動きます。アラームの中で、接続中の人数を数えたりボット名簿を読んだりするクエリが、usersとfeedのテーブルにインデックスがないせいで、毎回テーブルを頭から最後まで読んでいました。

1回290行は小さく見えます。でもアラームは1日17,280回動くので、掛けると498万になります。

ドットクランの強化画面。鍛冶場を背景に、武器が+7から+8になるときの成功率と破壊率が表示されている
犯人はこのゲームのサーバー側のアラームでした。

直したこと

ドットクランのサーバーとエラー収集サーバーを、それぞれ直しました。原因はどちらも同じで、インデックスなしで範囲を読むクエリでした。

  • 直したサーバーをデプロイしても、その日の上限はすでに使い切っていたのですぐには復旧しませんでした。エラー収集サーバーは次のリセット(00:00 UTC)で戻ります。
  • インデックスを付けると、1回に読む行数がテーブル全体の大きさから、必要な行数まで減ります。
サーバー変えたこと
ドットクランusers(bot, seen)とfeed(bot, ts)のインデックス2つ
ドットクランボット名簿を10分間メモリにキャッシュ
エラー収集issues(last_at)とhits(day)にインデックス
エラー収集90日を過ぎたhitsとsessionsを1日1回削除

同じことを繰り返さないために

直すだけでは足りませんでした。今回は上限が尽きてから気づき、誰が使っているのかはだいぶ後でわかりました。そこで2つ足しました。

1つ目はテストです。アラームを400回動かす間に実行されたクエリを集め、EXPLAIN QUERY PLANでプランを見て、usersやfeedをインデックスなしで読むクエリが1つでもあればテストが失敗します。LIMITが付いて先頭で止まるクエリは除きます。

2つ目は監視です。毎時7分にサーバーごとの読み取り行数を合計し、上限の60%を超えたら、犯人サーバーの名前と行数を付けて通知を出します。招待サーバーとエラー収集サーバーを直接叩いて1101が返ってきたらそれも通知し、復旧したら消します。

ドットクランのクラン内チャット画面。クランのメンバーが会話しているチャット欄が見える
クランチャットのあるドットクランの画面です。

私が間違えていたこと

間違いはクエリ1つではありませんでした。複数のサーバーが無料枠1つを分け合っているのに、サーバーごとにどれだけ読んでいるかを測る場所がありませんでした。1つのサーバーが上限を使い切ると、無関係なサーバーが代わりに止まり、止まった側だけが目に入ります。

そこでドットクランには、アラームの経路のクエリがインデックスを使うかを見るテストを入れました。上限がアカウント共有なら、先にサーバーごとの使用量の表が必要だと、今回学びました。

FAQ

Cloudflare Durable Objectsの1101エラーは何を意味しますか?

無料の上限を超えた場合があります。私の場合は本文に「Exceeded allowed rows read」も出ていて、1日の読み取り行数の上限(アカウント全体で500万行)を使い切っていました。

Durable Objectsの読み取り上限はサーバーごとですか、アカウント全体ですか?

私のアカウントでは、アカウント全体で1日500万行を分け合っていました。1つのサーバーがほぼ全部を使ったため、ほかの2つのサーバーも一緒に止まりました。

上限が尽きた日にサーバーを直すと、すぐ復旧しますか?

すぐには戻りません。直してデプロイしても、その日の上限はそのままなので、00:00 UTC(韓国時間の午前9時)のリセットを待つ必要がありました。

アラームがテーブル全体を読んでいるかどうか、どう確認しますか?

アラームの中で実行されるクエリを集めてEXPLAIN QUERY PLANを実行します。プランにインデックスなしのSCANとテーブル名が出ていたら、テーブル全体を読んでいます。

ほかの記事

新作ゲームと小さなお知らせを、月に1回ほどお届けします。

他の言語English한국어日本語繁體中文