Object Caching
Object caching stores the results of expensive operations—database queries, computed fragments, API responses—in a fast key/value store (e.g., Redis or Memcached). Unlike page caching (full HTML), object caching accelerates server-side work that builds pages and APIs, cutting query volume, CPU time, and tail latency under load.
How it works
- Key/value entries:
key → value (+ TTL)
. Values are serialized; Redis/Memcached keep them in RAM. - Hit → return immediately; miss → compute → set.
- TTL & eviction: Items expire after TTL or are evicted under memory pressure (LRU/LFU).
- Replication/HA: Redis Sentinel/Cluster or managed services (ElastiCache, Azure Redis) provide failover and scaling.
- Namespaces/prefixes: Avoid key collisions (multisite, staging vs prod).
What to cache (examples)
- Repeated WP_Query results (e.g., “featured products”, mega-menu trees).
- Computed fragments (price tables, availability matrices, navigation structures).
- Remote API responses (shipping rates, geolocation) with short TTLs.
- Options that are expensive to assemble (large config arrays).
What not to cache
- User-specific data without user-scoped keys (risk: data leakage).
- Non-deterministic results (e.g.,
ORDER BY RAND()
), unless you accept brief staleness. - Huge payloads (MB-scale) that thrash memory.
- Secrets (store securely; cache only derived, non-sensitive data).
WordPress specifics
- WP exposes the Object Cache API:
wp_cache_get()
,wp_cache_set()
,wp_cache_delete()
, groups, and increments. - A persistent object cache (Redis/Memcached) replaces the default in-memory-per-request cache via the
object-cache.php
drop-in. - Transients API uses the object cache when present; otherwise falls back to the DB (can bloat
wp_options
). Preferwp_cache_*
for hot paths. - Check if one is active:
wp_using_ext_object_cache()
. - Use groups (e.g.,
'menu'
,'product_meta'
) to organize and target invalidation.
WooCommerce specifics
- Woo already caches product objects and variation price lookups; align your keys with product/term IDs.
- Invalidate on updates:
save_post_product
,woocommerce_update_product
, stock/status changes, term edits. - Don’t cache cart/checkout computations across users; if caching fragments, include user/session in the key and keep TTLs tiny.
Sample pattern (safe, stampede-resistant)
function lc_get_featured_products_ids($limit = 8) {
$key = "featured_ids:$limit";
$group = 'catalog';
$ids = wp_cache_get($key, $group);
if ($ids !== false) {
return $ids;
}
// Simple lock to prevent thundering herd
if (false === wp_cache_add("$key:lock", 1, $group, 15)) {
// Another request is building; brief wait or return stale if you keep a shadow key
usleep(100000); // 100ms
$ids = wp_cache_get($key, $group);
if ($ids !== false) return $ids;
}
$q = new WP_Query([
'post_type' => 'product',
'posts_per_page' => $limit,
'meta_key' => '_featured',
'meta_value' => 'yes',
'no_found_rows' => true,
'fields' => 'ids',
]);
$ids = $q->posts;
wp_cache_set($key, $ids, $group, 300); // TTL 5 min
wp_cache_delete("$key:lock", $group);
return $ids;
}
Invalidation strategies
- ID-scoped keys: e.g.,
product_meta:{$product_id}
; purge on that product’s update hook. - Tag your own keys via naming conventions to bulk-delete by pattern (Redis
SCAN
+DEL
in admin task/CLI). - Use short TTLs for volatile data; rely less on manual purges.
- Keep deployment hooks that clear keys tied to templates/config changes.
Operational best practices
- Prefix keys per environment/site:
SITE_PREFIX
(e.g., domain or blog_id). - Serialization: Prefer igbinary for Redis if available (smaller/faster).
- Compression: Enable LZ4/Zstd at the client if supported and values are large.
- Connection hygiene: Persistent connections, sane timeouts, and retry/backoff.
- Monitoring: Track hit rate, memory used, evictions, p95/p99 latency, Redis slowlog.
- Capacity: Size RAM so eviction rate is negligible during peak traffic.
Common pitfalls
- Key collisions across environments (missing prefix).
- Caching personalized output without user scoping → data leaks.
- No invalidation path → stale menus/prices after edits.
- TTL set to 0 (“forever”) on volatile data.
- Relying on DB-backed transients at scale → options table bloat and slow autoload.
KPIs
- Cache hit rate per group (target >80% on hot paths).
- DB queries/request and CPU time drop post-adoption.
- Evictions/min (should be near zero).
- p95 render time for cached endpoints.