Repository navigation
[3.0] Keeps the substep count across the migration batches - #9645
Merged
Sesquipedalian merged 1 commit intoSep 6, 2026
Merged
Sesquipedalian merged 1 commit into
Sesquipedalian merged 1 commit into
Conversation
Running each version's migrations as its own batch, so that smfVersion can be written as each one finishes, gave every batch its own performSubsteps() call. The number those calls walk is not per-batch, though. It lives in the query string, the loop runs while it is below the number of substeps, and nothing sets it back between calls. The v2_1 batch is 81 migrations and 73 table normalizations, and leaves the count at 154. The v3_0 batch is 22 and 72, sets the total to 94, and asks whether 154 is below 94. It is not, so all 94 are passed over without a word, the call reports success because its loop simply ended, and finalize() then writes the new version number over a database that is still shaped like 2.1. Nothing in the result says so, which is the worst part: the forum is broken afterwards in as many ways as there are 3.0 features. Setting the number back between batches would fix the command line and break the browser, where jsonResponse() ends the request and one substep is one request, so that number is the only thing carrying the position between them. Put back to zero at the end of the first batch, the next request re-enters from the top of VERSION_MAP with a number that points into the first batch again, and the two take turns forever. So it stays unique across the step and each batch is told how many substeps ran before it, which also means a batch already finished is skipped by the same arithmetic. The batches are built before any of them runs, since the total is not known until they all are, and that total is what the progress bar and the template's iTotalSubSteps measure against. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
Sesquipedalian
approved these changes
Sep 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Since #9639, a 2.1 → 3.0 upgrade runs none of the 3.0 migrations. It reports success and stamps the database as 3.0 Alpha 4, but the database is still structurally 2.1.
#9639 gave each version namespace its own
performSubsteps()call so thatsmfVersioncould be updated as each batch finished. The substep counter it walks is not per-batch, though. It lives in the query string,performSubsteps()loopswhile (Maintenance::getCurrentSubStep() < Maintenance::$total_substeps), and nothing resets it between calls. So:v2_1normalize()= 154v3_0normalize()= 94Batch 1 leaves the counter at 154. Batch 2 sets
$total_substepsto 94 and loopswhile (154 < 94), which never runs. All 22 v3_0 migrations and all 72 v3_0 table normalizations are skipped in silence,performSubsteps()returns true because the loop simply ended, andfinalize()then writes the new version number over a 2.1 database.Against the 2.1.7 baseline from the 2.1 development environment, on
release-3.0:messages.edit_historymembers.spoofdetector_namecalendar.rrulesmf_calendar_holidays, whichHolidaysToEventsdropssmfVersionafterwardsThe last row is the reason this is worth catching quickly: nothing in the outcome says anything went wrong. A forum in that state then fails at runtime in as many ways as there are 3.0 features, and re-running the upgrader does repair it — by then only one namespace is selected, so the counter starts at 0 and the batch runs — which makes it look intermittent.
Why not simply reset the counter
Resetting it between batches fixes the command line and breaks the browser. There,
jsonResponse()ends the request, so one substep is one request and the counter is the only thing carrying the position across them. A counter reset to 0 at the end of batch 1 means the next request re-entersmigrations()from the top ofVERSION_MAPwith a number that now points back into batch 1, and the two batches alternate forever.So the number has to stay unique across the whole step.
performSubsteps()takes the count of everything that ran before its batch and finds its own place in it:Which means the batches have to be built before any of them runs, since the total is not known until they all are. That total is what
Maintenance::$total_substepsis set to, so the progress bar and the template'siTotalSubStepsmeasure the whole step again rather than whichever batch is in hand.A batch that has already been done is now skipped by the same arithmetic: its
$offset + count()is below the counter, the loop does not run, and it returns true.How this was verified
Against the 2.1.7 baseline from the 2.1 development environment, with #9644 applied so that the upgrade gets far enough to see any of this:
.docker/rerun-upgrade.shfrom [3.0][Testing] Check that an upgrade survives being run twice and being interrupted #9622 — the second upgrade changes nothing, on MySQL.smf_calendar_holidaysgone.iTotalSubStepsreads 248 rather than 154, which is the number the template's JavaScript compares against to decide the step is finished. Under the current code it would stop at 154 believing it was done.The browser could not be driven to the end here for an unrelated reason:
Maintenance::jsonResponse()callsob_end_clean()whether or not a buffer is open, and this environment runs withoutput_buffering = 0, so the notice it emits lands in the response before the JSON header and the JavaScript gets HTML. That is reported separately and reproduces without this change applied.Not reachable from the unit suite
tests/bootstrap.phpprovides no database and no request, and this is a question about how a request-scoped counter behaves across several calls against a live database. Verified by running real upgrades on both engines instead.Issues References (Fixes|Related|Closes)
smfVersionupdate it added is kept.🤖 Generated with Claude Code