Skip to main content

Rate Limiting

TigerBeetle can be used to account for non-financial resources.

In this recipe, we will show you how to use it to implement rate limiting using the leaky bucket algorithm based on the user request rate, bandwidth, and money.

Mechanism

For each type of resource we want to limit, we will have a ledger specifically for that resource. On that ledger, we have an operator account and an account for each user. Each user's account will have a balance limit applied.

To set up the rate limiting system, we will first transfer the resource limit amount to each of the users. For each user request, we will then create a pending transfer with a timeout. We will never post or void these transfers, but will instead let them expire.

Since each account's "balance" is limited, we cannot create pending transfers that would exceed the rate limit. However, when each pending transfer expires, it automatically replenishes the user's balance.

Request Rate Limiting

Let's say we want to limit each user to 10 requests per minute.

We need our user account to have a limited balance.

LedgerAccountFlags
Request RateOperator0
Request RateUserdebits_must_not_exceed_credits

We'll first transfer 10 units from the operator to the user.

TransferLedgerDebit AccountCredit AccountAmount
1Request RateOperatorUser10

Then, for each incoming request, we will create a pending transfer for 1 unit back to the operator from the user:

TransferLedgerDebit AccountCredit AccountAmountTimeoutFlags
2-NRequest RateUserOperator160pending

Note that we use a timeout of 60 (seconds), because we wanted to limit each user to 10 requests per minute.

That's it! Each of these transfers will "reserve" some of the user's balance and then replenish the balance after they expire.

Bandwidth Limiting

To limit user requests based on bandwidth as opposed to request rate, we can apply the same technique but use amounts that represent the request size.

Let's say we wanted to limit each user to 10 MB (10,000,000 bytes) per minute.

Our account setup is the same as before:

LedgerAccountFlags
BandwidthOperator0
BandwidthUserdebits_must_not_exceed_credits

Now, we'll transfer 10,000,000 units (bytes in this case) from the operator to the user:

TransferLedgerDebit AccountCredit AccountAmount
1BandwidthOperatorUser10000000

For each incoming request, we'll create a pending transfer where the amount is equal to the request size:

TransferLedgerDebit AccountCredit AccountAmountTimeoutFlags
2-NBandwidthUserOperatorRequest Size60pending

We're again using a timeout of 60 seconds, but you could adjust this to be whatever time window you want to use to limit requests.

Transfer Amount Limiting

Now, let's say you wanted to limit each account to transferring no more than a certain amount of money per time window. We can do that using 2 ledgers and linked transfers.

LedgerAccountFlags
Rate LimitingOperator0
Rate LimitingUserdebits_must_not_exceed_credits
USDOperator0
USDUserdebits_must_not_exceed_credits

Let's say we wanted to limit each account to sending no more than 1000 USD per day.

To set up, we transfer 1000 from the Operator to the User on the Rate Limiting ledger:

TransferLedgerDebit AccountCredit AccountAmount
1Rate LimitingOperatorUser1000

For each transfer the user wants to do, we will create 2 transfers that are linked:

TransferLedgerDebit AccountCredit AccountAmountTimeoutFlags (Note \| sets multiple flags)
2NRate LimitingUserOperatorTransfer Amount86400pending | linked
2N + 1USDUserDestinationTransfer Amount00

Note that we are using a timeout of 86400 seconds, because this is the number of seconds in a day.

These are linked such that if the first transfer fails, because the user has already transferred too much money in the past day, the second transfer will also fail.