Back to Portfolio
Case Study· Personal Project

Why Your Database Is Slow (It’s Not the Hardware)

PerformanceDatabaseGoPostgreSQL

8.8M rows

Dataset

666×

Best speedup

99.85%

Best latency reduction

The Problem

Most performance complaints sound like: “The database is slow, we need a bigger server.” Usually, that’s the wrong diagnosis. The real culprit is often the query plan — how PostgreSQL decides to fetch your data. To demonstrate this, I built two identical APIs against the same 8.8 million row IMDB dataset. Same server. Same data. Same queries. Only the query plan was different.

What I Did

1

Forced sequential scan on the unoptimized pool

Rather than writing a broken query, I disabled the index scanner at the PostgreSQL connection level using SET enable_indexscan = OFF, SET enable_bitmapscan = OFF, and SET enable_indexonlyscan = OFF. This replicates what happens in production when queries are written poorly — functions in WHERE clauses, type mismatches, or missing indexes on filtered columns.

2

Used proper indexes on the optimized pool

The optimized connection lets PostgreSQL use the indexes that are already in place: on title_principals(nconst), title_principals(category_id), and title_genres(genre_id). With indexes available, PostgreSQL jumps directly to relevant rows instead of scanning all 8.8 million of them.

3

Separated the two pools at the connection level

Each pool uses AfterConnect to apply its configuration on every new connection. This ensures the unoptimized pool consistently forces seq scans — making the benchmark repeatable and fair.

Benchmark Results

EndpointUnoptimizedOptimizedReductionSpeedup
/movies2,876 ms26.6 ms99.08%108×
/movies?title=batman3,711 ms409 ms88.97%
/movies/:id422 ms0.6 ms99.85%666×

The Takeaway

Before adding RAM or upgrading your database instance, run EXPLAIN ANALYZEon your slow queries. If you see “Seq Scan” on a large table, that’s your bottleneck — not the server. The right index on the right column can turn a 3-second query into a sub-millisecond one.