This is one of a series of articles about Going serverless with Firebase. You many want to read about Firebase cloud functions and Custom domains and ssl with Firebase hosting before this article.
Rate limiting
So you’ve created your cloud function and have a firestore backend. There are abuse limits built into the Firebase platform, but you’ll probably want to limit usage to ensure you keep your hosting costs under control too. In this example, there are a number of rate limits and quotas to enforce, based on the level of subscription of an account.
Here’s an extract from an example configuration, with 2 plans and 4 limits per plan
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
plans: { "a": { "limiters": { "burst": { "seconds": 30, "rate": 30 }, "minute": { "seconds": 120, "rate": 60, }, "day": { "seconds": 86400, "rate": 2000 }, "dailywrite": { "seconds": 86400, "rate": 10240000, "type": "quota" } } }, "b": { "limiters": { "burst": { "seconds": 30, "rate": 60 }, "minute": { "seconds": 120, "rate": 180 }, "day": { "seconds": 86400, "rate": 20000 }, "dailywrite": { "seconds": 86400, "rate": 102400000, "type": "quota" } } }, |
1 2 3 4 |
"minute": { "seconds": 120, "rate": 60, }, |
Firestore slotlimit document
Each account is associated with a particular plan, and has a firestore document like this, with a property corresponding to each of the limits that are being imposed. It also has an expiry date to clean up unused documents when no longer active.
Getting a slot
A slot is a window over which a rate is being measured. Calculating the current slot is simply a matter of dividing the current time by the size of the window, then each of the rates for the current slot can be checked to see if there is enough quota left to complete the proposed operation. Every operation starts with a fetch of the slot information for the current account
1 |
return dbStore.getSlotLimit (accountId).then (pack=> { |
1 |
const nowSecs = new Date().getTime()/1000; |
1 2 3 |
const ob = Object.keys(plan.limiters) .reduce((p,name)=>{ const co = plan.limiters[name]; |
1 |
const slot = Math.floor(nowSecs/co.seconds); |
1 2 3 4 5 6 7 |
p[name] = currentOb[name]; if (!p[name] || p[name].slot !== slot) { p[name] = { used:0, slot:slot }; } |
1 2 3 4 5 6 7 |
p[name].used += (co.type === "quota" ? volume : 1); manage.errify( p[name].used <= co.rate, "QUOTA", name + " quota/rate limit exceeded", pack ); |
1 2 |
return p; },{}); |
1 |
dbStore.setSlotLimit (accountId,ob) |
otherwise report a quota violation
And that’s all there is to it.
The complete code for the cloud function available on github
For other articles on this topic see Going serverless with Firebase. For more snippets, see Google Apps Scripts snippets