react_on_rails_pro/CONTEXT.md
How the Node Renderer bundle cache is warmed so that during a rolling deploy — when old and new app versions run side by side — no SSR request pays the cold-bundle penalty. Covers the seeding mechanism and how it interacts with the deploy pipeline (build vs. release, staging-to-production promotion).
Rolling deploy: The window where old (draining) and new app instances run side by side, both sending SSR requests to the Node Renderer fleet.
Draining bundle: The bundle hash that draining old Rails instances still request during the overlap; the new renderer fleet must serve it warm. Avoid: "old bundle" (old relative to what? — the draining bundle is defined by what is live-and-draining in this environment, not by build order).
Pre-seed: Staging previous bundle hashes into the new renderer cache before it serves traffic, so draining bundle requests hit warm cache instead of the 410 fallback. Avoid: warmup (means the per-request lazy cache fill, the opposite of this).
Pre-seed source:
The deployment(s) the pre-seed pulls bundles from — resolved from that
environment's rolling_deploy_previous_urls (HTTP adapter) at the moment the
seed runs. The source is whatever environment's config is in effect when and
where the seed executes.
Build-time seed:
Pre-seed baked into the image during assets:precompile. Uses the building
environment's config and a snapshot of its then-live bundle. Frozen into the
image layer.
Release-time seed (a.k.a. boot seed):
Pre-seed run by the target environment's renderer container at container boot
via rake react_on_rails_pro:pre_seed_renderer_cache, resolving that
environment's actually-live bundle at that moment. Readiness-gated.
Avoid: "runtime seed" (ambiguous with the per-request 410 path).
Multi-source seed:
A build-time seed whose pre-seed source is a list of endpoints (the
built-in HTTP adapter's rolling_deploy_previous_urls accepts more than one).
Staging seeds from both staging and production so the promoted image is born
prod-ready.
Promotion model: Production is deployed by promoting the exact staging image, not by building a fresh production image. Consequence: the image's build-time seed used the staging pipeline's config and a snapshot taken at staging-build time — never production's.
410 fallback:
The self-healing but slow per-request cold path: cache miss → 410 Gone → Rails
ships the bundle to the renderer → retry, repeating per request until cached.
The thing pre-seeding exists to avoid.
Seed correctness (R1): The requirement that the new renderer fleet holds the exact draining bundle for this environment at this release.
Deploy ordering (R2): The requirement that the new renderer fleet is live and cache-warm before new Rails takes traffic, and old renderers stay up until old Rails drains. Also called renderer-before-Rails.
Correctness is layered; each layer bounds the failure the prior one leaves open.
Interdependency: the boot seed returns the correct draining bundle only because the renderer boots before Rails (R2). Before new Rails is live, the environment's live endpoint is still served by the draining old pods, so it advertises the draining hash. If Rails cut over first, the live endpoint would advertise the new hash and the boot seed would miss the very bundle it needs. Layer 2 depends on Layer 3.
Dev: "We promote the staging image to prod, and pre-seed is on. Why is prod SSR still slow after a rolling deploy?" Domain expert: "Because the build-time seed ran in the staging pipeline — the image holds staging's previous bundle, not prod's draining bundle. The seed was correct for the wrong environment." Dev: "So if staging's pre-seed source points at production, the promoted image is already prod-ready?" Domain expert: "For a single pending promotion, yes. Two pending promotions still go stale — that's why promotion also runs a release-time seed against prod's live bundle." Dev: "And that's enough?" Domain expert: "That's R1. You still need R2 — the new renderer fleet live and warm before Rails cuts over — or you get the 410 storm anyway."