internal/planning/3253-gumroad-staging-benchmark-plan.md
Status: Planning — addresses Issue #3253 acceptance criteria 2 and 3 (stable deployed/staging repeat with full environment metadata and named measurement gaps).
Related: Issue #3128, Issue #3144, Issue #3259, Issue #3263, PR #3233.
Demo repo: shakacode/react-on-rails-demo-gumroad-rsc
Date: 2026-05-20
The April 30, 2026 local Gumroad benchmark documented in
performance-benchmarks.md is a
non-production directional signal. Its environment metadata (RAILS_ENV, hardware/OS, Ruby/Node/Rails versions,
browser-cache state) was not preserved and is not recoverable. The current docs hedge with that caveat, but readers
cannot calibrate the absolute timings without an environment baseline.
Issue #3253 asks for two follow-ups that this plan covers:
The plan below converts those into an executable protocol.
In scope:
/dashboard/inertia_demo vs /dashboard/rsc_demo) on a stable
staging-class host.performance-benchmarks.md env checklist.Server-Timing and/or Pro tracing).Out of scope:
The staging host must be stable across the full measurement window (no autoscaling, no shared CPU contention). Acceptable options, in order of preference:
Required configuration:
RAILS_ENV=production, NODE_ENV=production.RENDERER_PORT=3800, worker count fixed and recorded.n = 30 per route (60 total measured runs). This is enough to compute a real p95 rather
than a max-as-p95 substitute, and to bound the confidence interval on the median.responseEnd, controller
action_total, Server-Timing values, page-script request count, total transfer size.The local run could not attribute Rails wall time to the RSC render/payload split because the Pro Node renderer runs out-of-process. The staging repeat must close that gap with one of:
Server-Timing. Emit Server-Timing headers from the Rails controller covering: total controller
time, RSC payload-fetch time (round trip to the Pro Node renderer), HTML render time. The browser captures these on
PerformanceServerTiming entries and they can be read directly from Playwright. This is the most precise option and
it is visible from the browser side.config.tracing = true in config/initializers/react_on_rails_pro.rb. This logs
renderer-side timings to the Pro Node renderer log. Correlate with controller logs via request ID.The plan should produce at least the Server-Timing path. Tracing is a belt-and-suspenders backup for any timings the
browser cannot see.
Each staging run must publish (alongside the existing docs/performance-findings.md artifact in the demo repo):
environment.md — full env checklist from
performance-benchmarks.md,
filled in completely. Missing fields explicitly marked unknown rather than omitted.raw-runs.csv — one row per measured run with every captured metric. This is the distribution artifact #3263 has
been asking for.summary.md — median, p95, min, max, IQR for each metric; explicit n and variance commentary; renderer-internal
timing breakdown.methodology.md — exact run protocol, warmup script, browser-profile reset procedure, alternation pattern,
hardware/OS, tool versions.The demo repo's docs/performance-findings.md should link to all four.
When the staging numbers land, update
performance-benchmarks.md to:
responseEnd table with n = 30 p95 rather than max-as-p95.Server-Timing row to the metrics table so readers see the renderer-internal split.Issue #3253 closes when:
This plan tracks criteria 2 and 3; executing the staging run is a separate work item.
Server-Timing instrumentation. The Pro Node renderer round-trip is the trickiest segment; the Rails controller
may need a small wrapper around the renderer client to capture the round-trip duration. Plan a 1–2 hour spike before
the run to confirm the instrumentation works.n = 20 and accept wider confidence intervals.