メインコンテンツまでスキップ

レート制限とクォータ制限

Squid バックエンドの関数にレート制限とクォータ制限を設定する

この機能を使うと、バックエンドに記述した関数に対して制限を定義できます。

  • ユーザーごと、IP アドレスごと、グローバルなど、スコープごとに制限を定義できます。
  • 1時間ごとから年ごとまでのクォータ更新期間を指定できます。更新期間は renewPeriod パラメータで定義し、hourlydailyweeklymonthlyquarterlyannually を指定できます。
  • これらの特性をスタックして、複雑な制限を作成できます。

なぜバックエンドを制限するのか?

バックエンド関数にレート制限やクォータ制限を実装することで、不正利用の防止、リソース保護、コスト削減、サービス拒否(DoS)攻撃の緩和、そしてサービス品質の確保に役立ちます。

@limits デコレーター

Squid はデコレーターを提供しており、特定の executablewebhook、または OpenAPI 関数に適用する制限を定義するために使用できます。

次の例では、1 秒あたり 5 クエリ1 か月あたり 200 クエリの制限が適用されます。

Backend code
import { executable, limits, SquidService } from '@squidcloud/backend';

export class ExampleService extends SquidService {
@limits({ rateLimit: 5, quotaLimit: 200 })
@executable()
concat(str1: string, str2: string): string {
return `${str1}${str2}`;
}
}

@limits デコレーターは、任意の 2 つのパラメータ rateLimitquotaLimit を受け取ります。

rateLimit

rateLimit は次の 3 つの形式で定義できます。

  • 関数を制限したい 1 秒あたりのクエリ数を表す number。これはデフォルトで global スコープです。
@limits({ rateLimit: 5 })
  • スコープのカスタマイズを有効にする objectscope パラメータには useripglobal を指定できます。
@limits({ rateLimit: { value: 7, scope: 'user' } })
  • 複数の制限をスタックできる object のリスト
@limits({
rateLimit: [
{ value: 5, scope: 'user' },
{ value: 10, scope: 'ip' }
]
})
Note

このリスト内のすべての制限は、各クエリごとに消費されます。最初に消費された制限が超過を示した場合でも、例外がクライアントに返される前に他のすべての制限も消費されます。複数の制限が同じクエリを拒否している場合、最初に拒否したものがクライアントに返されます。

quotaLimit

quotaLimit も同じ 3 つの形式で定義できます。

  • 関数をクエリできる合計回数を表す number。これはデフォルトで global スコープ、monthly 更新期間です。
@limits({ quotaLimit: 5 })
  • スコープおよび更新期間のカスタマイズを有効にする object
@limits({ quotaLimit: { value: 7, scope: 'user', renewPeriod: 'annually' } })

注: scoperenewPeriod は任意で、省略した場合でも globalmonthly のデフォルトが適用されます。

  • 複数の制限をスタックできる object のリスト
@limits({
quotaLimit: [
{ value: 7, scope: 'user', renewPeriod: 'monthly' },
{ value: 20, scope: 'user', renewPeriod: 'annually' }
]
})

rate と quota の両方の制限を使用する

2 つのパラメータを一緒に使うことで、レート制限とクォータ制限の両方を定義できます。

@limits({
rateLimit: [
{ value: 5, scope: 'user' },
{ value: 10, scope: 'ip' }
],
quotaLimit: [
{ value: 7, scope: 'user', renewPeriod: 'monthly' },
{ value: 20, scope: 'user', renewPeriod: 'annually' }
]
})

これらの制限がどのように評価されるかの詳細は、enforcement セクションを参照してください。

制限を理解する

制限はいくつでも定義できます。すべての制限は各クエリごとに消費されます。いずれかの制限を超過すると、それが他のすべての制限を上書きし、クエリは拒否されます。

たとえば、ある関数に「月次クォータ 5 クエリ」と「年次クォータ 10 クエリ」があるとします。

@limits({
quotaLimit: [
{ value: 5, renewPeriod: 'monthly' },
{ value: 10, renewPeriod: 'annually' }
]
})

月の最初の週に 5 クエリが実行されると、年次クォータに到達していなくても、その月の残り期間は追加のクエリが許可されません。同様に、年の最初の 2 か月で毎月 5 クエリずつ実行され合計 10 クエリになると、残りの月で月次クォータに達していなくても、その年の残り期間は追加のクエリが許可されません。

Enforcement

レート制限は常に評価され、クエリが拒否された場合でも消費されることがあります。これが何を意味するのか、2 つの例で見てみましょう。

レート制限に達する場合

