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). Prefer wp_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.