Architecture Review: Read-Heavy SaaS API
A standard SaaS API architecture optimized for read-dominant workloads. PostgreSQL serves as the primary data store, Redis provides a caching layer for hot data, connection pooling bounds database concurrency, and read replicas scale read throughput without scaling write capacity.
Evidence Confidence
Moderate
moderate
Executive Summary
Read-Heavy SaaS API: strong operational readiness (78% evidence confidence). 5 architectural strengths identified, 2 operational risks to manage. Primary concern: Connection Pool Exhaustion. Requires Intermediate operational maturity.
Readiness Rationale
Overall strong readiness across 8 dimensions. Strong: operational, migration, observability.
Key Concerns
- !Connection Pool Exhaustion
- !Replication Lag Cascade
Key Strengths
- +Redis caching absorbs repeated read requests at the edge, reducing database load and latency for high read-to-write ratio workloads by orders of magnitude
- +A connection pool bounds the total database connections an application can open, preventing connection storms during traffic…
- +Read-heavy APIs benefit directly from Redis as a caching tier that absorbs repeated identical reads and provides sub-millisecond…
- +Read-heavy APIs generate large numbers of short-lived database connections
8
Assessments
2
Tradeoffs
6
Sections
11
Recommendations
Readiness Assessments
8Architectural Tradeoffs
2Recommendations
11Monitor: Connection Pool Exhaustion
risk_monitoringAll database connections in the pool are in use; new requests queue and then time out, causing cascading latency and errors across all dependent services.
Affects 1 node. (Redis). 1 mitigation identified
Monitor: Replication Lag Cascade
risk_monitoringAsynchronous replicas fall behind the primary under write load and serve reads from an older version of the data. Reads keep succeeding, so nothing errors; what breaks is one of three specific consistency guarantees (read-after-write, monotonic reads, or consistent prefix), each with a distinct user-visible anomaly.
Affects 1 node. (Read Replica)
Implement: Monitor connection pressure signals
observabilitySeed 'Connection Pool Pressure Under Load' identifies 4 metrics relevant to connection_exhaustion. Execution preview confirms this risk manifests under modelled load.
Metrics to instrument: active_connections, connection_wait_time_ms, p95_latency_ms
Single PostgreSQL, no cache, no pooling → PostgreSQL + PgBouncer + Redis cache
migration_planningTrigger: Connection pool exhaustion or p99 read latency > 200ms under normal load. Migrate from 'Single PostgreSQL, no cache, no pooling' to 'PostgreSQL + PgBouncer + Redis cache'. This migration is low-risk and high-impact. Start with PgBouncer; add Redis cache 1–2 weeks later after baseline is stable.
PgBouncer misconfiguration causes connection storms during migration; Cache warming period leaves Redis cold; database sees full load initially
PostgreSQL + PgBouncer + Redis cache → PostgreSQL + PgBouncer + Redis + streaming read replica
migration_planningTrigger: Primary CPU > 70% during peak read hours. Migrate from 'PostgreSQL + PgBouncer + Redis cache' to 'PostgreSQL + PgBouncer + Redis + streaming read replica'. Replica routing logic is the most common implementation mistake. Require a max_lag_ms threshold in the routing layer before routing to any replica.
Replication lag during replica setup under heavy write load; Application must implement lag-aware read routing: naive round-robin routes to lagged replicas
Prepare runbook for: Burst Traffic Cold Cache Stampede
simulation_preparednessSimulation demonstrates critical degradation of redis, postgresql
Without a runbook, recovery from this failure mode will be ad-hoc
Prepare runbook for: Connection Pool Exhaustion with Horizontal User Scale
simulation_preparednessSimulation demonstrates critical degradation of postgresql
Without a runbook, recovery from this failure mode will be ad-hoc
Plan evolution: Single Cache Layer → Distributed Cache
evolution_planningEvolution from Single Redis Node / Sentinel Cluster → Distributed Redis Cluster (Consistent Hash Ring)
Migration complexity: medium. Rollback: complex.
Plan evolution: Direct DB Queries → CQRS Read Models
evolution_planningEvolution from Unified Read/Write Database → CQRS with Separate Read Projections
Migration complexity: high. Rollback: complex.
Monitor threshold: Tier 1: Connection Exhaustion
scaling_monitoringSignal: p99 database latency rising; connection wait queue growing; requests timing out with "too many connections" or pool queue full errors
Bottleneck: Database connection pool saturated or max_connections exceeded. Evolution: Add PgBouncer connection pooler in transaction mode
Monitor threshold: Tier 2: Read Throughput Ceiling
scaling_monitoringSignal: Database CPU > 80% sustained; read query p99 rising; cache miss rate stable but overall latency increasing
Bottleneck: Single PostgreSQL primary saturated with read traffic. Evolution: Add one or more streaming read replicas; implement lag-aware replica routing
Scaling Pressure Signals
8p99 database latency rising; connection wait queue growing; requests timing out with "too many connections" or pool queue full errors
Threshold
Tier 1: Connection Exhaustion
Likely Bottleneck
Database connection pool saturated or max_connections exceeded
Recommended Evolution
Add PgBouncer connection pooler in transaction mode
Database CPU > 80% sustained; read query p99 rising; cache miss rate stable but overall latency increasing
Threshold
Tier 2: Read Throughput Ceiling
Likely Bottleneck
Single PostgreSQL primary saturated with read traffic
Recommended Evolution
Add one or more streaming read replicas; implement lag-aware replica routing
Redis hit rate < 60%; database read pressure rising despite cache presence; TTL expiry storms visible in Redis monitoring
Threshold
Tier 3: Cache Miss Amplification
Likely Bottleneck
Cache TTLs too aggressive or cache too small for working set
Recommended Evolution
Expand Redis memory allocation; segment cache by object lifecycle; implement staggered TTL jitter to prevent expiry storms
Write p99 > 50ms; replication lag > 1s consistently; WAL disk I/O saturated
Threshold
Tier 4: Write Bottleneck Emerges
Likely Bottleneck
Single PostgreSQL primary write throughput approaching ceiling
Recommended Evolution
Evaluate write sharding, CQRS separation, or domain decomposition. This represents a fundamental architectural transition.
p99 database latency rising; connection wait queue growing; requests timing out with "too many connections" or pool queue full errors
Threshold
Escalation trigger: Database connection pool saturated or max_connections exceeded
Likely Bottleneck
Tier 1: Connection Exhaustion
Recommended Evolution
Monitor: active_connections, connection_wait_time_ms, p95_latency_ms
Database CPU > 80% sustained; read query p99 rising; cache miss rate stable but overall latency increasing
Threshold
Escalation trigger: Single PostgreSQL primary saturated with read traffic
Likely Bottleneck
Tier 2: Read Throughput Ceiling
Recommended Evolution
Monitor: active_connections, connection_wait_time_ms, p95_latency_ms
Redis hit rate < 60%; database read pressure rising despite cache presence; TTL expiry storms visible in Redis monitoring
Threshold
Escalation trigger: Cache TTLs too aggressive or cache too small for working set
Likely Bottleneck
Tier 3: Cache Miss Amplification
Recommended Evolution
Monitor: active_connections, connection_wait_time_ms, p95_latency_ms
Write p99 > 50ms; replication lag > 1s consistently; WAL disk I/O saturated
Threshold
Escalation trigger: Single PostgreSQL primary write throughput approaching ceiling
Likely Bottleneck
Tier 4: Write Bottleneck Emerges
Recommended Evolution
Monitor: active_connections, connection_wait_time_ms, p95_latency_ms
Migration Readiness
12Migration Stages
2Single PostgreSQL, no cache, no pooling → PostgreSQL + PgBouncer + Redis cache
infoMigration trigger: Connection pool exhaustion or p99 read latency > 200ms under normal load
PostgreSQL + PgBouncer + Redis cache → PostgreSQL + PgBouncer + Redis + streaming read replica
infoMigration trigger: Primary CPU > 70% during peak read hours
Risks
10PgBouncer misconfiguration causes connection storms during m
warningPgBouncer misconfiguration causes connection storms during migration
Cache warming period leaves Redis cold; database sees full l
warningCache warming period leaves Redis cold; database sees full load initially
Replication lag during replica setup under heavy write load
warningApplication must implement lag-aware read routing: naive rou
warningApplication must implement lag-aware read routing: naive round-robin routes to lagged replicas
Projection lag creates a read-after-write window where users
criticalProjection lag creates a read-after-write window where users see stale data after their own writes. Mitigation: Route immediate post-write reads to the write store (session-scoped write token); accept eventual consistency only for non-user-initiated reads
↗ direct-db-to-cqrs
Projection rebuild after schema change can take hours or day
criticalProjection rebuild after schema change can take hours or days on large datasets. Mitigation: Design blue/green projection deployment: build new projection in parallel before switching traffic; test rebuild time in staging
↗ direct-db-to-cqrs
Missing partition for current time window causes all INSERTs
criticalMissing partition for current time window causes all INSERTs to fail with 'no partition of relation found'. Mitigation: Create partitions 7-30 days in advance; alert when next partition does not exist before its time window opens
↗ postgresql-to-partitioned
Historical data migration batch failures can leave partial d
criticalHistorical data migration batch failures can leave partial data in partitioned table. Mitigation: Validate row counts and checksums per partition before dropping old table; keep old table for 30+ days after cutover
↗ postgresql-to-partitioned
Read-after-write violations are invisible to monitoring but
criticalRead-after-write violations are invisible to monitoring but visible to users: 'my change disappeared'. Mitigation: Track write LSN per user session; route reads to primary until replica confirms that LSN; accept primary load increase
↗ single-region-to-multi-region
Replica promotion during primary region failure requires man
criticalReplica promotion during primary region failure requires manual intervention and causes data loss if replication lag is high. Mitigation: Document and test failover runbook quarterly; set maximum acceptable replication lag before automatic failover is blocked
↗ single-region-to-multi-region
Review Sections
6Referenced Intelligence