Repository navigation
memex cannot self-update: the portal image is ahead of its schema (db_version 54 < 55) and only helm upgrade can run the migration Job #3207
Description
Found while auditing config drift for #2355. Not drift — a live wedge, and it blocks that cleanup.
memex cannot self-update: the image walked past its schema
crit: Memex.Portal.Distributed.DbVersionGate[0]
DB migration incomplete: admin.mesh_nodes.db_version=54 < expected 55.
Refusing to start the portal until the DB is fully migrated.
The gate is doing exactly its job — a portal ahead of its schema refuses to serve rather than serving a half-migrated one. What follows it in the log (an OrleansProvisioningGate cancellation, Hosting failed to start, then a 685 MB core dump) is downstream noise of the shutdown, not the fault.
🚨 exitCode 139 on the container, but the dump records NT_SIGINFO … signo 6 — SIGABRT, not SEGV. Anyone triaging this from the exit code alone will go looking for a native crash that is not there.
Three self-updates today, all wedged the same way
| time (UTC) | ReplicaSet | image | ready |
|---|---|---|---|
| 09-02 23:14 | 5c8c476d54 |
ci.7632 |
2/2 — serving |
| 09-03 07:20 | 6c74846cf8 |
ci.7647 |
0 |
| 09-03 08:20 | 5d55558945 |
ci.7651 |
0 |
| 09-03 10:27 | 54f4c96b6b |
ci.7658 |
0 — CrashLoopBackOff, 14 restarts |
Available=True MinimumReplicasAvailable
Progressing=False ProgressDeadlineExceeded
Users are unaffected — maxUnavailable: 0 keeps the ci.7632 pods serving, which is the rollout policy working. But memex has been unable to take a new build since ~07:20Z, and each attempt leaves a pod crash-looping on a 5-minute back-off and writing a ~685 MB core dump to /data/dumps.
Why the schema never advances — the actual deadlock
There is no migration Job in either namespace. kubectl get jobs -n memex returns only assembly-cache-prune.
That is structural, not an accident:
- Self-update rolls the IMAGE only. It patches the Deployment from inside the pod. It cannot create the migration Job, so it can never carry the schema forward with the code.
- Only
helm upgradecreates the migration Job — the run-onceJobthe chart defines (per AGENTS.md, and memex-migration CrashLoopBackOff in all three prod namespaces (50/53/38 restarts) — run-once workload modelled as a Deployment #1788 is what happens when it is modelled as a Deployment instead).
So every schema-bumping release is, by construction, un-takeable by self-update. The instance rolls forward until it meets a build that needs a new schema, and then wedges — silently, because Available=True and the portal keeps serving the old build.
The fix is the sanctioned path, and its preconditions check out
helm-release.yml (mode deploy) is built for exactly this, and its --set block is explicit that the migration Job must ride the same tag as the portal — "a migration from a different build than the code that will run against it is how a schema lands half-applied."
Two preconditions verified before proposing it:
- The stale overlay pins are NOT a hazard. Both overlays still pin
portal.image/migration.imageat3.0.0-rc8.ci.5068, buthelm-release.ymlapplies--setafter both-ffiles, so the live (or requested) image wins. I checked this specifically because a rollback fromci.7632torc8.ci.5068would have been catastrophic — it does not happen. (The pins are still worth refreshing so the committed file is not misleading, but they are inert.) - The matching migration image exists. memex-migration Job cannot pull: helm-release derives its tag from the PORTAL image, and the two ACR repos do not share a tagging convention #2555 was a pull failure because the two ACR repos do not share a tagging convention. Not the case here —
meshweaver.azurecr.io/memex-migrationcarries3.0.0-rc9.ci.7658,.7669and.7671, matchingmemex-portal-aitag for tag.
Suggested remedy
Run helm-release.yml against memex with an explicit image at the target build. It sets portal.image to that build and derives migration.image from the same tag, so helm upgrade runs the migration Job at the matching version, the schema moves 54 → 55, and the already-attempted portal starts.
Worth confirming afterwards, per the deployment runbook: the Job logged Database migration completed. Version: 55, and the portal serves HTTP 200.
memex-cloud is behind the same wall
It serves ci.7621 today and is healthy, so it has not met a build needing schema 55 yet. Its next self-update past ci.7647 hits this identically. Whatever is done for memex should be done for it in the same pass, rather than waiting for it to wedge.
And it blocks #2355 / Memex#148
Any inline-env: deletion changes the pod template and spawns a new ReplicaSet, which lands on the same gate. The drift cleanup cannot proceed until the schema is current.
Measured 2026-09-03 on the live cluster: pod/RS state, deployment conditions, the crashed container's previous logs, kubectl get jobs, and ACR tag existence.
Activity
rbuergi commentedon Sep 3, 2026
Correcting my own remedy: this is a KNOWN, DOCUMENTED gap, and the fix is not a manual deploy
I suggested running
helm-release.ymlwith an explicitimage. Withdrawing that. It is a manual tag pin, and it contradicts the standing directive — "can we please see that we roll always via cd? this stabilizes" (maintainer, 2026-08-25). A hand-pinned roll is the thing that directive exists to stop, and "a consistently-OLD portal is a SAFE state": memex is servingci.7632withAvailable=True, so there is nothing here that justifies bypassing CD.The root cause is already written down — and it has now fired
Doc/Architecture/DeploymentAKS→ "Migration under self-update" describes this exactly:So the news in this issue is not the mechanism — it is that the gap has stopped being theoretical. It fired three times on memex today (
ci.7647,ci.7651,ci.7658), andmain-cd.ymlalready anticipated the shape in its own comments:CD builds and publishes a correctly-tagged
memex-migrationimage on every run (3.0.0-rc9.ci.7658,.7669,.7671all exist, matchingmemex-portal-aitag for tag). Nothing in the continuous path ever applies it. Continuous delivery ships a migration it never runs.One thing the doc predicts that did NOT happen, and it matters
The doc says a portal outrunning its schema means "the namespace has no serving portal". That is not what happened, and the reason is worth recording:
maxUnavailable: 0+maxSurge: 1kept the twoci.7632pods serving while the new ReplicaSet failed to go Ready. The rollout policy converted a fail-closed boot into a stalled rollout instead of an outage — exactly what that setting is for, and the same property the roll-via-CD note flags as "what keeps a fatal boot from becoming an outage — verify it before ANY roll."So the live severity is lower than the doc's worst case: no outage, a stalled rollout plus a pod crash-looping on a 5-minute back-off writing a ~685 MB core dump per attempt.
The fix, stated as a change to the pipeline rather than an operator action
The updater's migration leg targets the wrong object and runs in the wrong order:
memex-migration-deployment(the legacy shape). The chart renders a run-oncebatch/v1Job,memex-migration-<revision>. The updater should CREATE that Job at the target tag and await completion, not patch a Deployment that does not exist.resourceNames-scoped tomemex-migration-deployment. Creating and awaiting a Job needsbatch/jobsrights in its own namespace.Done that way the schema and the image move together in one CD-driven pass, which is the same atomicity argument that produced the roll-via-CD directive in the first place.
Until that lands, the honest status is: memex stays on
ci.7632and that is the safe state. It is not an outage and should not be "improved" by a hand roll.Root cause corrected 2026-09-03 after re-reading the deployment doc and
main-cd.yml. The measurements in the issue body stand; the suggested remedy in it does not.rbuergi commentedon Sep 3, 2026
The target build is now available and verified:
3.0.0-rc9.ci.7674The remedy in this issue needs a build that satisfies three things at once. As of now one does, and I have checked each rather than inferred it.
1. It carries the #3202 fix, so it does not ship the sign-in 503.
This is the constraint the issue could not have known about — it was filed at 11:28, and the freeze that followed matters here. Every
memex-portal-aiimage built after ~08:03Z returns 503 for every signed-in user (OnboardingMiddleware.LoadUserRolesissues an unanchorednodeType:AccessAssignment … scope:subtree, which Plugins#1231'sUnanchoredQueryExceptioncorrectly refuses). Deploying most of today's builds would trade a wedge for an outage.ci.7674is built from core8ffbe4762, which is the merge commit of #3206 (fix/3202-sign-in-reads-are-anchored). Verified by ancestry, not by tag order.2. It is a COMPLETE promoted set, so the migration Job and the portal ride the same build — the condition
helm-release.yml's own--setblock insists on ("a migration from a different build than the code that will run against it is how a schema lands half-applied"):3.0.0-rc9.ci.7674memex-portal-aisha256:4f2bc53e35b6…memex-migrationsha256:0722416f1c63…mw-plugin-testsha256:ad18945d16bc…3. The matching migration image exists —
memex-migrationcarries3.0.0-rc9.ci.7674, so this is not the #2555 shape (a pull failure from the two ACR repos not sharing a tagging convention).🚨 Sequencing — this cannot be run unilaterally today
A fleet freeze is in effect and is not mine to lift. Both production portals are deliberately pinned with
Admin/UpdatePolicy = None(memex.meshweaver.cloud atci.7621, memex.systemorph.com atci.7651) pending the #3202 fix being merged AND in a promoted image. That condition is now satisfied byci.7674— but the decision to lift belongs to the session holding the freeze, and thehelm upgradeitself is a production action that needs an explicit human go-ahead.So the order is: freeze lifts → then this runs at
ci.7674, not the other way round.One correction worth carrying
The issue's triage note is right and worth repeating because it will mislead anyone reading the exit code alone:
exitCode 139here is SIGABRT (signo 6), not SEGV. There is no native crash to hunt. The 685 MB dump and theOrleansProvisioningGatecancellation are shutdown noise downstream ofDbVersionGaterefusing to start — which is the gate working correctly.And the structural half is the real finding
That means every schema-bumping release is un-takeable by self-update, by construction — the instance rolls forward until it meets one, then wedges silently behind
Available=True. Fixingmemexatci.7674clears today's wedge; it does not stop the next schema bump doing this again, on either instance.memex-cloudis behind the same wall and has simply not met a schema-bumping build yet, so it should be done in the same pass rather than left to wedge on its own.rbuergi commentedon Sep 3, 2026
🚨 Correction to my previous comment —
ci.7674is NOT a safe target. Do not roll to it.My previous comment named
3.0.0-rc9.ci.7674as the verified target. That was wrong, and I am retracting it before anyone acts on it. Credit to the session running the build-process work for catching it.What I checked, and what I failed to check
I verified that
ci.7674(a) carries the #3202 sign-in fix — true, it is built from core8ffbe4762, the merge commit of #3206 — and (b) exists as three platform images:memex-portal-ai,memex-migration,mw-plugin-test. I called that a "complete promoted set".That definition is necessary but not sufficient. The binding condition is a sealed Plugins publication for the platform identity, and
ci.7674has none. Measured on its CD run33749847612:The seal FATALed on the #3175 one-producer guard:
So
ci.7674is platform image present, plugin modules absent for that identity (frameworksddbf8fa0…). Rolling to it produces exactly the half-broken state the maintainer's rule forbids: all plugins available for the correct platform version, else nothing goes.The corrected target
The first tag safe for both the freeze lift and this issue's
helm upgradeis the first CD run afterfix/3175-markdown-collaboration-one-producermerges — the one-producer fix. That run is the first that can produce a complete sealed set. (It is also the #2543 control arm, so it is being watched closely for other reasons.)That PR is not open yet. Until then there is no safe target, and this issue's remedy waits — not because of the schema, but because rolling to any current tag would swap a silent wedge for missing plugin modules.
The lesson worth keeping
"Complete promoted set" is a phrase that reads as sufficient and is not. Three images can be promoted all-or-nothing while the publication that makes them usable never sealed —
PromoteandVerify every image shippedboth go green in that state. A CD run's conclusion, and even a green Promote, says nothing about whether the modules for that identity exist. The check that discriminates is thePlugins: bake + sealjob, or the sealed publication for the framework identity — not the presence of tags.Nothing has been rolled, no hold lifted, no cluster touched. The structural half of this issue is unaffected and still stands: schema-bumping releases remain un-takeable by self-update, and
memex-cloudis behind the same wall atci.7621.rbuergi commentedon Sep 3, 2026
The structural half of this issue is now committed documentation rather than living only in this
thread:
Doc/Architecture/SelfUpdateSchemaWall(
src/MeshWeaver.Documentation/Data/Architecture/SelfUpdateSchemaWall.md), merged in #3215 at 13:30Z.It carries the finding as stated here — self-update patches the image, the migration is a run-once Job
only
helm upgradecreates, so an instance self-rolls forward until it meets a schema-bumping releaseand then wedges silently behind
Available=True— plus why it is structural rather than a bad build,the interaction with making memex the release-control instance, and the three candidate remedies
left explicitly open (self-update drives the migration at a schema boundary / releases are flagged
and the instance holds / status quo made legible and alarmed). It deliberately picks none; that is
still this issue's decision.
It also carries the target-selection half, which is what nearly went wrong here: a safe
helm upgradetarget must clear three conditions, each measured — carries the fix by ancestry (not tag
ordering), has
memex-migrationat the same tag, and has a GREENPlugins: bake + sealonthat tag's CD run.
ci.7674was proposed as this issue's target on the strength of the first two;its seal was RED, so it was platform-present / modules-absent and would have traded a wedge for a
half-broken portal.
One correction landed with it:
ContinuousDeliveryContract.mdclaimed the roll patches "the portaland migration Deployments" — false since #2797, and contradicting
DeploymentAKS.md. That sentenceis part of why this wall stayed invisible: it tells a reader self-update carries migrations.
Cross-links were added from
Deployment.md,DeploymentAKS.md("Migration under self-update"),ContinuousDeliveryContract.mdand theArchitecture.mdtopic map, so the next person arrives at itwithout knowing it exists.
rbuergi commentedon Sep 4, 2026
Resolved — and the resolution REFUTES the structural claim in this issue. Re-measured 2026-09-04 05:5x UTC.
Before acting on the proposed
helm-release.ymlrun, I re-ran this issue's own measurement. memex is no longer wedged:The three crash-looping ReplicaSets this issue tabulated are all scaled to 0 and superseded:
6c74846cf85d5555894554f4c96b6b77898c4947memex-cloudis on the same image (3.0.0-rc9.ci.7693, 3/3 Running). Both portals answer:DbVersionGatestops the app with aLogCriticalwhen the schema is behind, so two pods Running with zero restarts for 12 h is itself the proof thatdb_versionreached the expected value — a portal ahead of its schema cannot reach Ready.🚨 The interesting part: no
helm upgraderanRevision 28 is from Aug 30 — five days before the schema advanced. So the schema was carried forward between 09-03 07:20Z and 09-03 ~17:45Z without the sanctioned
helm upgradepath this issue proposes.That contradicts this issue's central structural claim:
That was true when this was filed and is no longer true.
Doc/Architecture/DeploymentAKSnow records the change:So the deadlock this issue names was closed by the self-updater learning to mint its own migration Job, landing the same day this was filed. The instance then healed itself: it re-attempted, the Job ran, the gate passed, and ci.7693 came up.
One honest limit on the evidence: I did not directly observe the
memex-migration-su-*Job —ttlSecondsAfterFinishedcleans it up and the Kubernetes event window has long since rolled past 12 h, sokubectl get jobsnow shows onlyassembly-cache-prune. What is directly measured is the outcome (schema advanced, gate passed, portal serving) and the absence of the alternative explanation (no helm revision since Aug 30). If someone wants the Job itself on the record, catch it on the next schema-bumping release.Not closed by this, and still worth having
3.0.0-rc8.ci.5068forportal.image/migration.image. This issue verified they are inert (helm-release.ymlapplies--setafter both-ffiles), and that is still true — but a committed file that misstates the running image is a trap for the next reader. Worth refreshing separately./data/dumpsduring the wedge are presumably still there. Worth a sweep.Closing as resolved.