次の設定例を考えます。

@limits({ rateLimit: 5, quotaLimit: 20 })

予算のタイムラインは次のとおりです。

EventRate Budget RemainingQuota Budget RemainingOutcome
Starting values520
Make 5 queries015Queries succeed
Make a 6th query015Rate limit rejection

レート制限が 6 回目のクエリを拒否し、クォータ制限は消費されません。

クォータ制限に達する場合

一方、クォータ超過では、常にレート制限の消費が発生します。

次の設定例を考えます。

@limits({ rateLimit: 10, quotaLimit: 5 })

予算のタイムラインは次のとおりです。

EventRate Budget RemainingQuota Budget RemainingOutcome
Starting values105
Make 5 queries50Queries succeed
Make a 6th query40Quota limit rejection

クォータ制限が 6 回目のクエリを拒否しますが、レート制限は 4 まで消費されます。

制限を超過した場合

制限を超えると、関数は状況に応じて "Rate limit on name exceeded" または "Quota on name exceeded" というメッセージを含む例外を返します。

name は、関数名、スコープ、(クォータ制限の場合は)更新期間を含む文字列です。スコープが user または IP の場合、ユーザー ID または IP アドレスも文字列に含まれます。

ユーザー/IP ベースの制限でユーザーまたは IP が不明な場合

ユーザー/IP ベースの制限を定義する際、何らかの理由で特定クライアントのユーザーまたは IP が不明な場合、そのクライアントは他の不明クライアントすべてと同じ単一の unknown エンティティとしてバケット化されます。つまり、ログインしていないすべてのユーザーは単一のユーザーと見なされ、同じレート/クォータバケットを消費します。

たとえば、次の制限がある場合:

@limits({ rateLimit: { value: 7, scope: 'user' } })

ログイン済みユーザーはそれぞれ 1 秒あたり 7 クエリの専用バケットを持ちますが、不明ユーザーは 1 秒あたり 7 クエリの単一バケットを共有します。

Atomicity

クエリがバッチとして送信され、バッチ途中で制限に到達した場合、バッチ全体が拒否されます。これにより、部分的な変更が行われないことが保証されます。

補充と更新

クォータ更新

未使用のクォータは次の期間に繰り越されません。各クォータには更新期間が定義されており、その正確な期間は次のとおりです。

PeriodDuration
hourly1 hour
daily1 day
weekly7 days
monthly30 days
quarterly90 days
annually365 days

Squid は次の 2 つの方法でクォータを更新します(先に満たされた方)。

  1. Periodically: 毎時の先頭(各時刻の 0 分)に、各クォータ制限が更新対象かどうかチェックされます。
  2. On demand: クエリがクォータを超過した場合でも、その時点でクォータが更新対象であれば更新されます。

クォータ期間の開始時刻は、その特定のクォータ(関数、スコープ、更新期間、値のユニークな組み合わせ)がバックエンドのデプロイで初めて導入された時刻です。

レート制限の補充

消費バケットは徐々に補充され、指定レートの最大 3 倍までのバーストを許容します。

Note

Gradual refill の例: @limits({ rateLimit: 5 }) を定義し、クライアントが制限を超えた場合、次のクエリを行うために必要な待ち時間は 1/5 秒(0.2 秒)だけです。

制限の変更

新しいバックエンドをデプロイすることで、制限はいつでも変更できます。クォータについては、特定の「limit combo」(関数、スコープ、renewPeriod のユニークな組み合わせ)に対する制限値を変更すると、アクティブなカウントがリセットされます。たとえば、ユーザーが 10 回呼び出していて、制限が 20 から 15 に変更された場合、そのユーザーは(残り 5 回ではなく)さらに 15 回呼び出せます。新しいバックエンドのデプロイで特定の「limit combo」に変更がない場合、アクティブなカウントはリセットされません。

アカウントへの影響を理解する

定義した制限の超過によって関数呼び出しが拒否された場合、その呼び出しは請求対象の利用量には カウントされません。ただし、Squid は課金プランに関連するクォータを維持しており、定義した制限で拒否されたかどうかに関係なく、すべてのクエリを課金プランのクォータに対して カウントします

たとえば、次のクォータ制限を定義した場合:

@limits({ quotaLimit: 5 })

そして 8 回クエリを行うと、最初の 5 回は成功し、最後の 3 回は拒否されます。請求されるのは成功した 5 回のみですが、Squid はアカウントのクォータに対して 8 回分のクエリをカウントします。

Squid のクォータと課金に関する詳細は、Quotas and limits documentation を参照してください。