Dumb Game Lab

部落格 · 開發幕後

獨立開發 失誤紀錄

一個鬧鐘獨吞每日 500 萬列讀取額度:Durable Objects 1101 追查紀錄

Justin Lee, Dumb Game Lab ·

도트혈맹 自動狩獵畫面。像素風地下城裡角色正在和怪物戰鬥,上方可以看到金幣和寶石的數字

11 月 7 日,我的錯誤收集伺服器開始回 500,錯誤碼是 1101,訊息為「Exceeded allowed rows read」。兇手是同一個帳號下的遊戲伺服器(도트혈맹)每 5 秒跑一次的鬧鐘。這個鬧鐘在 users、feed 兩張表上沒有索引,每次都整張表掃過去,一天的讀取額度 500 萬列,它一個就用掉 498 萬列。額度是整個帳號共用的,所以邀請伺服器和錯誤收集伺服器一起掛了。我加了 2 個索引和 10 分鐘快取來修,現在每小時統計各伺服器的讀取列數,超過 60% 就連同兇手名稱一起通知我。

先看數字

發現錯誤收集伺服器掛掉之後,我第一件事是把數字湊齊:額度到底是多少,各個伺服器各讀了多少。

項目數字備註
每日讀取額度(免費)500 萬列帳號內所有伺服器共用。每天 00:00 UTC(韓國時間 09:00)重置
도트혈맹 的一個鬧鐘498 萬列11 月 7 日一天的總和
鬧鐘執行次數每天 17,280 次每 5 秒一次。86,400 秒除以 5
每次讀取的列數約 290 列498 萬除以 17,280 的計算值
錯誤收集器的請求每天 2,000 到 3,000 筆讀取列數為每天 140 萬到 514 萬
長條圖,並排顯示每日額度 500 萬列與 도트혈맹 的一個鬧鐘讀取的 498 萬列
一個鬧鐘就讀掉額度的 99.6%。

一開始懷疑錯了伺服器

症狀先出現在錯誤收集伺服器。呼叫工作階段清單和問題清單都回 500,回應內容只寫著 error code: 1101。同一時間,好友邀請伺服器也用同一個錯誤碼停擺。

緊急通知橫幅把邀請伺服器列為兇手。其實就是把掛掉的伺服器直接當成兇手。但邀請伺服器一天只有幾千筆請求,不可能製造出那麼多讀取。

錯誤收集器也一樣。請求一天 2,000 到 3,000 筆,讀取列數卻有 140 萬到 514 萬。請求數和讀取列數差這麼多,代表不是請求在讀,而是別的東西在讀。

兇手是 5 秒鬧鐘

額度是整個帳號共用,所以得分開看每個伺服器的用量。我用 Cloudflare API 抓出各個伺服器(Worker)的讀取列數,幾乎全集中在一個地方,就是 도트혈맹 的遊戲伺服器。

這個伺服器每 5 秒跑一次鬧鐘。鬧鐘裡面要數目前在線的人數、讀機器人名單,這些查詢打在 users、feed 兩張表上,而這兩張表沒有索引,每次都從頭到尾整張表掃一遍。

一次 290 列看起來很少。但鬧鐘一天跑 17,280 次,乘起來就是 498 萬。

도트혈맹 強化畫面。鐵匠鋪背景裡,武器從 +7 變 +8 的成功與破壞機率顯示在畫面上
兇手是這款遊戲伺服器端的鬧鐘。

修了什麼

도트혈맹 伺服器和錯誤收集伺服器我各修了一次。兩邊原因一樣:沒有索引就去查範圍的查詢。

  • 修好的伺服器部署上去,當天的額度早就見底了,沒辦法立刻恢復。錯誤收集伺服器要等下一次重置(00:00 UTC)才會解開。
  • 加上索引之後,每次讀取的列數會從整張表的大小,降到真正需要的列數。
伺服器修改內容
도트혈맹users(bot, seen)、feed(bot, ts) 兩個索引
도트혈맹機器人名單在記憶體快取 10 分鐘
錯誤收集器issues(last_at)、hits(day) 索引
錯誤收集器每天一次刪除超過 90 天的 hits、sessions

避免同樣的事再發生

光是修好還不夠。這次是額度見底之後我才發現,而且是過了很久才知道是誰在吃。所以我又加了兩樣東西。

第一是測試。讓鬧鐘跑 400 次,把這段時間執行過的查詢收集起來,用 EXPLAIN QUERY PLAN 看執行計畫,只要有任何一個查詢是不走索引就掃 users、feed,測試就會失敗。帶 LIMIT、在前面就停下來的查詢不算。

第二是監控。每小時第 7 分鐘把各伺服器的讀取列數加總,超過額度的 60% 就附上兇手伺服器名稱和列數發出通知。我也會直接去戳邀請和錯誤收集伺服器,出現 1101 就發通知,恢復了就撤掉。

도트혈맹 血盟聊天畫面。可以看到血盟成員在聊天視窗裡對話
這是有血盟聊天的 도트혈맹 畫面。

我錯在哪

錯的不只是一條查詢。好幾個伺服器共用一份免費額度,卻沒有任何地方在量每個伺服器各讀多少。一個伺服器把額度吃光,好端端的伺服器就會代替它倒下,而我只會看到倒下的那一邊。

所以我在 도트혈맹 加了一個測試,檢查鬧鐘路徑上的查詢有沒有走索引。這次學到的是:額度只要是帳號共用,就得先有一張各伺服器的用量表。

FAQ

Cloudflare Durable Objects 的 1101 錯誤是什麼意思?

有可能是超過免費額度。我這次的回應內容同時出現「Exceeded allowed rows read」,是每日讀取列數額度(整個帳號 500 萬列)被用完了。

Durable Objects 的讀取額度是每個伺服器各算,還是整個帳號共用?

在我的帳號是整個帳號一天共用 500 萬列。所以一個伺服器幾乎用光之後,另外兩個伺服器也跟著停了。

額度用完的那天,把伺服器修好會馬上恢復嗎?

不會馬上。修好部署之後,當天的額度還是用完的狀態,要等 00:00 UTC(韓國時間早上 9 點)重置才行。

要怎麼確認鬧鐘有沒有掃整張表?

把鬧鐘裡執行的查詢收集起來,跑 EXPLAIN QUERY PLAN 看看。如果執行計畫出現沒走索引的 SCAN 表名,就表示讀了整張表。

更多文章

新遊戲與小消息,大約每月一封。

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