DEV Community

Cover image for Solved: What’s the annual revenue of your store?
Darian Vance
Darian Vance

Posted on • Originally published at wp.me

Solved: What’s the annual revenue of your store?

🚀 Executive Summary

TL;DR: Running heavy analytics queries, such as calculating annual revenue, directly on production databases causes severe performance degradation and downtime due to workload contention. The solution involves isolating these analytical workloads using read replicas and optimizing queries with proper indexing to maintain the primary database’s transactional performance.

🎯 Key Takeaways

  • Production databases (OLTP) are optimized for fast, small transactional operations, making them unsuitable for long-running, heavy analytical queries (OLAP) which cause workload contention.
  • Implementing appropriate indexes, such as on a created\_at timestamp column, can transform a full table scan for date-based analytical queries into a highly efficient indexed scan, drastically improving performance.
  • Utilizing read replicas provides a ‘grown-up’ architectural solution by creating a near-real-time copy of the production database dedicated solely to analytical workloads, completely isolating heavy queries from the primary OLTP system.

Stop running heavy analytics queries on your production database. Isolate workloads with read replicas and proper indexing to prevent costly downtime and performance degradation when calculating metrics like annual revenue.

I Saw Your Revenue Query Take Down a Production Database. Let’s Talk.

I remember it like it was yesterday. It was 2 PM on a Tuesday, peak traffic time. A panic message pops up in Slack: “SITE IS DOWN – DB CPU AT 100%”. I jump into our monitoring dashboard and see one query, just one, consuming all the resources on prod-db-01. It was a simple-looking query from our new marketing analytics tool, trying to calculate the total revenue for the year. It was a SUM(price) on the entire orders table. A table with 200 million rows. That one innocent request ground our entire e-commerce platform to a halt for 15 minutes. Someone was just trying to answer a simple question from a Reddit thread—”What’s the annual revenue of your store?”—and they nearly caused a six-figure outage. This isn’t a theoretical problem; it’s a rite of passage for growing tech companies.

So, Why Does This Keep Happening?

The root cause is simple: workload contention. Your production database is optimized for one thing: fast, small, transactional operations (OLTP). Think “create an order,” “update a user’s address,” “check inventory.” These are quick reads and writes that need to happen in milliseconds. An analytics query, like “sum all sales for the year,” is the complete opposite. It’s a long-running, heavy-lifting analytical operation (OLAP) that has to scan millions, or even billions, of rows. When you run an OLAP query on an OLTP database, the database chokes. It’s like asking a Formula 1 car to haul a load of bricks. It wasn’t built for that, and something is going to break.

How to Fix It (Without Getting Fired)

You’ve got a few options, ranging from the 3 AM emergency fix to the long-term architectural solution. Let’s walk through them.

Solution 1: The “Kill Switch” Triage (The Quick Fix)

This is the emergency brake. Your database is on fire, and you need to put it out right now. You connect directly to the production database and kill the offending query. It’s messy, it’s dangerous, and it doesn’t solve the underlying problem, but it gets the site back up.

First, you need to find the process ID of the long-running query. In PostgreSQL, it looks something like this:

SELECT
  pid,
  now() - pg_stat_activity.query_start AS duration,
  query,
  state
FROM pg_stat_activity
WHERE state != 'idle' AND query NOT ILIKE '%pg_stat_activity%'
ORDER BY duration DESC;
Enter fullscreen mode Exit fullscreen mode

You’ll get a list of active queries. Find the one that’s been running for minutes and is clearly the SUM(price) beast. Let’s say its pid is 12345. Now, you kill it. Gently at first, then with force if needed.

-- The polite way
SELECT pg_cancel_backend(12345);

-- The not-so-polite way (if the above doesn't work)
SELECT pg_terminate_backend(12345);
Enter fullscreen mode Exit fullscreen mode

Warning: Be extremely careful with this. Terminating the wrong process in production can lead to data corruption. Double-check the query and PID before you hit enter. This is a temporary fix, not a strategy.

Solution 2: The Right Way – Indexing for Analytics (The Permanent Fix)

The reason your query is so slow is that the database has to do a “full table scan.” It literally reads every single row in the orders table to find the ones from this year. The fix is to give it a shortcut: an index.

If you’re constantly querying by date, you need an index on the date column. Let’s say your orders table has a created\_at timestamp column.

-- Create an index on the order creation date
CREATE INDEX idx_orders_created_at ON orders (created_at);
Enter fullscreen mode Exit fullscreen mode

Now, when a query like WHERE created\_at >= ‘2023-01-01’ comes in, the database can use this index to jump directly to the relevant rows instead of reading the entire table. This can turn a 15-minute query into a 15-millisecond one. It’s often the single most effective change you can make.

Solution 3: The “Grown-Up” Architecture – Read Replicas (The ‘Nuclear’ Option)

When your company gets serious about data, you have to separate your workloads completely. The gold standard is to create a read replica. This is an exact, near-real-time copy of your production database. The production database (prod-db-01) handles all the writes (new orders, user signups), and it replicates those changes over to the read replica (analytics-replica-01).

All your analytics tools, BI dashboards, and data scientists then point their heavy queries at the replica. If they run a query that takes an hour, who cares? It has zero impact on the performance of the main application. The production database is left alone to do its job: process transactions quickly.

Characteristic Production DB (Primary) Analytics DB (Read Replica)
Primary Use Case OLTP (Transactions) OLAP (Analytics)
Allowed Operations Reads & Writes Reads ONLY
Query Profile Short, fast, affects few rows Long, slow, affects many rows
Impact of Bad Query High. Can take down the entire application. Low. Might slow down a report, but the app stays up.

Final Thoughts

That Reddit thread is a great reminder that behind every business metric is a technical query. And if you’re not careful, the query to measure your success can become the very thing that causes you to fail. Stop treating your production database like a data warehouse. Give it the respect it deserves, isolate your workloads, and you’ll sleep a lot better at night. Trust me.


Darian Vance

👉 Read the original article on TechResolve.blog


Support my work

If this article helped you, you can buy me a coffee:

👉 https://buymeacoffee.com/darianvance

Top comments (0)