Skip to content

pressable/pressable-solutions-cache-warmer

Repository files navigation

🔥 Pressable Solutions Cache Warmer

v1.2.0 — "The Cold Case"

Welcome to the Cache Warmer—a single-file mu-plugin that keeps low-traffic WordPress sites warm, measures cache health while it does it, and tells you the truth about whether warming is even your problem.

Your edge cache forgets. Your page cache forgets. max-age=86400 is less of a promise and more of a mood. And the first visitor after every eviction—a real human, a Googlebot, a PageSpeed test, a prospect your sales team is trying to impress—pays the full cold-render tax while everyone else gets the 70ms version. This tool makes sure somebody already paid that tax before anyone important shows up.

Current Bugs

No known bugs at this time.


🎯 Purpose

  • 🔥 Keep the pages that get judged warm — front page, key landings, whatever PSI and prospects actually hit
  • 🔍 Measure cache health continuously — every fetch logs which layer served it and how fast
  • 🧠 Tell you the truth — sometimes the right answer is not this tool, and the report says so
  • 🦥 Work on zero-traffic sites — the exact sites every other warming strategy quietly fails on

🧊 The Problem

Two facts about managed WordPress caching that nobody puts in the brochure:

  1. max-age ≠ stays-warm. Edge and page caches are LRU-bounded. A low-traffic site's entries get evicted regardless of TTL—measured in minutes at the edge on idle sites. Cold render: 1–4s+. Warm hit: ~70ms. That's a 35× tax on whoever shows up first.

  2. You can't self-schedule your way out. On hosts without a real cron runner, WP-cron only fires when PHP runs—and PHP only runs on a cache MISS. So a WP-cron-scheduled warmer is a perpetual motion machine: it dies the moment it succeeds. Warm site → all HITs → no PHP → no cron → eviction → cold. Physics wins.

The fix for #2 is admitting the system needs an external heartbeat. That's the whole architecture.


⚙️ How It Works

  1. A tick endpoint. The mu-plugin answers /?pscw_tick=<secret>&t=<timestamp>—validated, never-cached, interval-gated (early ticks are an instant 204 no-op). Each tick runs ONE time-boxed warm/measure cycle at muplugins_loaded and exits before your theme even wakes up.

  2. Any dumb pinger drives it. GitHub Actions cron. An uptime monitor you already pay for. A curl loop. One request per site per ~5 minutes. The pinger needs zero intelligence—all the thinking lives in-site.

  3. Two-tier warming (in warm mode):

    • Hot set — ≤10 curated URLs, every ~4 minutes. The pages that get judged. Always warm.
    • Broad set — sitemap-driven, capped, hourly, cursor-resumed across cycles. The long tail.
  4. Purge-aware. Content changes → platform purges the page → the warmer's queue re-warms it about a minute later. Your editor saves; your cache doesn't sulk about it.

  5. Polite by construction. One concurrent fetch, ever. Configurable sleep between requests. A responsiveness pre-check that skips the whole cycle if the site is struggling. Time-boxed cycles. This tool must never become the load problem it exists to solve. (Looking at you, every crawler-based preloader ever built.)


🔬 The Measurement (the part that outlives the warming)

Every fetch is the measurement. The warming stream and the diagnostic stream are the same requests—cycle N verifies cycle N−1 for free. Each record logs the URL, HTTP status, timing, x-ac (edge state), Batcache visibility, Set-Cookie presence, and cacheability headers.

Three serving layers, classified per fetch:

Layer Signal Typical cost
edge x-ac HIT or STALE (stale-while-revalidate = instant for the visitor) ~20–100ms
bcache x-ac MISS but fast — served by the origin page cache ~40–150ms
render x-ac MISS and slow — full PHP 1–4s+ 💸

Then wp pscw report hands down a verdict:

Verdict Meaning Action
HEALTHY-WARM ≥80% of responses avoid a full render turn the warmer off, you don't need it
NEEDS-WARMING most responses pay full PHP enable warm mode
CACHE-POISONED anonymous responses carry Set-Cookie fix the cookies first — warming cannot help an uncacheable site
inconclusive not enough data keep measuring

The report also flags uncacheable URLs (no-cache/no-store/private responses—password-protected content, plugin nocache, 404 paths) and non-200s (wrong URLs, slug drift). These flags exist because we stepped on every one of these rakes personally. The sitemap-driven URL selection and per-fetch status logging are not features, they're scar tissue.

Install in measure mode (the default), let it watch for a few days, read the report, then decide. Data first, warming second. Sometimes the verdict is "your cache is fine" or "your real problem is a rogue cookie"—and that's the tool working, not failing.


📦 Installation

  1. Copy pressable-solutions-cache-warmer.php into wp-content/mu-plugins/.
  2. (Optional) Copy pscw-config.example.json to wp-content/mu-plugins/pscw-config.json and tune it. Config hot-reloads—no code edits, no restarts, no excuses.
  3. Get your tick URL: wp pscw secret
  4. Point a pinger at it (see pinger-examples/). Append a changing &t=<timestamp>.
  5. Wait a few days. Then: wp pscw report

You're ready to simmer. 🍲


🛠 WP-CLI

wp pscw status            # config + aggregates + cursor + log location
wp pscw secret [--rotate] # tick secret + full tick URL
wp pscw run               # force one cycle now (testing)
wp pscw report [--days=7] # per-URL layer stats + the verdict

🚨 Honest Limitations

  • We warm the origin page cache (global) + the POP the fetch traverses. Full multi-POP edge warmth is a platform-layer concern—the 1–4s PHP render is the cost that matters, and origin-cache warmth kills it everywhere.
  • If the report shows x-sc headers, a platform-level preload layer is active on your site—don't double-warm.
  • Single-site WordPress (v1). Multisite untested.
  • A warmer cannot fix an uncacheable site. The verdict will tell you. Believe it.

⚙️ Config Reference

{
  "mode": "measure",
  "hot_urls": ["/", "/about/", "/contact/"],
  "broad_source": "sitemap",
  "broad_cap": 100,
  "hot_interval_s": 240,
  "broad_interval_s": 3600,
  "sleep_ms": 2000,
  "fetch_timeout_s": 10,
  "cycle_budget_s": 90,
  "backoff_ms": 3000,
  "log_max_bytes": 1048576,
  "user_agent": "Pressable-Solutions-CacheWarmer/1.0"
}

Kill switch: define('PSCW_DISABLED', true); in wp-config.php. Or delete the file. We're a mu-plugin, not a lifestyle.


📜 License

GPL-2.0-or-later. Same as WordPress, because that's how this works.

🧪 Why "The Cold Case"?

Because v1.2 shipped after a day spent investigating cache entries that kept turning up dead—and the culprit was never who we suspected first. The eviction wasn't an eviction, the poisoning wasn't a poisoning, and the 404 was coming from inside the house. Every diagnostic flag in the report is a closed case.

About

No description, website, or topics provided.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages