Serverless Instances Billing 101: How to Optimize Your Bill with Indexing
Rate this article
Serverless solutions are quickly gaining traction among developers and organizations alike as a means to move fast, minimize overhead, and optimize costs. But shifting from a traditional pre-provisioned and predictable monthly bill to a consumption or usage-based model can sometimes result in confusion around how that bill is generated. In this article, we’ll take you through the basics of our serverless billing model and give you tips on how to best optimize your serverless database for cost efficiency.
MongoDB Atlas serverless instances, recently announced as , provide an on-demand serverless endpoint for your application with no sizing required. You simply choose a cloud provider and region to get started, and as your app grows, your serverless database will seamlessly scale based on demand and only charge for the resources you use.
Unlike our traditional clusters, serverless instances offer a fundamentally different that is primarily metered on reads, writes, and storage with automatic tiered discounts on reads as your usage scales. So, you can start small without any upfront commitments and never worry about paying for unused resources if your workload is idle.
Pay only for the operations you run.
|Read Processing Unit (RPU)||Number of read operations* to the database|
*Sum of documents read divided by 4KB and index bytes read divided by 256 bytes
|$0.10/million for the first 50 million per day*|
*Daily RPU tiers: Next 500 million: $0.05/million Reads thereafter: $0.01/million
|Write Processing Unit (WPU)||Number of write operations* to the database|
*Sum of documents and index bytes written, divided by 1KB
|Storage||Data and indexes stored on the database||$0.25/GB-month|
|Standard Backup||Download and restore of backup snapshots*|
*2 free daily snapshots included per serverless instance
*To download or restore the data
|Serverless Continuous Backup||35-day backup retention for daily snapshots||$0.20/GB-month|
|Data Transfer||Inbound/outbound data to/from the database||$0.015 - $0.10/GB*|
*Depending on traffic source and destination
At first glance, read processing units (RPU) and write processing units (WPU) might be new units to you, so let’s quickly dig into what they mean. We use RPUs and WPUs to quantify the amount of work the database has to do to service a query, or to perform a write. To put it simply, a read processing unit (RPU) refers to the read operations to the database and one RPU is typically equivalent to either one document or one index entry scanned. Similarly, write processing units (WPUs) are write operations to the database, with one WPU typically being equivalent to each document or index entry written. For further explanation of cost units, please refer to our .
Now that you have a basic understanding of the pricing model, let’s go through an example to provide more context and tips on how to ensure your operations are best optimized to minimize costs.
Now, let’s take a look at what happens when we interact with our data and do some search queries.
For this exercise, I chose the sample_weatherdata collection. While looking at the data in the Atlas Collections view, it’s clear that the weather data collection has information from various places and that most locations have a call letter code as a convenient way to identify where this weather reading data was taken.
For this example, let’s simulate what would happen if a user comes to your weather app and does a lookup by a geographic location. In this weather data collection, geographic locations can be identified by callLetters, which are specific codes for various weather stations across the world. I arbitrarily picked station code “ESVJ,” which is a weather buoy in the Atlantic Ocean.
Here is what we see when we run this query in Atlas Data Explorer:
We can see this query returns three records. Now, let’s take a look at how many RPUs this query would cost me. Roughly, we should remember that:
RPU ≅ number of documents scanned. (In reality, if documents are larger than 4KB, then you might be charged more than 1 RPU per document.)
To execute the previous query, a full collection scan is required. Therefore, to run the query, all 10,000 documents in the collection are scanned, resulting in 10,000 RPUs (since each document is less than 4KB in length).
I took this query and ran this nearly 3,000 times through a shell script. This will simulate around 3,000 users coming to an app to check the weather in a day. Here is the code behind the script:
As expected, 3,000 iterations will be 10,000 * 3,000 = 30,000,000 RPUs = 30MM RPU = $3.00.
You can see the invoice below:
Based on this, the cost per user for this application would be $0.10 per user (calculated as: 30,000,000 / 3000 = 10,000 RPUs = $0.10).
The cost of $0.10 per user seems to be very high for a database lookup, because if this weather app were to scale to reach a similar level of activity to , who sees about 9.5B weather requests in a day, you’d be paying close to around $1 billion in database costs. By leaving your query this way, it’s likely that you’d be faced with an unexpectedly high bill as your usage scales—falling into a common trap that many new serverless users face.
Without indexes, MongoDB must perform a collection scan—i.e., scan every document in a collection—to select those documents that match the query statement (something you just saw in the example above). By adding an index to appropriate queries, you can limit the number of documents it must inspect, significantly reducing the operations you are charged for.
Let’s look at how indexing can help you reduce your RPUs significantly.
First, let’s create a simple index on the field ‘callLetters’:
This operation will typically finish within 2-3 seconds. For reference, we can see the size of the index created on the index tab:
Due to the data structure of the index, the exact number of index reads are hard to compute. However, we can run the same script again for 3,000 iterations and again look at the invoice to compare.
As seen on the invoice above, 3,000 queries on the indexed field did not change my bill at all, but you can see a slight increase in the RPUs (highlighted in blue). This is because the cost of running this operation was less than $0.01. Comparing the number of RPUs from the previous screenshot, we can see that they increased by 0.035M, which is around 35,000 RPUs, in contrast to the 30 million RPUs from the un-indexed query:
(30,000,000 - 35,000)/30,000,000 * 100 = 99.88% reduction
We can see that by simply adding the above index, we were able to reduce the cost per user to roughly $0.0001167 (calculated as: 35,000 / 3000 = 11.67 RPUs = $0.0001167), which is a huge cost savings compared to the previous cost of $0.10/user.
Therefore, indexing not only helps with improving the performance and scale of your queries, but it can also reduce your consumed RPUs significantly, which reduces your costs. Note that there can be rare scenarios where this is not true (where the size of the index is much larger than the number of documents). However, in most cases, you should see a significant reduction in cost and an improvement in performance.
As you can see, adopting a usage-based pricing model can sometimes require you to be extra diligent in ensuring your data structure and queries are optimized. But when done correctly, the time spent to do those optimizations often pays off in more ways than one.
If you’re unsure of where to start, we have available in the Atlas UI that can help you. The automatically monitors your database for slow-running queries and will suggest new indexes to help improve query performance. Or, if you’re looking to investigate slow-running queries further, you can use to view a breakdown of all slow-running queries that occurred in the last 24 hours. If you prefer a terminal experience, you can also analyze your in the MongoDB Shell or in MongoDB Compass.