[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6WXKVB8PD9RZ6FAQ01HV8JK":3,"skill-same:01M6WXKVB8PD9RZ6FAQ01HV8JK":73},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":52,"commitSha":53,"skillDirectory":54,"forksCount":14,"repositoryHtmlUrl":55,"repositoryHomepageUrl":56,"ownerHtmlUrl":57,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":58,"install":59,"resources":72},"01M6WXKVB8PD9RZ6FAQ01HV8JK","caching-strategies",null,"Caching patterns — cache-aside, write-through, TTL strategies, invalidation, HTTP caching. Use when adding caching to reduce latency or DB load, choosing between caching patterns, configuring HTTP cache headers, debugging stale data or stampedes, or designing an invalidation strategy.","projectious-work\u002Fprocesskit","projectious-work","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F263889726?v=4","01M6N97TJJ118WQ35CHRV8RD0E","projectious.work","01M6N97TPWJPF0BMKYQ4PBY5W8",0,"MIT","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Farchive\u002Fprocesskit-install-20261101\u002Fcontext\u002Ftemplates\u002Fprocesskit\u002Fv0.11.1\u002Fcontext\u002Fskills\u002Fengineering\u002Fcaching-strategies\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"caching","strategies","caching strategies","when","http","caching patterns","reduce","v1","load","ttl","headers","db","latency","stale","use","strategy","write","work","cache","debugging","2026-11-10 10:12:30",false,"---\nname: caching-strategies\ndescription: |\n  Caching patterns — cache-aside, write-through, TTL strategies, invalidation, HTTP caching. Use when adding caching to reduce latency or DB load, choosing between caching patterns, configuring HTTP cache headers, debugging stale data or stampedes, or designing an invalidation strategy.\nmetadata:\n  processkit:\n    apiVersion: processkit.projectious.work\u002Fv1\n    id: SKILL-caching-strategies\n    version: \"1.1.0\"\n    created: 2026-04-06T00:00:00Z\n    category: engineering\n---\n\n# Caching Strategies\n\n## Intro\n\nPick a caching pattern from read\u002Fwrite ratio and consistency\nrequirements, bound staleness with TTLs, and accept that invalidation\nis hard. Always set a TTL as a safety net even when you invalidate\nexplicitly. Protect popular keys from stampedes.\n\n## Overview\n\n### Choose the right pattern\n\n**Cache-Aside (Lazy Loading)** — most common, application manages the\ncache. On read, check cache; on miss, load from DB and populate. On\nwrite, update DB and invalidate the cache (do not write through).\nBest for read-heavy workloads tolerating brief staleness.\n\n**Read-Through** — the cache library loads from the data source on\nmiss. Reduces application complexity by moving load logic into the\ncache.\n\n**Write-Through** — writes go through the cache to the data source\nsynchronously. Higher write latency but the cache is always fresh.\nBest when downstream reads must never see a stale value.\n\n**Write-Behind (Write-Back)** — the cache writes to the data source\nasynchronously and returns immediately. Best for write-heavy\nworkloads, but you risk data loss if the cache fails before flush.\n\n### TTL strategy\n\nTTL is the primary tool for bounding staleness:\n\n- **Short (1-60 s)** — rapidly changing data: stock prices, sessions\n- **Medium (5-60 min)** — user profiles, product listings, API\n  responses\n- **Long (hours-days)** — static assets, configuration, reference\n  data\n- **No TTL (manual)** — data that changes only through known events\n\nApply jitter (e.g. ±10%) to TTLs so a wave of entries does not expire\ntogether and stampede the database.\n\n### Invalidation\n\nThe two hard problems in computer science: cache invalidation and\nnaming things. Three approaches:\n\n1. **Event-driven** (preferred) — on write\u002Fupdate\u002Fdelete, explicitly\n   invalidate or update the cache entry. Use pub\u002Fsub or CDC to\n   propagate across services.\n2. **Tag-based** — tag entries by entity (`user:123`, `product:456`)\n   and invalidate by tag.\n3. **Versioned keys** — `user:{id}:v{version}`; bump version on\n   change so old entries expire naturally via TTL.\n\nRules: prefer invalidation over update (avoids races); always set a\nTTL as safety net; accept that distributed cache invalidation is\neventually consistent.\n\n### HTTP caching\n\n```\n# Immutable static assets (hashed filenames)\nCache-Control: public, max-age=31536000, immutable\n\n# API responses that vary by user\nCache-Control: private, max-age=0, must-revalidate\nETag: \"a1b2c3\"\n\n# Shared content (CDN-cacheable)\nCache-Control: public, s-maxage=3600, max-age=60, stale-while-revalidate=300\n```\n\nUse ETag or Last-Modified for conditional revalidation — clients send\n`If-None-Match` and the server returns `304 Not Modified` when\nunchanged. Use `s-maxage` to control CDN TTL independently of the\nbrowser TTL. Use `Surrogate-Key` for tag-based purging on\nFastly\u002FVarnish.\n\n## Gotchas\n\nAgent-specific failure modes — provider-neutral pause-and-self-check items:\n\n- **Per-instance in-memory cache in a horizontal fleet.** Each replica holds a different cache state, so the same key returns different values depending on which instance handles the request. Either use a shared external cache or accept that per-instance caches are only valid for data that is safe to be inconsistent across replicas (e.g., non-critical local rate limiting).\n- **Updating cached values instead of invalidating them.** A write path that updates the cache entry directly while the database write is in-flight creates a window where the cache holds a value that the database doesn't yet confirm. Invalidate on write; let the next read repopulate from the authoritative source.\n- **No TTL with manual invalidation as the only expiry mechanism.** When invalidation logic is missed in any write path, stale data lives forever. Always set a TTL as a safety net — even a long one (hours or days) — so the worst case is bounded. Manual invalidation is a supplement, not a replacement.\n- **Caching error responses.** Caching a 404 or 500 from an upstream service means a transient failure becomes permanent until the entry expires or is manually cleared. Never cache error responses; only cache successful results from authoritative sources.\n- **No jitter on TTLs for a large key population.** If every key expires at the same second (e.g., all set with TTL 3600 at the same time), the resulting cache miss storm hits the origin simultaneously. Add per-key random jitter (±10–20%) to TTLs to spread expiry events.\n- **Key collisions across environments or tenants.** Using bare entity IDs as cache keys (`user:123`) without a namespace prefix causes staging data to collide with production data, or one tenant's data to collide with another's. Always prefix keys with environment and tenant identifiers.\n- **Not warming the cache before routing traffic.** A cold cache after a deployment or restart causes a thundering herd — all requests miss simultaneously and hammer the origin. Pre-warm critical keys before switching traffic, or use a staggered rollout that lets the cache fill naturally.\n\n## Full reference\n\n### Stampede prevention\n\nWhen a popular key expires, hundreds of requests can hit the database\nat once. Three mitigations:\n\n**Locking (mutex)**:\n\n```python\nvalue = cache.get(key)\nif value is None:\n    if cache.acquire_lock(f\"lock:{key}\", timeout=5):\n        value = db.query(key)\n        cache.set(key, value, ttl=300)\n        cache.release_lock(f\"lock:{key}\")\n    else:\n        value = cache.get(key)  # retry — another process is loading\n```\n\n**Probabilistic early expiration (XFetch)** — recompute before TTL\nexpires, with rising probability as expiry approaches:\n\n```python\nif time_remaining \u003C ttl * 0.1 * random.random():\n    recompute_and_cache(key)\n```\n\n**Stale-while-revalidate** — serve the stale value immediately while\nrefreshing in the background. Built into HTTP via the\n`stale-while-revalidate` directive.\n\n### Cache warming\n\nPre-populate caches before traffic hits. On deploy, warm the top-N\nmost accessed keys. On cache flush, run a warming script that replays\nrecent access patterns. For predictable access patterns, pre-compute\nduring off-peak hours. Do not warm everything — warm only keys with\nhigh access frequency and expensive computation.\n\n### Anti-patterns\n\n- **Per-instance in-memory caches** in a horizontally scaled fleet —\n  hit rates collapse and one instance can hold a stale value while\n  another invalidates. Use a shared cache (Redis, Memcached) with\n  consistent hashing.\n- **Updating the cache from writers** — racy. Invalidate instead.\n- **No TTL with manual invalidation** — one missed invalidation\n  becomes a permanent bug.\n- **Caching errors** — a downstream 500 cached for 10 minutes turns a\n  blip into an outage. Cache only successful responses, or use a much\n  shorter negative-cache TTL.\n- **Cache key collisions** — always namespace keys by service and\n  version.\n\n### Operational checklist\n\nTrack hit rate per cache, hit rate per key family, eviction rate, and\nP99 cache latency. Alert when hit rate drops sharply — that usually\nmeans an upstream change broke key generation. Cap entry sizes; a\nsingle huge value can blow out your cache memory budget.\n","# Caching Strategies\n\n## Intro\n\nPick a caching pattern from read\u002Fwrite ratio and consistency\nrequirements, bound staleness with TTLs, and accept that invalidation\nis hard. Always set a TTL as a safety net even when you invalidate\nexplicitly. Protect popular keys from stampedes.\n\n## Overview\n\n### Choose the right pattern\n\n**Cache-Aside (Lazy Loading)** — most common, application manages the\ncache. On read, check cache; on miss, load from DB and populate. On\nwrite, update DB and invalidate the cache (do not write through).\nBest for read-heavy workloads tolerating brief staleness.\n\n**Read-Through** — the cache library loads from the data source on\nmiss. Reduces application complexity by moving load logic into the\ncache.\n\n**Write-Through** — writes go through the cache to the data source\nsynchronously. Higher write latency but the cache is always fresh.\nBest when downstream reads must never see a stale value.\n\n**Write-Behind (Write-Back)** — the cache writes to the data source\nasynchronously and returns immediately. Best for write-heavy\nworkloads, but you risk data loss if the cache fails before flush.\n\n### TTL strategy\n\nTTL is the primary tool for bounding staleness:\n\n- **Short (1-60 s)** — rapidly changing data: stock prices, sessions\n- **Medium (5-60 min)** — user profiles, product listings, API\n  responses\n- **Long (hours-days)** — static assets, configuration, reference\n  data\n- **No TTL (manual)** — data that changes only through known events\n\nApply jitter (e.g. ±10%) to TTLs so a wave of entries does not expire\ntogether and stampede the database.\n\n### Invalidation\n\nThe two hard problems in computer science: cache invalidation and\nnaming things. Three approaches:\n\n1. **Event-driven** (preferred) — on write\u002Fupdate\u002Fdelete, explicitly\n   invalidate or update the cache entry. Use pub\u002Fsub or CDC to\n   propagate across services.\n2. **Tag-based** — tag entries by entity (`user:123`, `product:456`)\n   and invalidate by tag.\n3. **Versioned keys** — `user:{id}:v{version}`; bump version on\n   change so old entries expire naturally via TTL.\n\nRules: prefer invalidation over update (avoids races); always set a\nTTL as safety net; accept that distributed cache invalidation is\neventually consistent.\n\n### HTTP caching\n\n```\n# Immutable static assets (hashed filenames)\nCache-Control: public, max-age=31536000, immutable\n\n# API responses that vary by user\nCache-Control: private, max-age=0, must-revalidate\nETag: \"a1b2c3\"\n\n# Shared content (CDN-cacheable)\nCache-Control: public, s-maxage=3600, max-age=60, stale-while-revalidate=300\n```\n\nUse ETag or Last-Modified for conditional revalidation — clients send\n`If-None-Match` and the server returns `304 Not Modified` when\nunchanged. Use `s-maxage` to control CDN TTL independently of the\nbrowser TTL. Use `Surrogate-Key` for tag-based purging on\nFastly\u002FVarnish.\n\n## Gotchas\n\nAgent-specific failure modes — provider-neutral pause-and-self-check items:\n\n- **Per-instance in-memory cache in a horizontal fleet.** Each replica holds a different cache state, so the same key returns different values depending on which instance handles the request. Either use a shared external cache or accept that per-instance caches are only valid for data that is safe to be inconsistent across replicas (e.g., non-critical local rate limiting).\n- **Updating cached values instead of invalidating them.** A write path that updates the cache entry directly while the database write is in-flight creates a window where the cache holds a value that the database doesn't yet confirm. Invalidate on write; let the next read repopulate from the authoritative source.\n- **No TTL with manual invalidation as the only expiry mechanism.** When invalidation logic is missed in any write path, stale data lives forever. Always set a TTL as a safety net — even a long one (hours or days) — so the worst case is bounded. Manual invalidation is a supplement, not a replacement.\n- **Caching error responses.** Caching a 404 or 500 from an upstream service means a transient failure becomes permanent until the entry expires or is manually cleared. Never cache error responses; only cache successful results from authoritative sources.\n- **No jitter on TTLs for a large key population.** If every key expires at the same second (e.g., all set with TTL 3600 at the same time), the resulting cache miss storm hits the origin simultaneously. Add per-key random jitter (±10–20%) to TTLs to spread expiry events.\n- **Key collisions across environments or tenants.** Using bare entity IDs as cache keys (`user:123`) without a namespace prefix causes staging data to collide with production data, or one tenant's data to collide with another's. Always prefix keys with environment and tenant identifiers.\n- **Not warming the cache before routing traffic.** A cold cache after a deployment or restart causes a thundering herd — all requests miss simultaneously and hammer the origin. Pre-warm critical keys before switching traffic, or use a staggered rollout that lets the cache fill naturally.\n\n## Full reference\n\n### Stampede prevention\n\nWhen a popular key expires, hundreds of requests can hit the database\nat once. Three mitigations:\n\n**Locking (mutex)**:\n\n```python\nvalue = cache.get(key)\nif value is None:\n    if cache.acquire_lock(f\"lock:{key}\", timeout=5):\n        value = db.query(key)\n        cache.set(key, value, ttl=300)\n        cache.release_lock(f\"lock:{key}\")\n    else:\n        value = cache.get(key)  # retry — another process is loading\n```\n\n**Probabilistic early expiration (XFetch)** — recompute before TTL\nexpires, with rising probability as expiry approaches:\n\n```python\nif time_remaining \u003C ttl * 0.1 * random.random():\n    recompute_and_cache(key)\n```\n\n**Stale-while-revalidate** — serve the stale value immediately while\nrefreshing in the background. Built into HTTP via the\n`stale-while-revalidate` directive.\n\n### Cache warming\n\nPre-populate caches before traffic hits. On deploy, warm the top-N\nmost accessed keys. On cache flush, run a warming script that replays\nrecent access patterns. For predictable access patterns, pre-compute\nduring off-peak hours. Do not warm everything — warm only keys with\nhigh access frequency and expensive computation.\n\n### Anti-patterns\n\n- **Per-instance in-memory caches** in a horizontally scaled fleet —\n  hit rates collapse and one instance can hold a stale value while\n  another invalidates. Use a shared cache (Redis, Memcached) with\n  consistent hashing.\n- **Updating the cache from writers** — racy. Invalidate instead.\n- **No TTL with manual invalidation** — one missed invalidation\n  becomes a permanent bug.\n- **Caching errors** — a downstream 500 cached for 10 minutes turns a\n  blip into an outage. Cache only successful responses, or use a much\n  shorter negative-cache TTL.\n- **Cache key collisions** — always namespace keys by service and\n  version.\n\n### Operational checklist\n\nTrack hit rate per cache, hit rate per key family, eviction rate, and\nP99 cache latency. Alert when hit rate drops sharply — that usually\nmeans an upstream change broke key generation. Cap entry sizes; a\nsingle huge value can blow out your cache memory budget.\n",{"name":5,"metadata":44,"description":51},{"processkit":45},{"id":46,"created":47,"version":48,"category":49,"apiVersion":50},"SKILL-caching-strategies",1775433600000,"1.1.0","engineering","processkit.projectious.work\u002Fv1","Caching patterns — cache-aside, write-through, TTL strategies, invalidation, HTTP caching. Use when adding caching to reduce latency or DB load, choosing between caching patterns, configuring HTTP cache headers, debugging stale data or stampedes, or designing an invalidation strategy.\n","main","4876a92620ee792aac2ac9a1a806322d8a572367","archive\u002Fprocesskit-install-20261101\u002Fcontext\u002Ftemplates\u002Fprocesskit\u002Fv0.11.1\u002Fcontext\u002Fskills\u002Fengineering\u002Fcaching-strategies","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit","https:\u002F\u002Fprojectious-work.github.io\u002Fprocesskit\u002F","https:\u002F\u002Fgithub.com\u002Fprojectious-work","2026-11-09 00:53:29",{"source":8,"skillName":5,"commands":60,"prompt":70,"zipAvailable":71},[61,64,67],{"tool":62,"command":63},"npx","npx skills add projectious-work\u002Fprocesskit --skill caching-strategies",{"tool":65,"command":66},"bunx","bunx skills add projectious-work\u002Fprocesskit --skill caching-strategies",{"tool":68,"command":69},"pnpm","pnpm dlx skills add projectious-work\u002Fprocesskit --skill caching-strategies","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Farchive\u002Fprocesskit-install-20261101\u002Fcontext\u002Ftemplates\u002Fprocesskit\u002Fv0.11.1\u002Fcontext\u002Fskills\u002Fengineering\u002Fcaching-strategies\u002FSKILL.md",true,[],[74,100,120,141,158,176],{"skillNo":75,"name":76,"nameZh":6,"description":77,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":78,"categories":79,"keywords":85,"publishedAt":39},"01M6X5MTC89B4ZGXW3JVSQCPF8","skill-management","Manage Skill package manifests as v1 ontology entities.","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Fsrc\u002Fcontext\u002Fskills\u002Fprocesskit\u002Fskill-management\u002FSKILL.md",[80],{"categoryNo":81,"categoryName":82,"taxonomyType":83,"taxonomyTypeDesc":84},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[86,87,88,89,90,91,36,92,93,94,76,95,96,97,26,98,99],"management","skill management","create","update","package","alpha","list","manage","ontology","get","as","manifests","skill package","transition",{"skillNo":101,"name":102,"nameZh":6,"description":103,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":104,"categories":105,"keywords":107,"publishedAt":39},"01M6X5MCV74NHW5MEDNHZRF2C8","schema-management","Manage generated processkit schema contracts through deterministic MCP rebuild and inspection workflows.","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Fsrc\u002Fcontext\u002Fskills\u002Fprocesskit\u002Fschema-management\u002FSKILL.md",[106],{"categoryNo":81,"categoryName":82,"taxonomyType":83,"taxonomyTypeDesc":84},[86,108,109,110,111,112,91,36,113,114,93,115,116,117,95,118,119],"schema","schema management","MCP","workflows","validation","generated","rebuild","contracts","wed","deterministic","contract","mode",{"skillNo":121,"name":122,"nameZh":6,"description":123,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":124,"categories":125,"keywords":126,"publishedAt":39},"01M6X5M5G6YJ96AMZGSCZJ1D13","proposition-management","Record claims, risks, beliefs, world facts, WSJF estimates, and assumptions through the Proposition interface.","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Fsrc\u002Fcontext\u002Fskills\u002Fprocesskit\u002Fproposition-management\u002FSKILL.md",[],[86,127,128,88,89,129,91,36,130,131,132,133,95,134,135,136,137,138,139,140],"proposition","proposition management","index","log","world","record","query","creation","event","interface","updates","facts","claims","WSJF",{"skillNo":142,"name":143,"nameZh":6,"description":144,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":145,"categories":146,"keywords":147,"publishedAt":39},"01M6X5KMQ83QN9M6MPWZM7344F","ontology-management","Manage generated v1 ontology entities that do not require a dedicated domain server.","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Fsrc\u002Fcontext\u002Fskills\u002Fprocesskit\u002Fontology-management\u002FSKILL.md",[],[86,94,148,88,91,149,150,113,151,130,93,152,153,95,154,155,135,156,26,157],"ontology management","lifecycle","archive","roadmap","do","domain","iteration","keep","measurement","generic",{"skillNo":159,"name":160,"nameZh":6,"description":161,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":162,"categories":163,"keywords":164,"publishedAt":39},"01M6X5KCY2DN3SM21RERSFNEBT","okf-compatibility","Import, export, and validate conformant Open Knowledge Format bundles.","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Fsrc\u002Fcontext\u002Fskills\u002Fprocesskit\u002Fokf-compatibility\u002FSKILL.md",[],[165,166,167,168,91,169,170,171,36,172,173,174,175],"okf","compatibility","okf compatibility","validate","open","knowledge","format","export","bundles","bundle","import",{"skillNo":177,"name":178,"nameZh":6,"description":179,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":180,"categories":181,"keywords":182,"publishedAt":39},"01M6X5JA8S5PNGJVFW4EQY8T83","capability-management","Manage versioned Capability entities and their governed lifecycle.","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Fsrc\u002Fcontext\u002Fskills\u002Fprocesskit\u002Fcapability-management\u002FSKILL.md",[],[86,183,184,88,89,129,91,149,36,92,130,185,93,132,95,155,135,136,186,99],"capability","capability management","queries","changes"]