Demystifying Frappe Cloud Pricing
Everyone's a little confused about this, let's fix that.
This blog is an attempt to explain how the Frappe Cloud pricing model works. We will see what the limits described in the plans mean? How are they enforced? What we measure? What we don't measure? How you can perform these measurements yourself? How they translate into daily usage? How would you choose a plan for your site? What happens when you hit the limits of your plan?
How Does Frappe Cloud Pricing Work?
Plan | CPU Time |
---|---|
$10 | 1 hr / day |
$25 | 2 hr / day |
$50 | 4 hr / day |
$100 | 8 hr / day |
$200 | 16 hr / day |
$500 | 32 hr / day |
$1000 | 64 hr / day |
Overview
When you create a site you will be prompted to choose a plan. The site doesn't have a user limit. However, it is limited to certain CPU hours per day.
Let me explain what limits on CPU hours per day means. Instead of worrying about the number of CPU cores, amount of RAM, disk size, etc, we focus only on one resource: CPU time. More specifically, CPU time consumed in HTTP requests.
Let me explain what CPU time consumed in requests means before you start scratching your head...
The story of background jobs and requests
An HTTP request made on your site takes a certain amount of time to be processed. We measure this time for every request and keep track of it. Along with this, we also track the total time consumed by all requests made on your site. I will share how you can measure this yourself shortly.
Every request you make increments the total time consumed. When the total time consumed exceeds your plan's quota, we don't process subsequent requests and return HTTP 429 (Too Many Requests) response instead. At the end of the day (based on your site's timezone), we set total time consumed to zero, starting a new cycle.
Usually, a background job consumes more time than a request. But we can defer the execution of a background job, unlike a request which has to be processed as soon as it is received. Taking this into account, we do not consider the time consumed in the background jobs, backups, and updates. We only consider requests.
Furthermore, we don't consider the time consumed by requests that do not reach Gunicorn which is the Python WSGI server used by the Frappe framework. Not every request that is made from the browser reaches Gunicorn. For instance, the static assets (CSS, JS, images, fonts, etc) are served by NGINX, as well as the WebSocket requests that are served by the Socket.io server - these requests don't reach Gunicorn.
We don't bill you based on how much CPU time you have used, we bill you based on the quota requested, irrespective of whether you have used that quota or not. By design, you will not be allowed to exceed this quota.
Let me explain this with an example: If you choose the $50 plan, you will be allowed to use 4 CPU hours/day, and irrespective of the actual usage, you will be billed:
$50 / 30 = $1.67 (at the end of the day)
For every day your site is active, you will be billed $1.67. Essentially, if you use your site for only 20 days, we will bill you for 20 days, and your bill will be:
20 * $1.67 = $33.4
Your card will be charged at the end of the month.
In case you aren't using a site and don't want us to bill you, you can either drop that site or make it inactive.
Disclaimer: You will not be able to use an inactive site, and background jobs will also not run on this site.
Again, I can not believe I have to say this, but, We do not measure, limit, or bill based on how long a user sat in front of the computer, because that's just stupid.
We can use the same systems that implement this pricing model to implement another pricing model. For example at the end of every day, we can count the CPU time consumed for that day and bill based on the consumption, this is also known as metered billing. And yes, this would solve the problem of having to decide the plan beforehand. However, the metered billing model brings the unpredictability of pricing. With the current pricing model, you can choose a plan and rest assured that you know what you will pay for the month.
How are we getting this done?
We have introduced two features in Frappe framework, Rate Limiting, and Monitor.
Rate Limiting
Rate limiting configuration includes two variables:
- limit
- window
Key | Description |
---|---|
limit | The maximum amount of time permitted to use in the rate limit window (in seconds) |
window | Size of the rate limit window (in seconds) |
If the configuration is set to a limit of 3600 seconds, and a window of 86400 seconds, it translates to 1 Hour of CPU time every day.
Below are the steps of how the rate-limiting feature works:
- Keep a usage counter and set it to zero.
- At the beginning of each request, check if the counter has exceeded the limit.
- If it is not exceeded, then process the request. Business as usual.
- If it is, then do not process the request and respond with HTTP 429 (Too Many Requests).
- At the end of each request, increment the counter by how long the request took for processing.
- At the end of the window, reset to the counter.
For every request, we set some headers to give the current state of the counter and limit
For a request within quota
curl -i https://frappe.io/docs
HTTP/1.1 200 OK
X-RateLimit-Limit: 600000000
X-RateLimit-Remaining: 518060453
X-RateLimit-Reset: 3513
X-RateLimit-Used: 100560
For a request above quota
curl -i https://frappe.io/docs
HTTP/1.1 429 TOO MANY REQUESTS
X-RateLimit-Limit: 600000000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1242
Retry-After: 1242
This what the header values mean:
Header | Description |
---|---|
Retry-After | Time remaining until the current rate limit window resets (in seconds). |
X-RateLimit-Limit | Time permitted to use in a rate limit window (in microseconds). |
X-RateLimit-Remaining | Time remaining (to be used) in the current rate limit window (in microseconds). |
X-RateLimit-Reset | Time remaining until the current rate limit window resets (in seconds). |
X-RateLimit-Used | Time used for processing the current request (in microseconds). |
Monitor
Monitor records metadata of every HTTP request and background job. For a request, this includes the site, IP address that initiated the web request, path, HTTP method, timestamp of the request, and how long the request took to complete. Here is a sample of metadata captured for a request:
{
"duration": 5462,
"request": {
"counter": 81939547,
"ip": "127.0.0.1",
"method": "GET",
"path": "/api/method/frappe.ping",
"response_length": 18,
"status_code": 200
},
"site": "frappe.io",
"timestamp": "2020-02-20 09:37:17.397884",
"transaction_type": "request",
"uuid": "83be6a4c-27a1-497a-9ce6-c815bca4e420"
}
This data is periodically flushed into a log file. This file is parsed and you can see the results in the Analytics tab of your account.
Note: The data in the Analytics tab is updated every 5 minutes.
This means you can observe the current state of rate-limiting. You can visit the Analytics tab of your site. You can
make an HTTP request from your site, and observe the values of X-RateLimit
headers.
How long does a request take?
You must be wondering if you can get an estimate of how long a request may take. If you want to see how long a request takes, there are several ways to find out:
- You can sprinkle print statements in the code like a caveman.
- Or use one of the better tools that come with our framework, like Recorder.
For every SELECT, UPDATE, DELETE query made during the request recorder makes an EXPLAIN query. Because of this, typically a request measured with the recorder takes slightly longer to process.
- Go to the Awesome bar.
- Type
Recorder
. - Click on
Open Recorder
. - Click
Start
so that you can start recording your requests.
All requests made after this will be recorded. In the Duration field, you will see the time taken by each request in milliseconds.
Click on Stop
if you want to stop recording.
Note: Keeping the Recorder active will cause performance degradation due to its introspective nature.
Now, you know how long a request takes. But how does this look in terms of daily usage? Let's dive deeper into a study of frappe.io.
Case study - Frappe.io
Let us consider the example of frappe.io, there are 40+ users using this site, and frappe.io is also a website.
Let us see how much CPU hours does frappe.io consume in a day?
Observe the usage pattern from logs for 30 days from April 15th, 2020 to May 14th, 2020.
Every day, CPU Usage is under 4 hours. If frappe.io were to move to Frappe Cloud, we can choose the $50 (4 hours) plan. Now the question remains, what do 4 hours every day mean for this site?
Here is a summary of all the requests:
Endpoint | Total Requests | Daily Requests | Total Time (seconds) |
---|---|---|---|
Website | 1566847 | 52228 | 91823 |
API | 2502077 | 83402 | 80628 |
Here is a breakdown of API requests on common HTTP endpoints:
Endpoint | Total Requests | Daily Requests | Total Time (seconds) | Time/Request (milliseconds) |
---|---|---|---|---|
login | 13274 | 442 | 2690 | 202 |
/desk | 12879 | 429 | 4874 | 378 |
/api/method/frappe.desk.form.load.getdoc | 56099 | 1869 | 9823 | 175 |
/api/method/frappe.desk.reportview.get | 474002 | 15800 | 19251 | 40 |
/api/method/frappe.desk.form.save.savedocs | 13853 | 461 | 5677 | 409 |
/api/method/frappe.desk.query_report.run | 2124 | 70 | 2129 | 1002 |
This breakdown translates to approximately:
Endpoint | Total Hits | Daily Hits |
---|---|---|
Web page view | 1500k | 50k |
Login | 13k | 400 |
Desk page load | 12k | 400 |
Form view | 56k | 1800 |
List/Report view | 24k | 8k |
Form save/submit | 13k | 400 |
Query Report | 2100 | 70 |
Hardware specification (frappe.io): 4 Core/ 8 Thread - 64 GB RAM bare-metal server.
Notes:
- For the sake of brevity, other request endpoints have been omitted from this report.
- Every List/Report view accounts for approximately 2 /api/method/frappe.desk.reportview.get requests.
Frequently Asked Questions
How do I decide which plan to choose?
You can either estimate your usage based on the information provided in this blog. Or you can start with a plan that is closest to the cost you currently incur. Based on the usage report in the Analytics tab, you can change your plan anytime. Pay as per your usage, not the number of users.
If you choose a plan that allows less CPU time than what is required for your site, then all the requests above quota are not processed and responded with HTTP 429 (Too Many Requests). I think by now you understand what this means. It looks like this in the browser:
We are working on making this experience better. Our idea is to throttle the requests after the quota has been exceeded instead of stopping everything to a screeching halt. We will notify you with an email when you reach 80% of your daily quota.
If you have hit your daily limit, but want to keep using your site anyway, then you can upgrade your plan and your quota will be immediately increased.
I host many small sites on one/two servers. This is too expensive for me.
Perhaps, you should consider offloading the grunt work of hosting and management to someone else, maybe us, so that you can focus on your business, while we take care of the infrastructure.
Isn't this more expensive than other providers like EC2 or DigitalOcean?
When EC2 or DigitalOcean say you get 2 CPU Cores and 4GB RAM, it might not necessarily mean you get dedicated access to 100% capacity of your cores.
If you are using AWS ec2 t2, t3, and t3a instances then reading about CPU credits and baseline performance for burstable instance types might help you understand better.
If you are using DigitalOcean Standard droplets then consider reading about performance difference between Standard and general-purpose droplets.
Neither DigitalOcean nor EC2 provides you the managed Frappe hosting experience that Frappe Cloud brings in. You still have to manage your updates, custom domains, backups separately.
Also, Don't pay Jeff.
Actionables (for us)
While analyzing the logs for frappe.io, we came across some challenges, that if resolved would reduce resource consumption, like:
- Make website_script.js static and serve with NGINX.
- Make assets on frappe_io static and serve with NGINX.
- Remove socket.io related code from webpages that don't need it.
- Implement throttling for requests beyond the quota.
I am hoping this blog clarifies the pricing model of Frappe Cloud. In case you have more questions, please join the Frappe Cloud - Public Support telegram group.