Skip to content

Instantly share code, notes, and snippets.

@dd32
Last active May 8, 2026 16:26
Show Gist options
  • Select an option

  • Save dd32/bd15729b7de1e842cc8de9b2f875a63f to your computer and use it in GitHub Desktop.

Select an option

Save dd32/bd15729b7de1e842cc8de9b2f875a63f to your computer and use it in GitHub Desktop.
rtc_perf_report_anonymized.md

RTC storage-strategy performance: cross-environment comparison

Data source: post_type=rtc-perf-result on https://make.wordpress.org/hosting/, eight captures from 2026-04-29 through 2026-05-05. Test runner: WordPress/distributed-rtc-performance-testing. Strategies under test (all on WP 7.1-alpha):

# Approach Key PR
1 Post meta — RC2 baseline post-meta
2 Custom table for all data custom-table #11256
3 Post meta + transients for awareness post-meta-transients #11348
4 Custom table + object cache for awareness custom-table-with-transients #11599

Cohort

Captures are anonymised; provider relationships are noted where they're relevant for cross-cuts.

Label Hosting class Persistent cache PHP WP rev Notes
A Shared redis 8.3 62278
B Shared redis 8.3 62295 Same provider as A (later WP rev)
C Shared none 8.5 62295 Same provider as A, B (different PHP, no cache)
D Shared none 8.1 62290
E Shared none 8.2 62278 Same provider as D (earlier capture)
F ⚠️ Shared none 8.2 62290 Anomalous transient pathology
G Managed cloud memcached 8.4 62278
H Shared none 8.1 62295 Slow bootstrap, otherwise normal

A ninth, partial capture (PHP 8.0, no cache) ran only the post-meta strategy and is excluded from cross-strategy tables below.

"No persistent cache" = no object-cache.php drop-in; WordPress falls back to its built-in WP_Object_Cache class which is per-request, in-memory only.

How to read the numbers

The runner logs every test request via rest_pre_dispatch / rest_post_dispatch hooks (rtc-test.php:178–290):

  • dispatch ms (mean_disp_ms) — wall time spent inside the REST handler. This is the part the strategies actually affect.
  • total ms (mean_total_ms) — full request from REQUEST_TIME_FLOAT to end of dispatch. Includes WP bootstrap; mostly host-architecture, not strategy.
  • DB queries / DB time — measured during dispatch only.
  • n — number of polled requests aggregated. sustain runs for a fixed 30s window with N concurrent clients, so n=68–300+.

All numbers below are weighted by sample size across the 6 variants (poll_delay × update_size).


Headline: dispatch ms by strategy × environment (sustain)

Environment post-meta custom-table post-meta-trans ct-with-trans
B — Shared / redis / PHP 8.3 (62295) 2.17 1.14 1.08 0.87
A — Shared / redis / PHP 8.3 (62278) 2.07 2.05 1.39 0.95
D — Shared / no cache / PHP 8.1 2.28 0.90 2.17 0.98
E — Shared / no cache / PHP 8.2 2.79 1.28 2.74 1.33
C — Shared / no cache / PHP 8.5 2.63 1.50 1.93 1.14
G — Managed cloud / memcached 4.70 3.94 2.99 2.47
H — Shared / no cache / PHP 8.1 14.35 9.16 12.76 9.33
F — Shared / no cache / PHP 8.2 ⚠️ 5.54 6.22 26.07 12.10

Bold = winner per row. post-meta (the RC2 baseline) is last on every environment.

Improvement vs post-meta baseline

Environment custom-table post-meta-trans ct-with-trans
B — Shared / redis −47% −50% −60%
D — Shared / no cache (PHP 8.1) −60% −5% −57%
E — Shared / no cache (PHP 8.2) −54% −2% −52%
C — Shared / no cache (PHP 8.5) −43% −27% −57%
A — Shared / redis −1% −33% −54%
G — Managed cloud / memcached −16% −36% −47%
H — Shared / no cache (PHP 8.1) −36% −11% −35%
F — Shared / no cache ⚠️ +12% +371% +118%

