Patterns: Read-Through, Write-Behind & Session Cache
Apply Memcached to common application caching problems.
Code
# --- Read-through cache (cache-aside) ---
def get_user(user_id):
key = f"user:{user_id}"
val = mc.get(key)
if val is None: # cache miss
val = db.query("SELECT ...", user_id)
mc.set(key, json.dumps(val), expire=300) # 5 min
return json.loads(val)
# --- Write-through: update cache when DB changes ---
def update_user(user_id, data):
db.update("users", user_id, data)
mc.set(f"user:{user_id}", json.dumps(data), expire=300)
# OR invalidate: mc.delete(f"user:{user_id}")
# --- Session storage (with gat to extend on access) ---
def session_read(sid):
return mc.gat(sid, 1800) # read + extend ttl to 30min
def session_write(sid, data):
mc.set(sid, data, expire=1800)
# --- Lock with add (atomic) ---
def with_lock(key, ttl, fn):
token = uuid4().hex
if mc.add(f"lock:{key}", token, expire=ttl):
try:
return fn()
finally:
# release only if we still own the lock
cur, cas = mc.gets(f"lock:{key}")
if cur == token:
mc.cas(f"lock:{key}", b"", cas, expire=1)
else:
raise LockedError()
# --- Counter (rate limit) ---
def rate_limit(user_id, limit=100, window=60):
key = f"rl:{user_id}"
n = mc.incr(key, 1)
if n == 1:
mc.set(key, 1, expire=window) # set ttl on first hit
return n <= limitExplanation
Memcached shines as a cache-aside store: read checks the cache, on miss load from DB and refill. Use write-through (set on update) or invalidation (delete on update) to keep cache fresh. Sessions map naturally because they're ephemeral and keyed by ID — use gat to extend on each access. add is atomic and makes a great distributed lock primitive; release with cas to avoid clobbering a renewed lock held by someone else. incr is atomic for rate-limit counters (set ttl on the first hit). Never treat Memcached as a source of truth — it's a speed layer over your DB.
More Memcached Snippets
set / get — Basic Key-Value
Store and retrieve values by key with expiration and flags.
add / replace / append / prepend
Conditional writes and in-place string concatenation.
cas / gets — Compare-And-Swap
Optimistic locking: update a key only if it hasn't changed since you read it.
stats — Server Metrics & Slabs
Inspect memory, hit rate, evictions and per-slab allocation.
flush_all — Invalidate Everything
Logically invalidate all keys instantly (lazy deletion).
Expiration, Eviction & TTL Strategy
Choose TTLs wisely and understand how Memcached evicts under pressure.