---
title: "Five Warning Signs Your Database Needs Different Architecture"
published: 2026-05-07T07:40:41.000-04:00
updated: 2026-05-07T07:40:41.000-04:00
excerpt: "There's a category of Postgres performance issue that no amount of tuning will fix. Here's how to tell if you're already in it."
tags: PostgreSQL
authors: Matty Stratton
---

> **TimescaleDB is now Tiger Data.**

Every database has bad days. A slow query after a schema change. A spike in replication lag during a traffic surge. An autovacuum job that runs long enough to make you nervous.

Those are tuning problems. They have tuning solutions. A senior engineer can fix them in a day, sometimes in an hour, and the fix holds. You move on.

But there's a different category of symptom. The kind that comes back. The kind where each solution introduces the need for the next solution, where performance degrades not because of a specific bad query but because the workload has grown into territory the database wasn't designed for.

Here's the thing that makes this category different: growth changes the _nature_ of the problem, not just the severity. When your workload is fundamentally append-heavy and analytically queried, you're running it on a storage engine built for point lookups and updates. That mismatch doesn't show up at small scale. It compounds. And eventually no amount of configuration adjusts for it.

I've watched teams burn two quarters optimizing their way around this before realizing the optimization itself was the trap. The five signs below are how you recognize it before that happens.

If you're seeing three or more, the problem isn't your configuration.

## Sign 1: Your optimization work is cyclical, not cumulative

Tuning that sticks is cumulative. You add an index, queries get faster, they stay faster. You adjust `work_mem`, hash joins improve, the improvement is permanent. Done.

The warning sign: you're doing the same work again six months later. You partition the table, and after a while you need to re-partition because the data distribution shifted. You optimize a query, and three months later the same query is slow again because the table grew 40%. You upgrade the instance, and the headroom is gone in a quarter.

When optimization is cyclical, the underlying cause is growth, not misconfiguration. Growth doesn't respond to configuration. It responds to architecture.

**How to measure:** look at your sprint backlog. How many database-related tasks do you have this quarter versus last quarter versus the one before that? If the number is flat or growing, your optimization is running in place.

## Sign 2: Autovacuum is running constantly on tables with low update rates

Autovacuum on a heavily updated table makes sense. Dead tuples need cleaning, that's the job.

The warning sign: autovacuum running persistently on tables where fewer than 5% of rows are ever updated. This usually means one of two things: transaction ID wraparound prevention driving vacuum on high-insert tables, or hint bit maintenance generating constant background I/O.

Both are [MVCC overhead](https://www.tigerdata.com/blog/mvcc-feature-youre-paying-for-but-not-using) on append-only data. The visibility tracking machinery runs at full cost on rows that will never be modified. Tuning autovacuum parameters (`scale_factor`, `cost_delay`, `max_workers`) adjusts how the work is distributed, not whether it has to happen. The work still has to happen.

**How to measure:** check `pg_stat_user_tables` for your largest tables. Compare `n_dead_tup to autovacuum_count`. If autovacuum runs frequently but dead tuple counts are consistently low, the vacuum is maintenance overhead, not cleanup. You're paying the full MVCC tax on data that never gets modified.

## Sign 3: Query performance degrades linearly with data volume

Postgres query performance should be sublinear with data volume when indexes are working correctly. A B-tree index lookup is O(log n). Adding 10x more data should add a constant factor to indexed lookups, not 10x more time.

The warning sign: queries that get proportionally slower as the table grows. This happens when the dominant pattern is sequential scans or index scans over wide ranges rather than point lookups. Aggregations over time ranges, `GROUP BY` on large result sets, analytical scans touching millions of rows. These scale with data volume, not with the index.

Partitioning helps by limiting scans to relevant partitions. It's also a manual process that requires ongoing management, and partition-level sequential scans still scale linearly within each partition.

**How to measure:** run your five slowest queries against progressively larger time ranges. If execution time scales proportionally with the range, the queries are scan-bound. More data will always mean slower queries, and no index is going to change that trajectory.

## Sign 4: Index strategy has become a tradeoff negotiation

In a well-matched architecture, indexes are straightforward. Add them where you query, the read performance benefit outweighs the write cost, everyone's happy.

The warning sign: adding indexes to improve read performance measurably degrades write throughput, and the team is now making deliberate tradeoffs. "We can't add that index, it would slow inserts below our SLA." That conversation is the sign.

This is specific to high-throughput insert workloads. A SaaS app doing 1K inserts/sec can add ten indexes without noticing. A telemetry pipeline doing 100K inserts/sec feels every additional index in [write amplification](https://www.tigerdata.com/blog/write-amplification-in-postgres-the-3-4x-tax-on-every-insert). The math is just different.

When index strategy becomes a negotiation between read and write performance, the workload has outgrown what B-tree indexing on row-oriented storage can serve without tradeoffs. You can't index your way out.

**How to measure:** benchmark write throughput with your current index set, then with one additional index. If the drop is more than 10-15%, you're in the tradeoff zone. Whoops.

## Sign 5: Storage costs are growing faster than data value

Postgres stores data at the row level, uncompressed (unless you're using TOAST for large values). For time-series and event data, raw storage cost is proportional to row count with no automatic lifecycle management built in.

The warning sign: storage costs growing linearly with data volume, and the team having conversations about retention not because old data is worthless, but because storing it is expensive. You archive to cold storage or drop old partitions to manage costs, even though you'd rather keep the data online.

When storage cost forces data lifecycle decisions, the storage model isn't efficient for the data type. Columnar compression (10-20x for typical time-series data) and automatic data tiering change the economics so that keeping years of history queryable is practical rather than a monthly budget conversation.

**How to measure:** calculate your cost per million rows stored. Compare that number to the analytical value of the data. If you're deleting or archiving data you'd rather be querying, storage cost is constraining your product. That's the sign.

## The diagnostic

Count how many of these apply to your system.

**0-1:** Tuning is the right move. Your workload fits the architecture, specific optimizations will address the symptoms. The usual playbook works.

**2-3:** You're approaching the boundary. Current optimizations will buy months, not years. Start evaluating architectural options now, while you still have time to choose rather than react.

**4-5:** The workload has outgrown the architecture. Further optimization has diminishing returns. Migration to a system designed for this workload will be less expensive than continued optimization within 6-12 months. Not a fun conclusion, but better to reach it now than after another year of sprint-backlog churn.

## What this actually means

Tuning problems have solutions that stick. Architectural mismatches have solutions that buy time. The goal of this whole exercise is to figure out which one you're dealing with before you've spent another quarter on the latter.

If the diagnostic puts you in the 4-5 range, the answer isn't to abandon Postgres. The answer is to stop running a workload it wasn't designed for on vanilla storage primitives. What you actually need is automatic time-based partitioning, hybrid row/columnar storage for analytical scans, and lifecycle management that doesn't require you to babysit it. You need Postgres extended for this workload, not replaced.

That's what TimescaleDB does. Same database, same SQL, same toolchain your team already knows. Different storage engine underneath.

_If you want to go deeper on why vanilla Postgres hits this wall and what the underlying mechanics look like,_ [_Understanding Postgres Performance Limits for Analytics on Live Data_](https://www.tigerdata.com/blog/postgres-optimization-treadmill) _covers the architecture side in detail. And if some of these signs felt familiar but you're not sure you're fully in the architectural category yet,_ [_Six Signs That Postgres Tuning Won't Fix Your Performance Problems_](https://www.tigerdata.com/blog/six-signs-postgres-tuning-wont-fix-performance-problems) _has a different angle on the same diagnostic._