Hard Problems

Eight production incidents. Root causes ranged from query architecture to database constraints to process gaps to leadership judgment. Organized by severity and learning.

01. Query Shape, Not Tuning

When: 2022
Duration: 3 hours to diagnosis + fix
Impact: Cross-team, blocking

Problem

Paginated queries timing out. Team had exhausted index tuning. Every optimization lever was maxed. Root issue: join happened before pagination. Materializing millions of rows, then limiting to 100, is cardinality inversion.

Solution

Reverse operation order: LIMIT from primary table first, then join only for those 100 rows. Changed cardinality from millions-in-join to 100-in-join.

Result

Response time: 3+ minutes → under 500ms (360x faster). Key insight: separates query tuning from query architecture.

02. MySQL Lock Contention at Scale

When: Early 2022, production cutover
Duration: 1-1.5 days from escalation to fix
Impact: Production-wide regression, all 200+ merchants

Problem

Post-cutover response time spiked from seconds to 5+. Root cause: MySQL LOAD DATA holds locks for the entire operation. With 6 parallel threads per client Ɨ 100+ clients, hundreds of concurrent lock holders. Everything downstream waited for locks.

Solution

Reduce parallelism from 6 → 3 concurrent load threads. Released locks sooner. Allowed downstream queries to proceed.

Result

Response time: returned to baseline. CEO co-owned the recovery communication. Key insight: production scale ≠ staging scale.

03. Real-Time Product Changes in Recommendations

When: 2022-2023
Duration: 3-4 days to build + ship
Impact: Churn driver (merchants seeing stale recommendations)

Problem

Reco (old system) precalculated every 24 hours. If product went out of stock at hour 7, it appeared in recommendations for 17 more hours. Merchants saw broken recommendations, lost trust. CSMs complained (not in sprint planning). It was a churn driver.

Solution

Webhook → MySQL landing → Debezium → Pub/Sub. Real-time messages when product updates. Consuming services listen, update blocks in real-time. Max 5-minute lag instead of 24 hours.

Result

Complaints and churn disappeared. CSMs now escalate directly to you (organizational credibility shift). Key insight: listen to customers, not just tickets.

04. MySQL Views → Debezium CDC

When: 2022-2023
Duration: Phase 1: 2 months (tactical); Phase 2: 1 month (architectural)
Impact: Foundation of data platform architecture

Problem

MySQL views for normalized serving. Theory: one view, consuming services query it. Reality: views materialize the entire dataset before applying filters. COUNT(*) on one client read millions of rows. Queries: 30+ seconds.

Two-Phase Approach

Phase 1 (Urgent):

Convert views to CTEs. Apply filters before joins. Bought 2 months. 30s → 1.5-2s.

Phase 2 (Proper Fix):

Debezium tails MySQL → Pub/Sub. Services normalize into purpose-shaped stores: MongoDB (point lookups), ClickHouse (aggregation). Query denormalized stores directly.

Result

Phase 2 achieved same latency as Phase 1 with different approach. Proved design was correct. Became the platform architecture. Key insight: normalize in consumers, not database.

05. Missing Index on Production Query

When: One week after deployment
Duration: 3-4 hours to diagnose + fix
Impact: Intermittent performance degradation

Problem

Consuming service response time spiked. Certain queries slow, others normal. Root: query filtered on MongoDB field without index. Only intermittent because only certain queries hit the unindexed field.

Solution

Immediate:

Added the index.

Permanent:

Added query-performance monitoring. If response drops below threshold, alert immediately. Catches regressions in minutes, not days.

Result

Monitoring now prevents similar regressions. Key insight: process gaps matter as much as technical fixes.

06. Real-Time Optimization Gone Wrong

When: December 24, 2025 (Christmas Eve)
Duration: 12 hours troubleshooting
Impact: Production-wide, peak sales, org-wide panic

Problem

Team member optimized recommendation sorting: listen to real-time product changes, update ClickHouse on every change. Didn't understand ClickHouse's memory constraints. With millions of products and real-time traffic, database ran out of memory. Silent failure: reports looked correct but were wrong.

Solution

Immediate:

Scale ClickHouse RAM to buy breathing room.

Permanent:

Implement 24-hour batch solution. Had her own the fix (how junior engineers learn). Added AI-assisted code review to flag problematic patterns.

Result

Christmas sales unblocked. Process improved. Team member learned. Key insight: crisis management + mentorship under pressure.

07. Repeated Pattern: Segment Ordering

When: First incident 2023; second incident 2023-2024
Duration: 4 days (first); 4 hours (second)
Impact: Wrong audience got emails

First Incident

No explicit ORDER BY in segment API. BigQuery returns results in non-guaranteed order. Emails sent to wrong audience. Fixed with explicit ordering.

Second Incident (BQ → ClickHouse Migration)

Copied query from BigQuery to ClickHouse. Didn't realize ClickHouse's ORDER BY semantics differ (NULL handling, tie-breaking). Same problem: ordering changed. Same customer churned twice.

Systemic Fix

Three layers: (1) Producer maintains API contract and ordering guarantee. (2) Consumers validate incoming data matches expected ordering before sending emails. (3) Continuous QA validation across all services.

Result

Ordering validated continuously. Key insight: recognize when problems repeat, build systems to prevent it.

08. Silent Failure + Algorithmic Insight

When: BQ → ClickHouse migration (2023-2024)
Duration: 2.5 weeks (diagnosis + batched hashing solution)
Impact: Correctness issue (silent failure) + cost issue

Problem

Segment comparison: identify new/removed/unchanged members. On BigQuery: full outer join (expensive but works). Migrated to ClickHouse. Hit memory limits: comparing 100M members requires O(n²) memory. Scaled up RAM. System showed success. But results were silently wrong.

Root Cause

Trying to move BigQuery's unlimited-memory join model into ClickHouse's tight-memory budget. Incompatibility, not a tuning problem.

Solution: Batched Hashing with cityHash64

Instead of joining all members: (1) Use cityHash64 to partition both old and new segments deterministically. (2) Same email always falls into same bucket in both datasets. (3) Compare bucket-by-bucket via hashes. (4) Any hash mismatch detects changes in that bucket. Avoids O(n²) memory explosion entirely.

Result

Memory: unlimited → 16GB fixed. Cost: ₹3+ lakhs → ₹50K (6x reduction). Correctness: now produces correct results. Key insight: recognize incompatibility, solve algorithmically instead of throwing resources.

Eight Incidents as a Progression

01–03
Senior
Query shape, lock contention, customer insight
Foundation of technical judgment
04–05
Staff
Architecture under deadline, process improvement
Own systems, build process
06–08
Principal
Leadership under crisis, systemic patterns, algorithmic optimization
Mentor, recognize incompatibility, solve elegantly