Skip to content

Harden Starter Sites cache against orphaned/stale tiob_sites transients#493

Merged
selul merged 3 commits into
developmentfrom
copilot/fix-starter-sites-cache-issue
Jul 10, 2026
Merged

Harden Starter Sites cache against orphaned/stale tiob_sites transients#493
selul merged 3 commits into
developmentfrom
copilot/fix-starter-sites-cache-issue

Conversation

Copilot AI commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

The Starter Sites library caches the demosites API response in the tiob_sites transient with only WordPress's timeout-row semantics for freshness. When a DB/transient-cleanup tool (WP Rocket, WP-Optimize, etc.) strips _transient_timeout_tiob_sites but leaves _transient_tiob_sites, get_transient() treats the value as non-expiring and serves a stale, partial library indefinitely — unrecoverable by update, reinstall, or cache clears.

Implements the two low-risk defenses from the issue (items 1 & 2) in includes/Sites_Listing.php:

  • Self-validating freshness — wrap the cached payload with our own fetched_at stamp and validate it on read via a new get_cached_sites() helper, rather than trusting the transient timeout row. Any missing/malformed wrapper (including legacy raw payloads) or an age >= 12h forces a re-fetch.
  • Version-stamped keytiob_sitestiob_sites_<TIOB_VERSION>, so every plugin update reads from a fresh key and self-heals a stuck cache.
// write
set_transient( $this->transient_key, array(
    'fetched_at' => time(),
    'data'       => $response,
), $this->cache_ttl );

// read (get_cached_sites)
$cache = get_transient( $this->transient_key );
if ( ! is_array( $cache ) || ! isset( $cache['fetched_at'], $cache['data'] ) ) {
    return false; // no wrapper (or legacy payload) → re-fetch
}
if ( ( time() - (int) $cache['fetched_at'] ) >= $this->cache_ttl ) {
    return false; // orphaned-timeout / expired → re-fetch
}
return $cache['data'];

Adds tests/sites-listing-test.php covering: version-stamped key, fresh fetch stores the wrapped payload, fresh cache served without a remote call, stale fetched_at re-fetches (the orphaned-timeout case), and legacy raw payload re-fetches.

Items 3 (a UI "Refresh templates" button) and 4 (surfacing fetch failures via Logger/notice) are intentionally out of scope here as larger cross-boundary changes.

Copilot AI and others added 2 commits July 10, 2026 07:23
… timestamp

Co-authored-by: selul <3330746+selul@users.noreply.github.com>
Co-authored-by: selul <3330746+selul@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix Starter Sites library stale cache issue Harden Starter Sites cache against orphaned/stale tiob_sites transients Jul 10, 2026
Copilot finished work on behalf of selul July 10, 2026 07:27
Copilot AI requested a review from selul July 10, 2026 07:27
@selul selul marked this pull request as ready for review July 10, 2026 07:28
@selul selul changed the base branch from master to development July 10, 2026 07:29
@pirate-bot

Copy link
Copy Markdown
Collaborator

Plugin build for 4021d3b is ready 🛎️!

@selul selul merged commit bf53c0b into development Jul 10, 2026
8 of 9 checks passed
@selul selul deleted the copilot/fix-starter-sites-cache-issue branch July 10, 2026 07:50
@pirate-bot

Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 1.4.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@pirate-bot pirate-bot added the released Indicate that an issue has been resolved and released in a particular version of the product. label Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

released Indicate that an issue has been resolved and released in a particular version of the product.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Starter Sites library can get permanently stuck on a stale cached list (orphaned tiob_sites transient)

3 participants