// Case study
PostgreSQL slow-query tuning, API p95 1.2s to 180ms
Missing indexes, N+1 ORM patterns, and bloated JSONB columns, backend performance pass on a billing service.
A subscription billing API on Django ORM and PostgreSQL 14 degraded to p95 1.2s on invoice list endpoints as customer count crossed 8,000. RDS CPU pegged 78% during month-end billing runs.
1.2s → 180ms
List endpoint p95
Invoice list API
78% → 41%
RDS CPU peak (month-end)
During billing runs
8,000+
Customer count
Scale trigger for degradation
p95 > 250ms
Regression alert threshold
Datadog APM monitoring
Delivered by Keshav Sharma · Published August 5, 2025 · 9 min read
Client context
A subscription billing API, Django ORM on PostgreSQL 14, degraded to p95 1.2s on invoice list endpoints as customer count crossed 8,000. RDS CPU pegged 78% during month-end billing runs.
The challenge
Profiling: pg_stat_statements showed sequential scans on invoice_line_items; ORM prefetch missing on nested serializers; JSONB metadata column indexed with generic GIN but queried wrong operator class.
Problems: migration lock on 40M-row table, used CONCURRENTLY index builds in maintenance window; one index dropped wrong plan on rare filter, iterative EXPLAIN ANALYZE.
Delivery notes
Fixes: composite indexes on (customer_id, created_at DESC); partial index on open invoices; serializer refactor with select_related/prefetch_related; materialized view for AR aging refreshed every 15 minutes; connection pool sizing via PgBouncer.
Technical implementation
Verification: pgbench-derived workload test; Datadog APM before/after; regression alert if p95 > 250ms.
Results & impact
Outcome: list endpoint p95 1.2s → 180ms; month-end RDS CPU peak 78% → 41%. Billing SaaS, name withheld.
// Related services
CIN
AAU-8582
Startup India
DIPP83124
Founded
November 2020
Office
Residency Rd, Bengaluru, India
Ready to scope your next initiative?
Share your goals with our Bangalore team. We respond within one business day with a clear path from discovery to delivery.
