Performance & scale · Enterprise · 2024
Meridian Freight
An operations dashboard that took 40 seconds to load was costing thirty dispatchers an hour a day each. The fix was four queries, not a rewrite.
- Rails
- PostgreSQL
- Redis
- Datadog
- 40s → 1.2s
- median dashboard load time
- ~1 hr/day
- returned to each of thirty dispatchers
- 3 weeks
- total engagement, run inside their existing sprint process
Where they started
Meridian's internal dispatch dashboard had grown with the company from 400 shipments a month to 40,000. It still worked, but it had gotten slow enough that dispatchers kept a second browser tab open to avoid waiting for reloads. Their engineering team knew it was slow. What they didn't have was a spare person to find out why.
What was breaking
-
An n+1 query hiding behind a helpful-looking helper method
Each of 500 rows triggered three additional queries to render a status badge. Fifteen hundred queries per page load, from one innocuous line in a view.
-
A missing index on the column everything filtered by
Sequential scans across 1.4 million rows on every single request.
-
Counting in Ruby what the database could count
Six summary tiles each loaded an entire collection into memory in order to call length on it.
-
No instrumentation, so none of this was visible
Their monitoring covered uptime but not query performance. The slowdown was invisible to engineering until a human complained loudly enough.
What we changed
-
1
Instrumented first, guessed never
We added query-level tracing before changing a single line, so every subsequent fix could be measured instead of assumed. This is the step people skip, and it's why performance work so often produces no measurable result.
-
2
Fixed the n+1 with eager loading
Fifteen hundred queries became four. This one change alone took the page from 40 seconds to about six.
-
3
Added the missing index, verified with EXPLAIN
Built concurrently so there was no downtime on a 1.4 million row table in active use.
-
4
Moved the aggregates into the database
The six summary tiles became one grouped query. Six seconds became 1.2.
-
5
Left the tracing in place, with alerts
So the next performance regression pages their engineering team instead of being discovered by their dispatchers a year later.
The expensive assumption
Meridian had budgeted for a platform rebuild. Multiple quarters, a dedicated team, the whole exercise. The dashboard was slow, the codebase was old, and the conclusion looked obvious.
It was slow for four specific, findable reasons. None of them had anything to do with the age of the codebase or the framework it was written in.
This is the most common expensive mistake we see at enterprise scale: “slow” gets treated as a property of the system rather than a symptom with a cause, and the proposed fix is sized to the frustration instead of to the problem. Two days of measurement would have told them what three weeks of work eventually did.
We’d rather tell you your problem is small. It’s a better use of your money and it’s how we end up working with people again.
“We had assumed this was a rebuild. It was three weeks and four changes. I would rather have heard that a year earlier.”
Got something that looks like this?
Thirty minutes, no pitch. We'll tell you what it would actually take.
Book a free 30-min callRather not book a time? Send a message instead.