Accidental DELETE Without a WHERE Clause in Production
An engineer running a cleanup script against the wrong environment deleted 2.3 million user preference records in 4 seconds.
Overview
A cleanup script meant for staging ran against production because of a typo in an environment variable, and hard-deleted 2.3 million rows in 4.1 seconds.
What happened
The task was routine: delete soft-deleted user records older than 90 days from the staging database. The engineer had run the same script dozens of times. This time the DSN variable had a typo, so it fell back to the default, which was the production connection string set in the shell profile.
Why it broke
Each safeguard the engineer relied on was missing or bypassed.
> [!WARNING] No WHERE clause scoped the delete by environment because the engineer trusted the DSN to keep environments apart.
A startup health check caught it: it queried user preferences, found zero rows and reported unhealthy.
Impact
- Rows deleted — 2.3 million
- Time to delete — 4.1 seconds
- RTO target — 4 hours
- Actual recovery time — 6 hours 12 minutes
The fix
Recovery combined a 2-hour-old RDS automated snapshot with 4 hours of binary log replay, getting as close to the deletion as possible.
- DSN falls back to a production default — Separate credentials for production DML
- Destructive script runs directly — Dry run or row-count check first
- Hard delete on user_preferences — Soft delete on critical tables
- Snapshots plus manual log replay — Point-in-time recovery enabled
Key concepts
- RTO (Recovery Time Objective)
- The maximum acceptable time to restore service after a failure. In this incident, 4 hours was the target; 6+ hours was actual.
- RPO (Recovery Point Objective)
- The maximum acceptable amount of data loss measured in time. An RPO of 2 hours means the recovery point can be up to 2 hours before the incident.
- Binary log replay
- Replaying MySQL/PostgreSQL WAL or binary logs from a snapshot forward to a specific timestamp, recovering changes made after the snapshot.
- Point-in-time recovery (PITR)
- RDS/Aurora feature that allows restoring a database to any second within the backup retention window using automated backups plus transaction logs.
- Soft delete
- Marking rows as deleted with a flag and timestamp rather than issuing a DELETE statement, preserving the data and enabling trivial recovery.
When to use it
- Discussing database safety controls in production environments
- Designing recovery plans for data loss scenarios
- Explaining RTO vs RPO tradeoffs in database backup strategy
Interview questions
- An engineer accidentally deleted 2 million rows from production. Walk me through the recovery.
- What is the difference between RPO and RTO, and how do backup strategies affect them?
Sign up free to read the full topic
More War Stories topics
- A Deployment That Could Not Be Rolled Back
- A Failed Migration That Left the Database in a Partial State
- A Feature Flag Change That Triggered a Production Outage
- An IAM Misconfiguration That Let a Developer Become Admin
- AWS Credentials Committed to a Public GitHub Repository
- Backups That Were Never Actually Completing
- BGP Route Leak That Rerouted Traffic Through an Unexpected AS
- Blue-Green Deployment That Resulted in Split-Brain Sessions
- Cache Stampede on a Flash-Sale Product Page
- Connection Pool Exhaustion That Brought Down 12 Microservices
- DynamoDB Hot Partition That Throttled a Flash Sale
- Failover to a Lagging Replica Lost 4 Minutes of Orders
Start free on Cariara · Coding problems · System design studio