custom-table-with-transients is first or tied-first on six of seven complete environments and never worse than post-meta. Two patterns to note:

  • No-cache shared environments (D, E, H): custom-table (no transients) is a hair faster than custom-table-with-transients — within 0.05–0.17 ms. The transient layer adds no value when there's no persistent cache to serve it from, so the win comes purely from the schema.
  • Environment F: see anomaly section.

DB queries during dispatch (sustain)

Environment post-meta custom-table post-meta-trans ct-with-trans
B — Shared / redis 6.00 2.16 1.00 1.00
A — Shared / redis 6.00 4.00 1.00 1.00
G — Managed cloud / memcached 6.00 4.00 1.00 1.00
C — Shared / no cache (PHP 8.5) 8.00 4.15 6.11 4.10
D — Shared / no cache (PHP 8.1) 9.00 5.15 8.07 5.03
E — Shared / no cache (PHP 8.2) 9.00 5.20 8.14 5.14
H — Shared / no cache (PHP 8.1) 9.00 5.25 8.20 5.16
F — Shared / no cache ⚠️ 9.00 5.37 8.21 5.22

Two very clean signals:

  1. Persistent cache → 1 query for both transient strategies. Without it, transients fall through to the options table and behave roughly like the post-meta path.
  2. Custom table → ~half the queries of post meta on every environment, regardless of cache.

custom-table-with-transients wins because it gets the schema reduction when caching is absent, and the cache reduction when it's present.


Useful cross-cuts

Cache effect, near-controlled (B vs C — same provider)

Environments B and C are from the same provider; PHP versions differ (8.3 vs 8.5), but the cache config is the only deliberate change:

Strategy B (redis) C (no cache) Penalty for losing cache
post-meta 2.17 ms 2.63 ms +21%
custom-table 1.14 ms 1.50 ms +31%
post-meta-transients 1.08 ms 1.93 ms +79%
custom-table-with-transients 0.87 ms 1.14 ms +31%

The strategies that lean hardest on the cache pay the most when it's gone. post-meta-transients nearly doubles in latency without persistent cache — useful if anyone considers it a fallback default.

WordPress core code change between WP rev 62278 and 62295

Environments A and B are the same provider, same hosting tier, same PHP, both on redis — only the WP rev changes. The latency change isn't environmental:

Strategy A (62278) B (62295) Δ DB queries Δ
post-meta 2.07 ms 2.17 ms +5% unchanged
custom-table 2.05 ms 1.14 ms −44% 4.00 → 2.16
post-meta-transients 1.39 ms 1.08 ms −23% unchanged (1.00)
custom-table-with-transients 0.95 ms 0.87 ms −8% unchanged (1.00)

custom-table got materially faster between revs — about half its DB queries went away. Worth identifying which commit and confirming it doesn't regress the cached path. (It looks neutral-to-positive there, but the signal is smaller than test noise.)

Bootstrap floor (baseline scenario total_ms / total_cpu_ms)

Environment total ms total CPU ms
F — Shared / no cache 44 17
D — Shared / no cache (PHP 8.1) 46 31
C — Shared / no cache (PHP 8.5) 50 37
B — Shared / redis 55 41
A — Shared / redis 56 42
H — Shared / no cache (PHP 8.1) 199 60
E — Shared / no cache (PHP 8.2) ⚠️ 212 53
G — Managed cloud / memcached 630 427

The managed cloud platform's per-request cost is consistent across two weeks (~630 ms total, ~430 ms total CPU) — it's an architectural property of that platform, not a one-off. Most RTC dispatch optimisations are <1% of that budget, so the request-handling overhead in that platform likely deserves its own investigation independent of RTC storage strategy.

Environment H's bootstrap is ~4× slower than other no-cache shared environments (199 ms vs 44–50 ms), with elevated CPU as well. That's why H's dispatch numbers are roughly ~5× larger than D, E, or C on the same custom-table strategy: the strategy is doing the same work, the host is just slower on every code path. The strategy ranking is preserved, just compressed (−36% best vs −60% on D).

