はじめに
スタンバイアドベントカレンダー10日目の記事です![]()
Vespaのレート制限(ratelimit)機能について調べる機会がありましたので、ご紹介させていただきます。
できること
ratelimitは一定の時間内にできる操作の回数を制限する仕組みのことを指すことが多いと思います。
Vespaにも同様の機能があり、主に以下のことが可能です。
- rate制限
- クエリによってrateの制限を変更する
vespaの公式に説明があります。
実施してみる!
環境準備
公式tutorialのGetting startedでクエリを実行するQuerying Vespaまで実施します。
※tutorialを実施すると以下の構成になりますが、以降はapp-1-getting-started配下のファイルを修正していきます。
news/
├── app-1-getting-started/
├── app-2-feed-and-query
├── app-3-searching
├── app-5-recommendation
├── app-6-recommendation-with-searchers
├── app-7-parent-child
├── doc.json
├── README.md
├── requirements.txt
ratelimitの設定
servicesの設定を変更
services.xmlにレート制限の設定を追加します。
<search>
<chain id="default" inherits="vespa">
<searcher id="com.yahoo.search.searchers.RateLimitingSearcher"/>
</chain>
</search>
query-profileの作成
設定を追加しただけでは、レート制限が有効にならないので、有効にするためのquery-profileを作成します。
ratelimitの設定をするものとしないもの2つ作成します。
いずれもapp-1-getting-started/search/query-profiles/に作成します。
※query-profileについてはこちらを参照ください。
- 設定しない(default.xml)
<query-profile id="default">
</query-profile>
- 設定する(ratelimit.xml)
<query-profile id="ratelimit">
<field name="rate.quota">2</field>
<field name="rate.id">default</field>
</query-profile>
rate.quota
rate.quotaは1秒間に消費できるコスト量です。
The cost per second a particular id is allowed to consume in this system.
1秒間に2回リクエストしたら制御がかかるわけではなく、容量の考え方があります。
容量の仕組み
rate.quota=2の場合、毎秒2個の容量がバケツに補充され、各リクエストが1個ずつ消費します。容量が不足するとレート制限が発動し429のエラーが返されます。
容量はリクエストされる度に、前回リクエストからの経過時間に基づいて加算されます。
容量プール: [●●] (2個)
↑ ↓
+2個/秒 -1個/req
0秒: [●●] → 2req処理 → [ ]
1秒: [ ] + 2個 → [●●] → 2req処理
2秒: [ ] + 2個 → [●●] → 2req処理
容量の追加は以下の箇所で実施されます。
デプロイ
設定が完了したらデプロイを実施します。(tutorial記載のデプロイです)
vespa deploy --wait 300 app-1-getting-started
動作確認
queryProfileを指定してリクエストを送る。
vespa query \
"select * from news where true" \
queryProfile=ratelimit \
"hits=1"
しばらく送り続けると以下のようなエラーになることが確認できます!
(容量の仕組み上、すぐにエラーにならない可能性があるため、スクリプトで連続実行することをお勧めします。)
Error: invalid query: 429 Too Many Requests
{
"root": {
"id": "toplevel",
"relevance": 1.0,
"fields": {
"totalCount": 0
},
"errors": [
{
"code": 429,
"summary": "Too many requests",
"message": "Allowed rate: 2.0/s"
}
]
}
}
queryProfile=ratelimitの箇所をqueryProfile=defaultに変えてリクエストした場合、このプロファイルには設定がないため、何度実行しても429エラーは発生しません。
query-profileを切り替えることで、レート制限の有効/無効を使い分けることが可能です。
クエリ毎にratelimitの設定を変更
ratelimit.xmlを以下のように修正します。
<query-profile id="ratelimit">
<dimensions>clientId</dimensions>
<field name="rate.quota">1</field>
<field name="rate.id">default</field>
<field name="rate.idDimension">clientID</field>
<query-profile for="clientA">
<field name="rate.quota">2</field>
<field name="rate.id">default</field>
<field name="rate.idDimension">clientID</field>
</query-profile>
<query-profile for="clientB">
<field name="rate.quota">3</field>
<field name="rate.id">default</field>
<field name="rate.idDimension">clientID</field>
</query-profile>
</query-profile>
このようにすると、clientIdをパラメータに指定しない場合、またはclientA・clientB以外の値を指定した場合は、rate.quota:1が適用されます。
仮にclientBで指定して429エラーになると以下のようになります。
vespa query \
"select * from news where true" \
queryProfile=ratelimit \
"clientId=clientB" \
"hits=1"
Error: invalid query: 429 Too Many Requests
{
"root": {
"id": "toplevel",
"relevance": 1.0,
"fields": {
"totalCount": 0
},
"errors": [
{
"code": 429,
"summary": "Too many requests",
"message": "Allowed rate: 3.0/s"
}
]
}
}
このようにquery-profile内でクエリごとの設定を動的に変更することが可能です。
パラメータの補足
公式に記載があるようにパラメータを指定することが可能です。
クエリの重みを指定するrate.costや429のエラーにはせずにメトリクスのみ取得するrate.dryRunのパラメータなどもあります。
こちらにはパラメータとして記載されていないのですが、実際のレート制限コードを確認すると、前述の容量設定やレート制限をクラスターとノードのどちらの単位で解釈するのかなども変更することが可能です。
container-search/src/main/java/com/yahoo/search/searchers/RateLimitingSearcher.java
以下の定義ファイルにデフォルト値とパラメータの項目名が記載されています。
container-search/src/main/resources/configdefinitions/search.config.rate-limiting.def
設定を変更する際はservices.xmlを修正します。以下のようにconfigタグを追加します
<container id='default' version='1.0'>
<search>
<chain id="default" inherits="vespa">
<searcher id="com.yahoo.search.searchers.RateLimitingSearcher" />
</chain>
<config name="search.config.rate-limiting">
<capacityIncrement>99</capacityIncrement>
<localRate>true</localRate>
</config>
</search>
<document-api></document-api>
<nodes>
<node hostalias='node1'></node>
</nodes>
</container>
デプロイ後、設定を確認するためのAPIを実行し、変更が反映されていることを確認できます。
curl -s "http://localhost:19071/config/v1/search.config.rate-limiting/default/searchchains/chain/default" | jq .
{
"capacityIncrement": 99.0,
"maxAvailableCapacity": 10000.0,
"recheckForCapacityProbability": 0.001,
"localRate": true
}
さいごに
VespaのRateLimitingSearcherは、柔軟で高機能なレート制限システムです。クエリプロファイルによる動的な設定変更により、様々な要件に対応できます。
特に本番環境での導入時は、rate.dryRunを活用したメトリクス監視から始めることで、安全かつ効果的な運用が可能になると感じます。
最後までお読みいただき、ありがとうございました。
参考