自分たちのサービスに、自分たちがレート制限をかけていた
MythexのAPIは、1つのネットワークアドレスから1分あたり300リクエストまで許可しています。この制限は、1人の利用者やスクリプトがAPIを連打することから守るためのものです。本番環境では、この制限が、私たち自身のサービスを拒否し始めました。対象は、公開済みアプリを配信するエッジの部分と、エージェントを動かすサービスです。それぞれが、ごく少数のアドレスからAPIにアクセスしているためです。内部の認証情報を伴うリクエストは、今では制限の対象外です。それ以外のすべては、以前とまったく同じ制限を受けます。
症状
APIが、内部からの呼び出しの一部に429 Too Many Requestsで応答するようになりました。影響を受けたのは3種類の呼び出しで、どれもMythexを使う人にとって重要なものです。
- カスタムドメインにあるアプリが、どこにあるかを調べる。 訪問者が独自のドメインで公開済みアプリを開くと、エッジは、そのサイトがどこにあるかをAPIに問い合わせます。ここで429が出ると、訪問者はサイトに到達できません。
- 実行中のエージェントのターンを保存する。 エージェントが作業している間、その進捗は作業しながら保存され、更新やネットワーク切断の後も応答が残るようになっています。ここで429が出ると、保存されなかった進捗が生じます。
- プレビューへのアクセスを確認する。 作っているアプリのプレビューは、アクセスが制御されており、その制御がAPIに問い合わせます。ここで429が出ると、プレビューが開けません。
どれも、APIを呼んだ人ではありませんでした。すべて私たち自身でした。
なぜ起きたのか
アドレスごとのレート制限は、1つの前提を置いています。1つのアドレスは、だいたい1人の呼び出し元である、という前提です。人間にとっては、それでほぼ十分です。しかし、私たち自身のインフラにとっては、まったく違います。
- 公開済みアプリとプレビューを配信するエッジサービスは、少数の共有の送信元アドレスからAPIに到達します。どの公開済みアプリのすべての訪問者も、すべてのプレビューの確認も、それら少数のアドレスのどれかから来たように見えます。
- オーケストレーター、つまりエージェントのターンを動かすサービスは、1つのコンテナのアドレスからAPIに到達します。実行中のすべてのターンが、すべてのユーザーについて、それを共有しています。
つまり、1人のための予算が、サービス全体のすべてのトラフィックで分け合われていたのです。それらのサービスは、合わせてその予算を使い果たし、レート制限は指示どおりに、それらを拒否しました。
修正
私たちのサービスは、自分が誰であるかをすでに証明しています。内部の呼び出しはすべて内部トークンを伴い、APIの内部セクションのルートは、何かをする前に必ずそのトークンを確認します。
レート制限は、今では同じチェックを使います。内部トークンと一致するリクエストは許可リストに入り、アドレスごとの予算には数えられません。それ以外のすべては、以前とまったく同じように数えられます。アドレスごとに1分300リクエストです。
これが抜け穴にならないよう、2つの細部に注意しました。
- 間違ったトークンでは通れません。 許可リストが一致するのは、本物の内部トークンだけです。偽のトークンを持つリクエストは、他のリクエストと同じように数えられ、制限されます。内部ルートは、それでも
401 Unauthorizedで拒否します。 - 制限そのものは変えていません。 余地を作るために数字を上げることはしませんでした。数字を上げると、トラフィックが再び増えるまで問題が隠れてしまいますし、制限が守るべき場面での保護も弱まってしまいます。
レート制限の設定は、小さな1つの関数にまとめ、それに対するテストを書きました。1つ目のテストは、内部トークン付きで20リクエストを、5回まで許可するように設定したリミッターに送り、20件すべてが成功することを期待します。2つ目のテストは、1つのアドレスから通常のリクエストを送り、最初の5件は成功し、それ以降は拒否されることを期待します。3つ目のテストは、偽造されたトークン付きのリクエストを送り、同じように拒否されることを期待します。
得られた教訓
- 制限には前提があります。 「1つのアドレスは、1つの呼び出し元」は、アドレスごとの制限に組み込まれています。自分のサービスが共有アドレスの背後に置かれるとすぐに、その前提は成り立たなくなります。そして多くのサービスは、ロードバランサー、エッジネットワーク、コンテナ、社内ネットワークの背後にあります。
- 自分のトラフィックは、アドレスではなく身元で区別してください。 アドレスはトラフィックが届く経路であり、誰が送ったかを示すものではありません。私たちはすでに、呼び出しが自分たちのものであることを証明する認証情報を持っていました。レート制限器が、それを見ていなかっただけです。
- 数字を上げて解決しないでください。 上限を上げれば、しばらくの間は429が消えたでしょう。しかし保護は弱まり、後で再び失敗したはずです。
- 誰が制限されているかを見てください。 自分のサービスに当たった429は、制限が機能している証拠ではなく、制限についてのバグ報告です。
Mythexで構築する人にとっての意味
あなたの側で変更すべきことはありません。カスタムドメインの公開済みアプリ、エージェントのターン、プレビューは、Mythexの他の部分がどれだけ混んでいるかに依存しなくなりました。ここで説明した制限は、私たちのAPIに対するものであり、あなたが公開するアプリに対するものではありません。あなたのアプリ自身のトラフィックは、あなたのアプリの問題です。
自分でバックエンドを動かしているなら、追加するどのレート制限にも、同じ罠が当てはまります。APIとは何かとAIで作ったアプリのセキュリティチェックリストが、出発点として適しています。また、私たちのインフラが自分自身の邪魔をしていた別の例については、デプロイを502エラー0にした方法をご覧ください。