Search... ⌘K
Write Log In Sign Up

Join DevSolved

Share war stories & investigate outages.

Sign Up for Free Log In to Account
ESC
CRITICAL Resolved

Memcached Eviction Avalanche: The Serialization Trap

Avatar 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

critical Mandate cache key version bumps for any ActiveRecord model schema changes @Backend Team
high Migrate from `Marshal` serialization to raw JSON to prevent class-binding issues @Architecture
|

Discussions 0

Most recent