The 30 second answer
I cache aggressively on the edges that repeat: popular query strings, common filter combinations, and rendered facet payloads. Coordinator-side result caches help aggregations. I keep user-specific personalized scores lightly cached or computed late so one shopper does not poison another. Price and stock badges need short TTLs or live re-checks. Cache is a performance layer, not the source of truth, so every entry needs TTL and a bust path on ingest.
Cacheable.
- Normalized query → structured plan (after spell-fix / NER) for popular head queries.
- Filter combination → candidate counts / facet payloads (Zalando's DoS post notes query builder and Catalog API caching of popular queries and filter combinations, plus coordinator-node caches for results and aggregations).
- Product card hydration by id (short TTL).
- Customer embedding / feature-store lookups reused across layers within a request (described in Zalando's ranking paper as near-real-time features with caching).
Risky or short-TTL.
- Exact "buyable" stock counts.
- Personalized re-rank scores (privacy + correctness).
- Anything used for checkout authorization.
Patterns. Cache-aside with TTL is the default. Prefer caching filter clauses and deterministic query shapes because Elasticsearch can cache filters effectively when queries are structured that way (Elasticsearch in Action discusses filter/query caches in the 1.x era; modern docs rename internals — cite current Elastic caching docs for production). For deep pagination, prefer search_after / point-in-time over huge from offsets.
Opinion: during incidents, increasing TTL alone rarely saves you if the new traffic is unique expensive aggregations — Zalando's Dec 2025 post lists longer cache TTL among first actions that did not help when a buggy internal client issued high-cardinality SKU aggregations.
Sources:
- Elasticsearch — Shard request cache
- AWS ElastiCache — Caching strategies
- Zalando Engineering — The Day Our Own Queries DoS'ed Us
- arXiv 2409.02856 — feature cache notes