feat(edge): gate per-minor routing on published minors and add a staging zone #175
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/verify-per-minor-routing"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Motivation
UNION_SLOTSis empty, so per-minor routing has never been exercised end to end. Before it can be enabled and advertised, two things were missing: a way to test it without pointing production at it, and evidence that the published indexes actually support it.Verifying the data first turned up a defect that would have broken users the moment the flag was flipped.
The defect
contribPath()redirects tocontrib/<minor>/whenever the User-Agent carries any R minor, with no existence check and no fallback:Only
4.4,4.5and4.6are published.4.3and4.2return 404 on all 16 slots. WithUNION_SLOTSset, an R 4.3 client would be redirected to a non-existent index and see zero packages: a silent, total failure rather than a degraded one. R 4.3 is still advertised as supported on the website and indocs/configuration.mdoc, thoughbuild-env-imagesnow pins only 4.6.0/4.5.3/4.4.3.Changes
KNOWN_MINORS(default4.4,4.5,4.6), falling back to the flat index for anything else. Unknown minor now behaves exactly as today.EXTRA_PUBLIC_HOSTS.publicCdnOrigin()falls back to the hardcodedPUBLIC_CDN_ORIGINfor any hostname not inPUBLIC_CDN_HOSTS, so a staging zone on ab-cdn.nethostname would redirect into production and silently measure the wrong system. This lets the identical script run on staging and redirect within itself.cran-rpkgs-testpull zone withUNION_SLOTSpre-enabled for all 16 slots, same B2 origin, served on the bunny default hostname so it needs no DNS record and is never advertised.scripts/verify-r-minor-routing.sh, covering every<arch>/<os>slot: index reachability per minor, the union property against flat,Path:target resolution, coverage parity across minors, and with--livethe real User-Agent routing, the non-R User-Agent case, and that tarballs are never rewritten.Findings from the full run
112 passed, 16 failed across the 16 slots. Every failure is the same: no R 4.3 index.
All 16 slots carry union indexes that are supersets of flat, every sampled
Path:target resolves, and all indexes were republished within minutes of each other, so the build side is healthy.Coverage is not yet even, which is why "full coverage for ABI-sensitive packages" is not a claim to make yet:
On the R 4.5-built distros (
resolute,alpine324, and their arm64 twins) a 4.4 or 4.6 client sees ~300 fewer packages than a 4.5 client. Onnoble/jammy/rhel9/alpine323the spread is under 5. The new parity check encodes this with a configurablePARITY_TOLERANCE.Verification
just edge-test: 18 steps pass. The two new steps were confirmed to fail with theKNOWN_MINORSgate removed and pass with it.tofu validate: passes. Not applied - no bunny.net or state credentials were available, so the staging zone still needs atofu apply.scripts/verify-r-minor-routing.sh: full 16-slot run, results above.shellcheck: clean.Not done here
Applying the staging zone, then running
BASE=https://cran-rpkgs-test.b-cdn.net scripts/verify-r-minor-routing.sh --liveagainst it. ProductionUNION_SLOTSis deliberately left empty.Updated after the decision to send unsupported R minors to CRAN rather than serving them, and to state a supported window rather than a list of versions.
Supported window
The current R minor plus the two previous, which is exactly what
build-env-imagesinstalls:Today that resolves to 4.4, 4.5, 4.6, matching the published indexes on all 16 slots. 4.3 left the window when 4.6 landed.
This is now declared once as
local.rpkgs_supported_minorsincdn.tfand pushed toKNOWN_MINORSon both zones, so the router cannot drift from the build images unnoticed. A published minor missing from the list would send those clients to CRAN despite us having built for them; a listed minor without an index would 404 them. The verification script fails if the two lists disagree.Excluded minors go to CRAN
parseSlot()already sends an unidentifiable distro to CRAN, so an unsupported R minor now does the same: the client gets source packages that compile against their own R rather than a 404 or binaries built under a different minor.The whole interaction moves, not just the index. R resolves tarball URLs against the repo it was configured with, so serving
PACKAGES.gzfrom CRAN while serving tarballs from here would hand R a binary where it expects a source tarball — a worse failure than the one being fixed. Covered by a test.A client that reports no R minor at all is deliberately not excluded: mirror scripts and image builds keep getting the flat slot.
Full run: 140 passed, 4 failed
All four failures are the same coverage gap, on the R 4.5-built distros:
The other 12 slots pass everything: index reachability per minor, the union property against flat,
Path:target resolution, and coverage parity within tolerance.So 12 of 16 slots are ready to enable. The remaining 4 are exactly the ones the website already warns about ("Alpine 3.24, RHEL 10 and Ubuntu 26.04 will be built using R 4.5.x and hence will not be fully compatible"), except RHEL 10 has since closed its gap and Ubuntu 26.04 and Alpine 3.24 have not.
Verification
just edge-test: 20 steps pass, including the CRAN fallthrough for an excluded minor, for a future minor, and for tarballs.scripts/verify-r-minor-routing.sh: 140/4 as above.tofu validate: passes. Still not applied — no bunny.net or state credentials in this environment.shellcheck: clean.Still needed
tofu applyfor the staging zone, thenBASE=https://cran-rpkgs-test.b-cdn.net scripts/verify-r-minor-routing.sh --live.Updated after the decision to send unsupported R minors to CRAN rather than serving them, and to state a supported window rather than a list of versions.
Supported window
The current R minor plus the two previous, which is exactly what
build-env-imagesinstalls:Today that resolves to 4.4, 4.5, 4.6, matching the published indexes on all 16 slots. 4.3 left the window when 4.6 landed.
This is now declared once as
local.rpkgs_supported_minorsincdn.tfand pushed toKNOWN_MINORSon both zones, so the router cannot drift from the build images unnoticed. A published minor missing from the list would send those clients to CRAN despite us having built for them; a listed minor without an index would 404 them. The verification script fails if the two lists disagree.Excluded minors go to CRAN
parseSlot()already sends an unidentifiable distro to CRAN, so an unsupported R minor now does the same: the client gets source packages that compile against their own R rather than a 404 or binaries built under a different minor.The whole interaction moves, not just the index. R resolves tarball URLs against the repo it was configured with, so serving
PACKAGES.gzfrom CRAN while serving tarballs from here would hand R a binary where it expects a source tarball — a worse failure than the one being fixed. Covered by a test.A client that reports no R minor at all is deliberately not excluded: mirror scripts and image builds keep getting the flat slot.
Full run: 140 passed, 4 failed
All four failures are the same coverage gap, on the R 4.5-built distros:
The other 12 slots pass everything: index reachability per minor, the union property against flat,
Path:target resolution, and coverage parity within tolerance.So 12 of 16 slots are ready to enable. The remaining 4 are exactly the ones the website already warns about ("Alpine 3.24, RHEL 10 and Ubuntu 26.04 will be built using R 4.5.x and hence will not be fully compatible"), except RHEL 10 has since closed its gap and Ubuntu 26.04 and Alpine 3.24 have not.
Verification
just edge-test: 20 steps pass, including the CRAN fallthrough for an excluded minor, for a future minor, and for tarballs.scripts/verify-r-minor-routing.sh: 140/4 as above.tofu validate: passes. Still not applied — no bunny.net or state credentials in this environment.shellcheck: clean.Still needed
tofu applyfor the staging zone, thenBASE=https://cran-rpkgs-test.b-cdn.net scripts/verify-r-minor-routing.sh --live.Diagnosing a live R 4.6.1 breakage (
rlang,undefined symbol: SETLENGTH) changed what "enable this in prod" requires. Recording the state so the sequence is explicit.What the breakage showed
With routing off, an R 4.6.1 client is served the generic slot's
rlang, built under R 4.4.3.SETLENGTHis a non-API symbol R 4.6 no longer exports, sodyn.load()fails. This is the exact failure per-minor routing exists to prevent — an argument for enabling it.But enabling it as-is would have been a net regression:
rlang's 4.6 artifact is a source fallback, and 320 of the 351 fallbacks in that slot have working binaries in the generic slot.Why so many fallbacks, and what is actually broken
Not the builds. The generic slot for
nobleis built entirely under R 4.4 (23,899 binaries), and 342 of the 375 source fallbacks in the 4.4 per-minor slot have a 4.4-built binary sitting in generic. Same minor, same ABI. The per-minor pass is incomplete, andunion_index_records()steers clients to the source anyway because a per-minor record shadows the generic one unconditionally.Fixed in rpkgs/bincraft#113. Effect:
Non-primary minors are unchanged by design: there the generic binary is built under a different minor, so a source fallback is the correct answer for an ABI-risky package.
Where that leaves enablement
After bincraft#113 and a reindex, each slot's primary minor drops to roughly 5% fallbacks, comparable to the generic slot's own 3.6%. Non-primary minors stay near 50% until the per-minor passes actually complete.
So routing is safe to enable for clients on a slot's primary minor, and still degrades to source compiles for the others. That is correct behaviour, just not yet fast.
Sequence
scripts/verify-r-minor-routing.shand confirm the fallback rates above.tofu applythe staging zone. Blocked: no bunny.net or Terraform state credentials in the agent environment, and there is no CI pipeline that runstofu— it is a manual step.BASE=https://cran-rpkgs-test.b-cdn.net scripts/verify-r-minor-routing.sh --live.UNION_SLOTS.Making it automatic
UNION_SLOTSandKNOWN_MINORSare hand-maintained lists that will drift. Two options, neither built yet:UNION_SLOTSto the bunny script variable via the API after each reindex. A slot enables itself once its fallback rate is acceptable and drops out if it regresses.KNOWN_MINORSfrom the build images. It should come frombuild-env-images'R_VERSION_LATEST/PREV1/PREV2rather than being retyped incdn.tf. Today the coupling is a comment; a CI check comparing the two would make it enforceable.Both need a decision on where the automation runs and a Crow secret for the bunny API. Worth its own issue rather than expanding this PR.
Still outstanding
SETLENGTHis not inabi_volatile_symbols.txt.rlanggets per-minor treatment anyway viaabi_risky_linking_deps.txt, but a package usingSETLENGTHwithout linking to rlang/Rcpp/cpp11 would be classified safe-compiled and land in generic with exactly this failure. The list's header says to append symbols that surface breaks; this one just did. Not added here because it triggers rebuilds.