「書き込み回数が「ライト1.5万件/時」になっていて無料枠が消し飛んだ話
ある朝、アプリが静かに壊れていた
「他の人の投稿が見えない」——ユーザーからそんな報告が届いたとき、最初は単なる通信の不調だと思っていた。
ところが管理画面を開いて血の気が引いた。データベースへの読み取りが急増し、書き込みは1時間あたり1.5万件。誰も大量投稿などしていないのに、だ。
犯人は、よかれと思って入れた「リアルタイム表示」の仕組みそのものだった。
そもそも、リアルタイム表示はなぜ「高くつく」のか
チャットやSNSのように「他人の更新が即座に画面に反映される」体験は、いまや当たり前に感じる。だが裏側では、サーバーに常時つなぎっぱなしの監視が走っている。
普通のWebページは「開いたときに1回だけ読み込む」。一方リアルタイム表示は「変化があるたびに何度でも読み込む」。便利さの裏で、課金対象であるデータの読み書き回数が青天井に増えやすい構造なのだ。
無料枠で個人開発しているうちは気づかない。問題が牙をむくのは、ユーザーが増えたときだ。
静かに膨らむコスト、その正体
今回の事故は、3つの要素が絡み合って「自分で自分を加速させる悪循環」に陥っていた。
① 上限に達した瞬間、すべてが止まる
無料枠には「1日あたりの利用上限」がある。複数ユーザーで使えば、その枠は意外なほど早く尽きる。上限に達した瞬間、リアルタイム監視は強制終了される。ここまでは仕様通り。問題はその先だった。
② 「復旧しようとする処理」が、傷口を広げる
監視が切れたとき、自動で再接続する仕組みを入れていた。一見、親切な設計だ。だが再接続するたびに数十件分のデータ読み取りが発生する。
しかも上限を超えている最中でも、最大1分ごとに再接続を試み続ける。つまり——回復しようとする動きが、さらに枠を食いつぶす。
③ 送れなかった投稿が、永遠に再送をくり返す
上限超過中はユーザーの投稿も保存できず、手元に「保留」として溜まる。これを自動で送り直す処理も入れていた。
しかし送信もまた失敗する。失敗してもタイマーは止まらない。結果、全ユーザーの保留投稿が、一定間隔で延々と再送され続ける。
ざっくり計算するとこうだ。
50ユーザー × 保留5件 × 1時間に120回 = 1時間で3万回の書き込み試行
誰も悪意なく、ただアプリを開いているだけ。それだけでこの数字になる。
悪循環を一枚の図にすると
上限を使い切る
↓
監視が切れる → 再接続 → また読み取りが発生
↓(再接続のたびに読み取り増加)
さらに枠を消費
↓
並行して、保留投稿が一定間隔で再送をくり返す
↓
書き込みでも枠を消費
↓
さらに上限超過 ──→(最初に戻る)
「枠が切れた」という出来事が引き金になり、それを直そうとするコードが、かえって枠を消費する。一度回り始めると止まらない、自己強化型のスパイラルだった。
どう収めたか(考え方だけ)
技術的な詳細は省くが、対処の方針はシンプルだった。
1. 「待つしかないエラー」と「やり直せばいいエラー」を区別する
通信が一瞬切れただけなら、再接続すれば直る。だが「今日の上限を使い切った」エラーは、何度やり直しても無駄——むしろ悪化する。やみくもに再試行せず、ユーザーに「時間をおいて再読み込みを」と伝えて潔く止める。
2. 自動リトライの間隔を、思い切って長くする
保留投稿の再送を「30秒ごと」から「5分ごと」へ。失敗が続く処理を高頻度で回しても、消耗するだけだ。
3. 一度に読み込む件数そのものを絞る
リアルタイム監視が一回で取得するデータ量を、全体的に削減した。1件あたりは小さくても、回数が掛け算で効いてくる世界では、ここが効く。
この事故から持ち帰った3つの教訓
「便利な機能」ほど、増えたときのコストを設計段階で見積もる。 リアルタイム表示は体験としては最高だが、ユーザー数に比例して——時には掛け算で——コストが跳ね上がる。最初に「上限」を意識していないと、ある日いきなり跳ねる。
「自動で復旧する仕組み」には、必ず「止め時」をセットで入れる。 再試行ループは便利だが、止まる条件を持たない再試行は凶器になる。「待つべきか、やり直すべきか」を判断するロジックがなければ、復旧処理が事故を増幅させる。
無料枠は「無料」ではなく「制限つきの本番環境」。 上限に達したときに何が起きるか、を試しておくこと。順調なときのコードだけ書いて満足していると、限界を超えた瞬間の挙動で足をすくわれる。
便利さには裏側がある。リアルタイム表示はその典型だ。「動いた!」の先にある「増えたらどうなる?」まで考えておくと、ある朝の青ざめる体験を一つ減らせる。
