SPB Git

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%
3.7 KB · 125 lines markdown
Rendered Raw Blame History
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