ArticlesPERFORMANCE
PERFORMANCEScaling PostgreSQL Read Paths for Multi-tenant SaaS
Practical patterns for indexes, caching boundaries, and query discipline when tenant growth starts to show up in p95 latency.
8 min readSagar Tank
Why read paths fail first
In multi-tenant SaaS, product growth rarely breaks writes first. It breaks the hot read paths that every dashboard, list view, and permission check depends on.
When p95 latency climbs, teams often jump to sharding. In practice, most systems still have substantial headroom in indexing strategy, query shape, and cache boundaries.
Start with the real bottleneck
Instrument the query, not the anecdote.
- Capture p50 / p95 / p99 for the top 10 endpoints.
- Attribute latency to database time versus application time.
- Identify tenant skew before assuming uniform load.
If one enterprise tenant generates most read traffic, your indexing and caching strategy should reflect that skew.
Index for access patterns, not tables
Useful indexes follow product journeys:
- Tenant-scoped list queries with stable filters.
- Detail lookups by public identifier.
- Permission and membership checks on every request.
Composite indexes that match WHERE and ORDER BY clauses outperform a pile of single-column indexes that never get used together.
Cache at the correct boundary
Caching is not a substitute for query design.
- Cache immutable reference data aggressively.
- Cache tenant-scoped projections with explicit invalidation keys.
- Avoid caching authorization decisions without a clear revocation path.
Redis helps when the cache key model is boring and intentional.
Keep queries boring
The most expensive production incidents often come from clever ORM queries.
Prefer:
- Explicit select lists
- Predictable joins
- Pagination with keyset patterns for deep pages
- Timeout and statement budgets on analytical paths
A practical checklist
Before you propose sharding:
- Confirm the top N queries by total time.
- Fix missing or incorrect composite indexes.
- Remove N+1 patterns in list endpoints.
- Add cache only where invalidation is obvious.
- Re-measure p95 under representative tenant traffic.
Closing
PostgreSQL scales further than many teams expect when the read path is treated as a product surface. Measure first, index for real journeys, and keep the query layer boring enough to operate confidently.