Hyperdrive MySQL 快取 bug:節點寫入的位元組格式不一致
Cloudflare Blog · 2026-10-14
Hyperdrive 的 MySQL 查詢快取把「已翻譯給 Worker 的位元組」直接存進共用快取,導致另一個節點讀到格式錯誤的結果,約 0.001% 的查詢因此回傳空結果。修正方式是改為一律寫入 Hyperdrive 自身能力集編碼的標準版本。
Cloudflare 在 事後檢討文中說明,這個 bug 在 Hyperdrive MySQL 進入 GA 前被找到,並與 Postgres 版快取設計的一個隱含假設有關。
原本的問題
Hyperdrive 是 Workers 的資料庫連線池。建立一條 SQL 連線至少要五次網路往返,因此 serverless 環境需要連線池。Hyperdrive 對 Worker 扮演 server,對來源資料庫扮演 client,兩側協商的能力旗標(capability flags)可以不同。
Beta 期間,用量最大的 MySQL 客戶看到一種穩定但極低頻的症狀:該有資料的查詢回了空結果,比例約 0.001%,端到端追蹤很難抓到。團隊排除了網路問題,關鍵線索是這個現象只出現在啟用快取的 Hyperdrive 實例上。
快取架構與協定差異
Hyperdrive 分成兩種節點:Client 節點與 Worker 在同一台機器上,Endpoint 節點持有連線池,位於離來源資料庫最近的資料中心。兩者各自使用所在資料中心的 CDN 快取,若位於同一個資料中心就共用同一份快取。快取鍵是查詢封包內容的雜湊(含部分正規化),快取值是結果的位元組。
MySQL 的 CLIENT_DEPRECATE_EOF 能力旗標會改變結果集的結尾格式:
| 未協商 | 已協商 | |
|---|---|---|
| 欄位定義之後 | 送出 EOF 封包 | 省略 |
| 結果集結尾 | EOF 封包 | OK 封包 |
文中指出,MySQL 5.7.5 以上支援此旗標,且只有在 client 協商時才會使用 OK 封包。當時熱門的 Node.js 函式庫 mysql2 並不支援,所以 Hyperdrive 對這類 Worker 驅動必須補回 EOF、把 OK 轉成 EOF。
根因
Postgres 版快取的假設是「寫給 Worker 的位元組,就是可以快取的位元組」。這在 Postgres 成立,在 MySQL 不成立:Endpoint 永遠使用 Hyperdrive 自己的能力集,Client 寫出的卻是依 Worker 能力翻譯過的位元組。兩個節點在同一個資料中心、共用快取時,就會互相讀到對方格式的資料。
- 情境一:Endpoint 把 Hyperdrive 格式的結果存進快取,同一查詢的 Client 找到後未經翻譯直接回給 Worker,格式與 Worker 的能力不符。
- 情境二:Client 存入已翻譯給 Worker 的結果,Endpoint 讀到後送給另一個 Client,該 Client 再翻譯一次,最後遇到提前出現的 EOF 或卡住。
觸發條件需要兩件事同時成立:兩個節點在同一個資料中心,且其中一個讀到另一個寫入的值。這解釋了為什麼錯誤率這麼低、又只出現在開啟快取的實例。
採用的方法
修正是不論由哪個節點寫入,快取中一律存放以 Hyperdrive 能力集編碼的標準版本。Client 讀取後再依各自 Worker 的能力翻譯。這等於把「翻譯」從快取的寫入端移到讀取端。
舊:Client 寫入 R_worker(已翻譯) → Endpoint 讀到後再翻譯一次
新:任何節點寫入 R_hyperdrive(標準)→ 各 Client 讀取時才翻譯同一批 GA 前修掉的兩個編碼問題
文章還提到兩個較早的 bug,都屬於字元集。第一,握手階段使用 utf8,而 MySQL 把它當成已棄用的 utf8mb3 別名,每個字元最多 3 位元組,emoji 這類 4 位元組字元會壞掉,改用 utf8mb4 後解決。第二,Hyperdrive 為避免 session 狀態在連線借出之間外洩,加入 COM_RESET_CONNECTION 清理;但此命令會把字元集重設成伺服器預設,而非啟動時協商的值,預設為 latin1 的伺服器上連線會退回 latin1。修法是每次重設後再下一次設定 utf8mb4 的命令。
影響範圍
文中沒有提供受影響的版本範圍,也沒有說明 GA 前後的具體部署時程。可以確定的是:該問題集中在啟用 Hyperdrive 查詢快取、且使用不支援 CLIENT_DEPRECATE_EOF 的驅動的情境,例如 mysql2。若你在 beta 期間遇過偶發的空結果、早期 EOF 或查詢卡住,而且當時開著快取,這篇文章描述的機制就是需要比對的對象。
修正釋出後,空結果問題消失,Client 連線成功率達到 99.99%,與團隊設定的可用性目標一致。文章未公布修正釋出的日期,也未公布受影響的客戶數。
對一般自行設計快取層的工程師來說,這個案例的啟示很具體:快取的值如果是經過「依對端能力調整」的輸出,就不能直接當成共用資料。要檢查的是你的快取鍵是否涵蓋了所有會影響輸出位元組的協商參數,或快取內容是否一律存放標準格式。
連線方式沒有變:用 Wrangler 即可建立 MySQL 設定。
npx wrangler hyperdrive create HYPERDRIVE_NAME \
--connection-string="mysql://user:password@db-host.example.com:3306/dbname"原始來源:Cloudflare:Inside Hyperdrive、MySQL:COM_RESET_CONNECTION