Cloudflare KVの書き込み上限は1日1,000回。超えたら、LINE botの回数制限が黙って外れる作りだった

1,000を4で割ると、250になる。

1,000は、Cloudflare KVに無料で書き込める1日の回数だ。4は、私のLINE botがAIで1回返事をするたびに、KVへ書き込んでいた回数。250は、AIがふつうに答える返事だけなら、その日の書き込み枠が尽きるまでに返せる通数になる。

このbotには「AIを呼ぶのは店全体で1日300回まで」という上限を付けていた。KVの書き込み枠は日本時間の朝9時に戻り、AIの300回は0時に戻るので、区切りはずれている。それでも、AIがふつうに答える返事だけなら、KVの枠は朝9時から次の朝9時までのあいだで約250通分しかない。AIの上限の300回より少ない。AIを使わない返事やスタッフへの引き継ぎでは書き込みの回数が変わるので、250はあくまで目安だ。

では、枠が尽きた日にbotはどうなるか。止まるのではない。返事は続いたまま、回数の上限が効かなくなる作りだった。

実際に枠に当たったことはない。計算で見つけて、10月11日に、回数を数える場所をKVからD1へ移すようコードを直した。この記事に書くのは、なぜ上限が外れるのか、どう直したか、まだ直していない所だ。

KVの無料枠は、書き込みが1日1,000回

KV(Workers KV)は、Cloudflare Workersから使える文字の保存場所だ。無料プランの上限は公式のページにこう書かれている。

項目 無料の上限
書き込み(別々のキー) 1日1,000回
同じキーへの書き込み 1秒に1回まで
リセット 毎日0時(UTC)=日本時間の朝9時
超えたとき その種類の操作がエラーになる

読み取りは1日10万回まで使えるので、書き込みだけが極端に少ない。無料プランのままなら、超えても請求は来ない。次の日本時間の朝9時まで、書き込みが失敗し続けるだけだ。

返事1回で、KVに4回書いていた

botは、公式LINEに届いた質問にClaudeのHaiku 4.5で返事をする。作った経緯は公式LINEにAIの自動返信を付けた記事に書いた。

AIがふつうに1回返事をするたびに、KVへ書いていたのはこの4つだった。

書いていたもの 何のため
届いたメッセージの番号 LINEから同じメッセージが2回届いたとき、2回返さないため
その人の1時間の回数 1人1時間20回までにするため
店全体のAIの回数 1日300回までにするため
会話の履歴 前のやり取りを踏まえて返すため

回数の数え方は単純だ。KVから今の数を読み、上限より小さければ1を足して書き戻す。上限に届いていたら、AIは呼ばない。

書き込みに失敗すると、数が増えなくなる

穴は、書き戻すところにあった。

KVへの書き込みが失敗しても、コードはエラーを記録するだけで先へ進む。コードには「書き込み失敗でお客様への返信は止めない」という方針が書いてある。履歴の保存に失敗したくらいで、返事まで止めないためだ。

この方針のまま書き込み枠が尽きると、こうなる。

KVの書き込み枠が尽きたあとの回数の数え方の例。今の数を読むと180。1を足して181を書き込もうとするが失敗。次のメッセージでも読むと180。上限の300に届かないので、このカウンターではAIを止められない

たとえば、読むと180。1を足して181を書こうとして失敗する。次に読んでも180のままだ。書き込み枠が戻る朝9時まで数は増えず、上限の300に届かない。だから毎回「まだ上限前」と判断して、AIを呼び続ける。

1人1時間20回の上限も、同じ理由で効かなくなる。すり抜けるのは、上限より小さい数が読めて、書き戻しだけが失敗する場合だ。枠が尽きる前にその時間の上限に届いていた人も、止まるのはその時間のあいだだけだ。次の時間になってもKVの書き込み枠が戻っていなければ、その人にも上限が効かなくなる。

返事は普通に返り続けるので、ブレーキが外れたことは表に出にくい。

AIを1回呼ぶのは0.5円前後で、300回なら約150円という見積もりで上限を決めていた。上限が外れた日に何回呼ばれるかは、届くメッセージの数しだいになる。上限を置いたと記事にまで書いていたのに、その上限が効かない日がありえた。

前の記事には、同じ人がほぼ同時に何通も送ると、回数の上限を少し超えたり、会話の履歴が1往復抜けたりすることがある、と書いた。実際には、少し超えるどころでは済まない場合があった。

見つけたのは、別の記事の事実確認だった

気づいたのは私ではない。10月10日、Cloudflare Workersで副業の仕組みを動かしている記事の下書きに、私は「回数の上限は、どれも私の使い方とは桁が違う」と書いた。事実確認を任せたAIがこの一文を止めた。botは返事1回でKVに約4回書くので、自分で決めた300回近くを、KVの同じ区切り(朝9時〜次の朝9時)のあいだに返せば、KVの書き込み1,000回を超える計算になる、と。

その記事からは一文を外し、直す前の穴は書かずに、翌朝に直した。

直し方:回数はD1で、1回の命令で数える

回数を数える場所を、KVからD1に移した。D1はCloudflareのデータベースで、無料の書き込みは1日10万行まである。

数え方も変えた。前は「読む」と「書く」が別々で、書くほうが失敗しても気づけなかった。今は「上限より小さければ1を足して、新しい数を返す」を1回の命令で行う。返ってくるのは3通りだ。

3つ目が今回いちばん大事な変更だ。前は、書き込みの失敗は「数え損ねたけど通す」だった。今は「数えられないなら使わせない」。

命令そのものは、この3行だ。

INSERT INTO counters (key, count, expires_at) VALUES (?, 1, ?)
ON CONFLICT(key) DO UPDATE SET count = count + 1 WHERE count < ?
RETURNING count

同じメッセージを2回処理しないための番号も、D1に移した。「初めて見た番号なら記録する」を1回の命令で行う。こちらは失敗したら通す。二重に返事をするほうが、返事をしないよりましだからだ。費用は回数の上限の側で守る。

これで、AIがふつうに返事を1回するたびのKVへの書き込みは、約4回から1回(会話の履歴だけ)に減った。期限が過ぎた回数の記録は、毎日1回動く処理(Cron)で消す作りにした。手元のテストには、「AIの1日の枠が尽きた」「回数の記録に書き込めない」ときの確認を足した。

本番に出す順番には、1つ注意がある。新しいコードは、D1に回数を数える表があることを前提に動く。表を作る前にコードを出すと、すべての問い合わせが「数えられない」になり、全部がスタッフ行きになる。表を先に作り、コードはそのあとに出す。

直していない所

会話の履歴は、まだKVに残している。同じ人がほぼ同時に何通も送ると、履歴の1往復が抜けることがある。

対策の候補は、Cloudflareの別の仕組み(Durable Objects)で1人ずつ順番に処理させることだ。1日の問い合わせが数百件になったら手を付けるつもりで、今は保留にしている。

Workersで回数を数える人へ

私なら、回数を数える場所はKVに置かない。KVの無料枠は、読み取り10万回に対して書き込みが1,000回しかない。メッセージのたびに書くカウンターは、その少ないほうの枠をいちばん速く使う。

もう1つ。数え損ねたときにどちらへ倒すかを、先に決めておく。AIのように、呼ぶたびにお金がかかるものなら「数えられないなら使わせない」。

無料プランのままなら、KVの枠を超えてもCloudflareから請求は来ない。そこで止まるか、ブレーキが外れたまま走り続けるかは、自分のコードが書き込みの失敗をどう扱っているかで決まる。

→ 関連:公式LINEにAIの自動返信を付けた。アレルギーの質問は、AIに渡さない

参考