Devsolved
Sep 18, 2023 —
12:00 UTC
MTTR:
11 hours
2 min read
155000 views
Executive Summary
A seemingly safe deployment changed the underlying class structure of cached objects in our Rails app. The new code treated all 50GB of previously cached Memcached data as invalid, simulating a catastrophic 0% cache hit rate at peak load.
Performance Impact: Database CPU hit 100% instantly upon deployment. 503 Service Unavailable errors spiked to 30% for 45 minutes.
The Symptom
Symptom
We deployed a minor feature release at 12:00 PM. Instantly, our database connection pool maxed out and the application ground to a halt. We checked Memcached, and it was perfectly healthy, holding 50GB of data. However, the application's cache hit rate had plummeted from 98% to 0%.
Root Cause Analysis
Root
Cause
In Ruby on Rails, `Rails.cache.fetch` serializes complex objects (like ActiveRecord models) using `Marshal.dump` before storing them in Memcached. In this release, a developer added a new `virtual_attribute` to the User model. When the new code booted up, it queried the cache and received the old binary payload. However, because the underlying class structure had changed in memory, the `Marshal.load` deserialization failed silently (or the cache key versioning automatically rejected it). The application assumed the cache was empty and immediately fell back to the database. 50,000 concurrent users instantly executed heavy SQL queries, completely overwhelming the database.
5 Whys Root Cause Drill-Down
1
Why #1
2
Why #2
3
Why #3
4
Why #4
5
Why #5
Resolution
Resolution
We immediately rolled back the deployment to restore the old class structure and rescue the database. To fix it properly, we implemented "Cache Key Versioning" (e.g., changing the cache key prefix from `v1/users/123` to `v2/users/123`). We then deployed a background script to slowly warm up the `v2` cache keys over 24 hours. Once the `v2` cache was warm, we deployed the code change safely.
Code
ruby
# The fix: Explicit cache key versioning
def cache_key
"v2/users/#{id}-#{updated_at.to_i}"
end
Preventive Action Items
criticalMandate cache key version bumps for any ActiveRecord model schema changes@Backend Team
highMigrate from `Marshal` serialization to raw JSON to prevent class-binding issues@Architecture
|
Discussions
0
Most recent
Embed Incident
Copy the HTML below to embed this postmortem on your company blog, Notion, or internal docs.
Discussions 0