GitHub

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.

Ledger Account Flags
Request Rate Operator 0
Request Rate User debits_must_not_exceed_credits

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

Transfer Ledger Debit Account Credit Account Amount
1 Request Rate Operator User 10

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

Transfer Ledger Debit Account Credit Account Amount Timeout Flags
2-N Request Rate User Operator 1 60 pending

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:

Ledger Account Flags
Bandwidth Operator 0
Bandwidth User debits_must_not_exceed_credits

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

Transfer Ledger Debit Account Credit Account Amount
1 Bandwidth Operator User 10000000

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

Transfer Ledger Debit Account Credit Account Amount Timeout Flags
2-N Bandwidth User Operator Request Size 60 pending

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.

Ledger Account Flags
Rate Limiting Operator 0
Rate Limiting User debits_must_not_exceed_credits
USD Operator 0
USD User debits_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:

Transfer Ledger Debit Account Credit Account Amount
1 Rate Limiting Operator User 1000

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

Transfer Ledger Debit Account Credit Account Amount Timeout Flags (Note | sets multiple flags)
2N Rate Limiting User Operator Transfer Amount 86400 pending | linked
2N + 1 USD User Destination Transfer Amount 0 0

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.

Edit this page