By
Sept. 9, 2026
The Monday morning Slack message arrived at 9:47 AM: "Dashboard still timing out. Can someone check the replica?" Your engineering team spun up that read replica months ago specifically to isolate analytics workloads from your production transactional database. The architecture made sense on paper—route heavy analytical queries to a dedicated replica, protect the primary from expensive scans, keep your application fast. Yet here you are again, fielding complaints from the analytics team while your cloud bill climbs and query performance degrades. The uncomfortable truth is that your PostgreSQL replica isn't failing because you configured it wrong. It's failing because PostgreSQL was never designed to be an analytics engine, and no amount of replication can fix an architectural mismatch.
PostgreSQL has earned its reputation as one of the most reliable transactional databases in production today. It powers e-commerce platforms processing thousands of orders per second, social networks managing millions of user sessions, and SaaS backends handling continuous high-frequency writes without breaking a sweat. The database excels at what it was built for: online transaction processing, or OLTP. When your application needs to fetch a user's complete order—order ID, total, status, timestamps, line items—PostgreSQL's row-oriented storage delivers that entire record efficiently in a single read. This is the workload pattern PostgreSQL optimizes for, and it does it exceptionally well.
But the moment your analytics team runs a query that scans millions of rows to aggregate three columns across a 200-column events table, that same row-oriented storage becomes a liability. PostgreSQL must read every column in every row to access the few columns your query actually needs. Research into columnar versus row storage patterns reveals the scale of this problem: a sequential scan on a sensor readings table with row storage can read 18.6 GB from disk and execute in over 38 seconds, while the identical query against columnar storage reads just 80 MB and completes in under 200 milliseconds. The query planner handles both, but row storage fundamentally reads every column to access any column. For analytical queries scanning millions of rows and needing only a handful, this becomes the largest source of I/O overhead.
Your read replica inherits this limitation wholesale. It's the same database engine, the same storage format, the same query execution paths—just running on different hardware. When your analytics team queries the replica for customer behaviour trends, cohort analysis, or real-time dashboards, they're asking a transactional database to perform analytical work at scale. The replica doesn't magically transform PostgreSQL into a columnar analytics engine. It simply moves the performance problem to a different server.
Replication lag compounds the issue in ways that erode trust across your organisation. Streaming replication in PostgreSQL works by shipping write-ahead log records from the primary to the replica, where they're replayed to keep data in sync. Under normal read-heavy workloads, lag remains minimal—often measured in milliseconds. But when your replica is simultaneously processing expensive analytical scans that consume CPU and I/O, replay can fall behind. A LinkedIn discussion on replication lag highlighted how certain workloads simply can't tolerate significant lag: order management systems, inventory tracking, and any dashboard where executives expect current-hour data. When your CFO pulls up a revenue dashboard and sees numbers from 45 minutes ago because the replica is lagging, the technical explanation about WAL replay and I/O contention doesn't restore confidence in the data.
The cache eviction problem creates a vicious cycle that impacts both the replica and, indirectly, your application's perception of database stability. To process massive analytical scans, PostgreSQL cycles large portions of your dataset through RAM in the buffer cache. On a dedicated replica, this evicts the "hot" transactional data that would normally stay resident for fast repeated access. One analysis of OLTP versus OLAP tuning noted that a single large analytical scan can evict the working set that every OLTP query depends on from shared buffers. Even though your replica is isolated from the primary, your analytics team begins to notice that the same query runs fast the first time after a restart, then degrades as the cache fills with data from other teams' dashboards. You're not just fighting the row-store overhead—you're fighting the database's own memory management, which was tuned for transactional access patterns.
Vertical scaling offers diminishing returns and an eventual hard ceiling. The instinctive response when PostgreSQL analytics queries slow down is to upgrade the instance: more RAM to cache more rows, more CPU to parallelize scans, faster NVMe storage to reduce I/O latency. This works for a while. Your 16-core replica becomes 32 cores, your 128 GB RAM becomes 256 GB, and query times improve—temporarily. But vertical scaling hits physical and economic limits. Research into PostgreSQL scaling strategies confirms that at some point, the architectural constraints compound: a query scanning 10 columns from a 200-column table still reads 20 times more data in PostgreSQL than it would in a columnar engine, regardless of how much RAM you throw at it. You're paying exponentially more for incrementally smaller performance gains, and your cloud bill reflects the mismatch between workload and database architecture.
The real-time analytics gap widens as your business grows. When your company was processing 5,000 events per day, PostgreSQL handled analytical queries without complaint. But as you scale to 50,000 events per second—IoT sensors reporting telemetry, financial systems processing trades, APM platforms collecting metrics from thousands of hosts—the combination of continuous high-frequency ingestion and analytical query load exposes PostgreSQL's limits. One case study of a platform hitting 50,000 events per second found that queries took two to five seconds even with aggressive optimization, prompting a database migration. For dashboards that poll every minute on datasets under 10 million rows, PostgreSQL remains viable. But once you cross into hundreds of millions of rows with continuous ingestion and sub-second query expectations, the friction becomes undeniable.
Materialized views and pre-aggregation buy time but introduce new operational complexity. Many teams turn to materialized views as a middle-ground solution: pre-compute common aggregations, refresh them periodically, and query the materialized result instead of scanning raw event tables. This works well for known, stable queries—daily revenue summaries, weekly cohort reports, monthly retention metrics. But materialized views come with impractical limitations for real-time analytics. They're snapshots, not live views. A refresh can take minutes or hours on large datasets, during which the view shows stale data. If your analytics team needs to slice data by dimensions you didn't pre-aggregate, they're back to querying the raw tables and facing the same performance wall. You end up maintaining a growing library of materialized views, each with its own refresh schedule and staleness trade-offs, adding operational burden without solving the underlying architectural mismatch.
The Solution
So what do you do about it? The first step is acknowledging that this isn't a tuning problem—it's an architecture problem. PostgreSQL will continue to excel as your system of record for transactional workloads. The solution isn't to abandon it; it's to stop asking it to be something it's not. The pattern that's emerging across data-intensive companies is to keep PostgreSQL as the authoritative source for transactional data and route analytical workloads to a purpose-built columnar engine.
Columnar databases like ClickHouse are designed from the ground up for analytical query patterns. Instead of storing rows together, they store columns together, so a query that aggregates three columns across millions of rows reads only those three columns from disk. The performance difference isn't incremental—it's often orders of magnitude. The same query that scans 18 GB in PostgreSQL scans 80 MB in a columnar format. This isn't about better indexing or smarter caching; it's about storage layout matching query patterns. When your analytics team asks for real-time dashboards on high-velocity event streams, a columnar engine delivers sub-second response times at a fraction of the infrastructure cost.
The architectural shift is simpler than it sounds. You don't rip out PostgreSQL or rewrite your application. You set up replication or change data capture from your PostgreSQL primary to your columnar analytics database. Transactional writes continue to land in PostgreSQL, where they belong. Analytical queries get routed to the columnar engine, where they belong. Your application logic doesn't change. Your analytics team gets the performance they need without evicting cache or lagging replicas. Your infrastructure costs stabilize because you're no longer scaling a transactional database vertically to handle workloads it was never optimized for.
This separation also eliminates the replication lag problem for analytics. Instead of a PostgreSQL replica struggling to keep up while processing heavy scans, your columnar database ingests change streams and makes data available for queries within seconds. There's no WAL replay bottleneck, no shared buffer contention, no cache eviction. The analytics workload runs on infrastructure purpose-built for it, and your transactional workload remains unaffected.
The cost equation shifts in your favor. Columnar compression is significantly more effective than row-store compression because similar values in a column compress together. Event timestamps, user IDs, status codes—these compress to a fraction of their raw size. You store more data in less space, query it faster, and scale horizontally by adding nodes rather than vertically by doubling instance sizes. One analysis comparing PostgreSQL and ClickHouse noted that ClickHouse is a purpose-built columnar analytical database that does one thing exceptionally well, while PostgreSQL is a general-purpose transactional database that can handle analytics but wasn't optimized for it. The cost of running the wrong tool for the job—both in infrastructure spend and engineering time—becomes clear when you see the alternative.
Next Steps
Your next step is to measure the gap. Instrument your current PostgreSQL replica to capture query latency distribution, cache hit rates, replication lag during peak analytical load, and the percentage of queries scanning more than one million rows. Identify the queries that represent 80% of your analytical workload. If most of them are scanning large tables, aggregating across many rows, or timing out during business hours, you've confirmed the mismatch. Then model the cost: what are you spending today on your replica instances, and what would it cost to run those same queries on a columnar engine? Include the engineering time spent tuning indexes, refreshing materialized views, and fielding Slack messages about slow dashboards.
Conclusion
The companies seeing the fastest time-to-insight and the lowest analytics costs aren't the ones with the biggest PostgreSQL replicas. They're the ones that recognized the architectural boundary between OLTP and OLAP, and built their infrastructure accordingly. Your PostgreSQL replica isn't failing your analytics team because of a configuration mistake. It's failing because you're asking a transactional database to solve an analytical problem at scale. The fix isn't a bigger replica—it's the right tool for the job.
Let's collaborate to turn your business challenges into AI-powered success stories.
Get Started