Environment E sits ~5× above its sister capture D (same provider, different PHP / different day). That looks like environmental noise on E's capture day — treat that row's total_ms figures with caution. Dispatch-only numbers are unaffected.


⚠️ Anomaly: environment F

Strategy F dispatch Other no-cache environments
post-meta 5.54 ms 2.3–14.4 ms
custom-table 6.22 ms 0.9–9.2 ms
post-meta-transients 26.07 ms 1.9–12.8 ms
custom-table-with-transients 12.10 ms 1.0–9.3 ms

The non-transient paths are merely slow. The transient paths are pathological — even compared to environment H (the next-slowest no-cache host), F's post-meta-transients is 2× slower. Likely candidates worth investigating with the hosting provider:

  • Slow options table writes (autoload list churn from transient set/expire cycles?)
  • Missing or unused index on wp_options.option_name
  • Per-request schema introspection (SHOW COLUMNS) hitting a cold path

F's bootstrap is actually the leanest in the cohort (17 ms total CPU), so this isn't a generally-slow host — it's specifically the transient code path that misbehaves there. Recommend reaching out to that provider before drawing conclusions for similarly-tiered hosting in general.


Recommendation

Ship custom-table-with-transients (#11599) as the default RTC storage strategy.

  • First or tied-first on six of seven complete environments (excludes the F anomaly; ties closely with custom-table on no-cache shared environments D, E, H).
  • Best worst case among the candidates — never slower than the RC2 baseline, and on F it's safer than post-meta-transients.
  • Degrades gracefully without persistent cache: within 0.05–0.17 ms of plain custom-table on every no-cache shared host, comfortably ahead of the post-meta variants.
  • Best cache utilisation when caching is present: drops to 1 DB query per dispatch on both redis and memcached, beating both post-meta-transients (which has the same cache profile but worse schema) and custom-table (which has the same schema but doesn't benefit from cache).

Avoid post-meta-transients as a fallback default: on no-cache environments it is at best a small win (H −11%, C −27%), at worst within noise of raw post-meta (D/E −2 to −5%), and on environment F it's catastrophic.


Caveats

  • Sample size: sustain is the load to trust (n=68–300+ per variant). single-idle (n=10) is too noisy for ranking.
  • Per-environment, single capture: each environment is one test run. Day-to-day noise (the environment E total_ms outlier) can move dispatch numbers by 10–30%; the strategy ranking has been stable across captures, but absolute deltas should be read as a band, not a point.
  • Cache-effect comparison (B vs C) is confounded by PHP version (8.3 vs 8.5). The directional signal is consistent with the rest of the data but a clean controlled comparison would need the same PHP across both cache configurations.
  • Each rtc-perf-result post is one host's submission, not aggregated; running the test multiple times produces additional posts.
  • One partial capture (PHP 8.0, no cache) ran only post-meta and is excluded from cross-strategy comparison.

Open questions for the working group

  1. Identify the WP core commit between revs 62278 and 62295 that halved custom-table's DB queries. Confirm intentional and forward-port to custom-table-with-transients if there's anything more to be gained.
  2. Environment F transient pathology: needs a host-side trace. Reproduce with WP_DEBUG_LOG + SAVEQUERIES and look at the slow query log for the 30s window.
  3. Managed cloud bootstrap (G): 630 ms is real and consistent. Out of scope for RTC, but it is the dominant variable in that platform's user-perceived latency for any small REST endpoint.
  4. Environment H bootstrap: 199 ms is ~4× slower than other no-cache shared environments. Not pathological, but worth confirming with the provider whether this represents typical traffic or a cold-cache run.
  5. More captures: only one persistent-cache config per cache-type so far. A second capture per environment would help disentangle environmental noise from strategy effects in the Δ-vs-baseline column.

Source code: WordPress/distributed-rtc-performance-testing. Underlying captures live on https://make.wordpress.org/hosting/ as rtc-perf-result posts; identifiers withheld here per the call-for-testing commitment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment