Demystifying Frappe Cloud Pricing

Everyone's a little confused about this, let's fix that.

 · 10 min read

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

Frappe Cloud Plans

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:

  1. Keep a usage counter and set it to zero.
  2. At the beginning of each request, check if the counter has exceeded the limit.
  3. If it is not exceeded, then process the request. Business as usual.
  4. If it is, then do not process the request and respond with HTTP 429 (Too Many Requests).
  5. At the end of each request, increment the counter by how long the request took for processing.
  6. 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.

Frappe Cloud - Site Analytics

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.

  1. Go to the Awesome bar.
  2. Type Recorder.
  3. Click on Open Recorder.
  4. 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.

Recorder in action

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.

Daily CPU Time Consumed - frappe.io

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:

  1. For the sake of brevity, other request endpoints have been omitted from this report.
  2. 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:

Response after hitting daily limit - HTTP 429 Too Many Requests

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:

  1. Make website_script.js static and serve with NGINX.
  2. Make assets on frappe_io static and serve with NGINX.
  3. Remove socket.io related code from webpages that don't need it.
  4. 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.