---
title: "When Your Slowest Page Tells You It's Missing Two Things"
canonical: https://dxdev.com/blog/2026-09-24_query-index-caching-compound-fix/
datePublished: 2026-09-24
---
The Marketing admin page took about 5 seconds to load, and the fix took 1h25m to trace and ship. Nearly all of that time went into the query, not the page.

The page is a staff-facing overview. The header block of counts and summaries renders before anything else, so the slow part was the first thing anyone saw. Nothing was erroring. It was just slow enough that people had stopped opening it.

## Two faults behind one 5-second header

I started at the top of the page and worked down to the query that fed it. It had two separate faults, and both contributed to the 5 seconds.

**A missing index.** The query filtered and joined on a column that had no index, so the database scanned the table for every lookup. The fix is one statement, shaped like this:

```sql
CREATE INDEX ix_<table>_<filter_column> ON <table> (<filter_column>);
```

**Too many round trips.** The page issued its lookups one at a time. Each was cheap on its own, but every one paid its own network and query overhead, and that cost multiplied. I cut the round trips by fetching what the header needed in fewer, wider queries.

Then I added caching on top. The header numbers do not need to be true to the second, and the same admins load the same page many times a day. A short-lived cache means most loads skip the database entirely.

The order matters, and I would repeat it. The index makes the cold path fast. Fewer round trips make the cold path cheap. The cache makes the warm path nearly free. Had I done only the cache, the page would have looked fixed on the second load while the first load of every cache window stayed at 5 seconds. Someone would have reported it again within a week.

## Confirming it on production

This went out as a same-day hotfix. I confirmed it live on production, not just on staging, by loading the page and watching the header render. That check is the one I trust. A query plan that looks good on a small staging dataset says little about a production table.

While in there I hit an unrelated bug and filed it as its own ticket instead of folding it into the hotfix. A hotfix that carries a second change is harder to roll back, and this one needed to stay a single small diff.

## What I can and cannot tell you

My session log records the outcome (index added, caching added, round trips cut, hotfix confirmed live) but no before-and-after timings beyond the roughly 5 seconds. I did not keep a per-query breakdown of what each of the three changes bought, so I cannot say the index was worth X seconds and the batching Y. I can say the combination fixed the page, and that the order above is the one I would choose again.

When a page is slow at the top, look at what the top depends on before anything else. Here it depended on a table scan and a chatty loop of lookups, and both were fixable in an afternoon.
