spb/ultra-sharp-agent-skills Public
Ultra-Sharp Agent Skills — a research-first skill-authoring system + 72 production-ready skills for AI agents.
Python 100%
1<!--2Author: Simon-Pierre Boucher3Contact: contact@spboucher.ai4-->56# Patterns — Caching78## Contents9- Cache-aside with fallback10- TTL jitter11- Single-flight rebuild12- Versioned keys13- Purge-on-write14- Negative caching15- Metrics16- Gotchas1718## Cache-aside with fallback1920```python21import json, random2223CACHE_TIMEOUT_S = 0.05 # cache slower than 50 ms is worse than origin24TTL_S = 300 # staleness tolerance for this data class2526def get_product(pid: int):27 key = f"product:v2:{pid}"28 try:29 raw = redis.get(key) # timeout=CACHE_TIMEOUT_S on the client30 if raw is not None:31 return json.loads(raw)32 except RedisError:33 pass # cache down → serve from origin34 value = db.fetch_product(pid)35 try:36 redis.setex(key, jittered(TTL_S), json.dumps(value))37 except RedisError:38 pass # failing to cache is not an error39 return value40```4142## TTL jitter4344```python45def jittered(ttl: int) -> int:46 return int(ttl * random.uniform(0.9, 1.1)) # ±10% desynchronizes expiry47```4849## Single-flight rebuild5051```python52LOCK_TTL_S = 10 # > rebuild p99 so a crashed builder's lock self-clears5354def get_report(rid: str):55 key = f"report:v1:{rid}"56 raw = redis.get(key)57 if raw is not None:58 return json.loads(raw)59 if redis.set(f"lock:{key}", "1", nx=True, ex=LOCK_TTL_S):60 value = build_report(rid) # only this process rebuilds61 redis.setex(key, jittered(600), json.dumps(value))62 redis.delete(f"lock:{key}")63 return value64 time.sleep(0.1) # others: brief wait then retry once65 raw = redis.get(key)66 return json.loads(raw) if raw else build_report(rid) # last resort: origin67```6869## Versioned keys7071```python72# Invalidate a whole family by bumping the version constant in code —73# no scan-and-delete, old entries simply age out via TTL.74PRODUCT_CACHE_V = 375key = f"product:v{PRODUCT_CACHE_V}:{pid}:{locale}"76```7778## Purge-on-write7980```python81def update_product(pid: int, fields: dict):82 db.update_product(pid, fields)83 try:84 redis.delete(f"product:v2:{pid}") # delete, don't rewrite: the next85 except RedisError: # read repopulates from fresh origin86 log.warning("purge failed for %s — TTL is the backstop", pid)87```8889Delete (not set) after write: writing the new value here races concurrent90readers repopulating from a stale read replica.9192## Negative caching9394```python95NEG_TTL_S = 30 # short: absorbs miss storms without delaying creates for long9697if value is None:98 redis.setex(key, NEG_TTL_S, "__miss__")99# On create: redis.delete(key) so the sentinel dies immediately.100```101102## Metrics103104```python105metrics.incr(f"cache.{family}.{'hit' if raw is not None else 'miss'}")106```107108Alert when hit rate for a family drops below its target (~80%) — usually a109key-schema change or an invalidation bug, not traffic.110111## Gotchas112113- **Caching the serialized response of another cache-user** stacks TTLs;114 staleness = sum of layers, not max.115- **`KEYS pattern*` for invalidation** blocks Redis; that need is the signal116 to switch to versioned keys.117- **Thundering read-repair after purge-on-write**: hot keys need118 single-flight even with purge-on-write.119- **Objects that serialize differently across app versions** poison shared120 caches during deploys — version the key (rule 5) on format changes.121- **Read replicas + purge-on-write**: purge, then repopulate-on-read may122 read a stale replica and resurrect old data; short TTL bounds the damage.123- **In-process caches in autoscaled fleets** are N independent staleness124 bubbles; keep them ≤5 s or accept per-instance divergence.125