You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Roland's policy for platform rolls — the aim is to always serve fast and never disturb the user:
As long as there is activity on a grain, we don't tear it down. Deactivation is for idle grains only; a roll must not evict pages people are looking at.
We bake, and until baking is finished, every grain allocation goes to an OLD silo. The new generation earns traffic by having every assembly warm; the old generation is warm by definition.
If the bake fails, we never switch away from the old silos. A broken new generation is a non-event for users, not an outage — fail closed toward OLD, never toward cold/broken NEW.
Only a successful and finished bake opens the new generation — from then on, new grains are allocated on the new silos. Old silos drain naturally (rule 1) and retire.
A recycled grain re-hashes onto the new generation — the explicit early-move. Everything else transitions smoothly: activity-pinned until idle, re-placed on next activation.
Gap analysis (memex, 2026-08-12)
The roll is a single-Deployment rolling update: the old pod dies grains-and-all — rule 1 has no mechanism at all (preStop/grace only softens the kill).
Observed same day: /SST/Subscribe "took forever with many redirects" — cold hub activations through the paywall redirect chain after a roll + syncs.
Shape of the implementation
Two-generation topology during a roll (old ReplicaSet kept serving, not scaled to zero), placement director that routes NEW activations by bake state (old until bake-success, new after), activity-based deactivation as the only drain, and recycle as the explicit re-hash. The readiness gate (DynamicTypePreWarmer) becomes the bake-success signal that OPENS the new generation rather than a boolean that blocks the only pod.
Policy is documented in Doc/Architecture/ReleaseToProductionPipeline → "The roll policy — serve fast, never disturb" (PR to follow).
Audited 2026-08-21 against the live memex deployment. The policy in this issue (serve fast,
never disturb the user) is unchanged and correct. The mechanism it proposes —
bake-gated silo generations, a two-generation topology, a placement director routing new
activations by bake state — no longer addresses a real present cost, because there is no boot bake
left to gate on.
The gap analysis dated itself — all three items are now false
This issue's own "Gap analysis (memex, 2026-08-12)" is the right thing to re-measure, so here it is
re-measured, on the roll that landed at 08:40Z today:
"The image bake contributes zero (alreadyBaked=0, #1347): the new pod boots cold and the 76 s
prewarm sweep races the first visitors"
ShippedPrebuiltBundles: 50 prebuilt assembly(ies) from 19 shipped bundle(s) under
/data/prebuilt-bundles/s335e04b229c182637869bebae8687c94 are backed by the assembly store —
0 adopted now, 50 already current and skipped WITHOUT activating their NodeType hubs — in 00:00:02.38
DynamicTypePreWarmer: ADOPT-ONLY boot complete in 00:00:30.27 — adopted=50 uncovered=59 of 109
dynamic NodeType(s); nothing was compiled at boot (PreWarm:DynamicTypes!=true).
framework=s335e04b total=109 baked=50 pending=59 — managed +198 MB, working set +211 MB
Against #1814's measured before-number — "every deploy's first pod compiles all 269 types (~10 min,
+1598 MB)" — that is ~10 min → 30 s, +1598 MB → +211 MB, and 269 compiled → 0 compiled.
"The roll is a single-Deployment rolling update: the old pod dies grains-and-all — rule 1 has no
mechanism at all (preStop/grace only softens the kill)."
Rule 1 has a mechanism now, and it is #1342. Measured on the same roll: the old pod was
SIGTERM-scheduled at 08:40:52Z and was still 3/3 Running, serving, and executing application code
at 09:09:33Z. It kept its grains for the same 29 minutes, for free — MessageHubGrain is [PreferLocalPlacement], so a circuit's hubs live on the pod serving that circuit.
"the sweep warms the most user-visible types … last (#1347)"
The compiling sweep was retired fleet-wide on 2026-08-17 (a31dd19b8); adoption always runs, and
ordering within a sweep that no longer compiles is moot.
Rule by rule
rule
state
1
activity-pinned grains, no teardown while in use
delivered — #1342 + [PreferLocalPlacement], bounded by terminationGracePeriodSeconds
2
until the bake finishes, allocate on OLD silos
premise gone — boot compiles nothing; there is no bake to wait for
3
if the bake fails, never switch away from OLD
premise gone — same
4
only a finished bake opens the new generation
premise gone — same
5
a recycled grain re-hashes onto the new generation
works; the old pod is out of the Service, so new sessions land on the new pod without a placement director
Rules 2–4 are all one bet: that a deploy's cost is a bake happening at boot on the serving pod.
That bet was correct when this was filed and is not correct now. The maintainer's own direction in #1660 says the same thing from the other end — "we should not dynamically build at all … built on
CI and just loading into assembly cache" — and that is what shipped.
59 of 109 types on memex are not covered by the shipped bundles and still pay a Roslyn compile on first access — rsalzmann/*, UWDeepfield/*, RFPProcessor/*, i.e. DB-resident user and
partition types that CI cannot see. That is #1660 Workstream 2 verbatim ("Pre-roll bake Job for
DB-resident types"), already open and already owned. It does not need this issue, and it does not
need silo generations — it needs the bake to run before the roll, which is what WS2 is.
I am deliberately not filing a successor for the two-generation topology. Its three stated
prerequisites — expand/contract Postgres migrations for the whole overlap window, grain state
round-tripping both ways, and a second full set of assemblies resident — are all costs of that
design. The bounded-overlap design that shipped pays none of them.
Reader six months from now: start at #1660 for the build-on-CI direction, #1342 for why your
sessions survive a roll, and #1814 for the incident that produced the before-numbers above.
Activity
rbuergi commentedon Aug 21, 2026
Verdict: SUPERSEDED — closing
Audited 2026-08-21 against the live
memexdeployment. The policy in this issue (serve fast,never disturb the user) is unchanged and correct. The mechanism it proposes —
bake-gated silo generations, a two-generation topology, a placement director routing new
activations by bake state — no longer addresses a real present cost, because there is no boot bake
left to gate on.
The gap analysis dated itself — all three items are now false
This issue's own "Gap analysis (memex, 2026-08-12)" is the right thing to re-measure, so here it is
re-measured, on the roll that landed at 08:40Z today:
Against #1814's measured before-number — "every deploy's first pod compiles all 269 types (~10 min,
+1598 MB)" — that is ~10 min → 30 s, +1598 MB → +211 MB, and 269 compiled → 0 compiled.
Rule 1 has a mechanism now, and it is #1342. Measured on the same roll: the old pod was
SIGTERM-scheduled at 08:40:52Z and was still
3/3 Running, serving, and executing application codeat 09:09:33Z. It kept its grains for the same 29 minutes, for free —
MessageHubGrainis[PreferLocalPlacement], so a circuit's hubs live on the pod serving that circuit.The compiling sweep was retired fleet-wide on 2026-08-17 (
a31dd19b8); adoption always runs, andordering within a sweep that no longer compiles is moot.
Rule by rule
[PreferLocalPlacement], bounded byterminationGracePeriodSecondsRules 2–4 are all one bet: that a deploy's cost is a bake happening at boot on the serving pod.
That bet was correct when this was filed and is not correct now. The maintainer's own direction in
#1660 says the same thing from the other end — "we should not dynamically build at all … built on
CI and just loading into assembly cache" — and that is what shipped.
What replaced it
/drainsession drain: preStop holds the pod until its last circuit closes, socircuits and their grains survive a roll. This is rule 1, and it is what made a
two-live-deployments topology unnecessary to get it.
alreadyBaked=0. With that fixed the shipped bundles are found and adopted.generation pointer, the install-records partition is durably readable.
a31dd19b8(2026-08-17) — the compiling boot sweep retired; boot is adopt-only.What survives, and where it already lives
59 of 109 types on
memexare not covered by the shipped bundles and still pay a Roslyn compile onfirst access —
rsalzmann/*,UWDeepfield/*,RFPProcessor/*, i.e. DB-resident user andpartition types that CI cannot see. That is #1660 Workstream 2 verbatim ("Pre-roll bake Job for
DB-resident types"), already open and already owned. It does not need this issue, and it does not
need silo generations — it needs the bake to run before the roll, which is what WS2 is.
I am deliberately not filing a successor for the two-generation topology. Its three stated
prerequisites — expand/contract Postgres migrations for the whole overlap window, grain state
round-tripping both ways, and a second full set of assemblies resident — are all costs of that
design. The bounded-overlap design that shipped pays none of them.
Reader six months from now: start at #1660 for the build-on-CI direction, #1342 for why your
sessions survive a roll, and #1814 for the incident that produced the before-numbers above.