Repository navigation
[3.0][Testing] Check that an upgrade survives being run twice and being interrupted - #9622
Merged
Sesquipedalian merged 2 commits intoSep 5, 2026
Conversation
The upgrader has to cope with two things that happen to it constantly and that nothing tests: being run more than once, and being cut off half way. Running twice is not the exotic case. It is what an admin does when a page times out, and it is what every 3.0 patch upgrade does, since VERSION_MAP keys on an upper bound and '3.0.99' selects the v3_0 migrations for any 3.0.x forum. rerun-upgrade.sh upgrades a 2.1 baseline, upgrades it again, and reports what the second run changed. It should change nothing. Being interrupted is harder. The step, substep and start position live in $_GET and nowhere else; maintenance_tool_progress in Settings.php is the only thing written to disk, it records the version the run started from rather than where it had reached, and it is written by preExit(), which a killed process never runs. So recovery means starting again from the top over a database that is in neither the old shape nor the new one. interrupt-upgrade.sh kills the upgrader at a chosen substep, starts it again, and reports whether the forum it ends up with is the one an uninterrupted upgrade would have built. Kill points are given as a percentage of an uninterrupted run's substeps, so the same numbers mean the same places whatever the baseline is and however many migrations the version grows. --backup turns on the backup step, which the command line otherwise skips: a retry is what endangers what it produces, since backup_table() drops the backup table before writing it. Four readings are taken of each database, each written so that the same database always produces the same bytes: the DDL of every table, the row count of each, the settings table, and the columns of the backup_ tables. Between them they separate DDL that ran when it should not have from rows inserted twice from a setting rewritten in place from a backup replaced by a retry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
Three things were being read as changes when the database had not changed. pg_dump 17 brackets its output with \restrict and \unrestrict lines carrying a key it generates afresh on every run, so two dumps of one untouched database differed in two lines. calendar_updated is written whenever the calendar is looked at, which puts it with the other timestamps the settings reading already drops. grep -c prints its count and then exits 1 when that count is zero, so the `|| echo 0` fallback appended a second line and every numeric test on the result failed. The kill points all came out as zero, every one of them was skipped for being out of range, and the script reported that nothing had gone wrong -- a pass told by an empty loop. The reference log is now required to have substeps in it before any of that can happen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
This was referenced Sep 5, 2026
Sesquipedalian
approved these changes
Sep 5, 2026
This was referenced 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
The upgrader has to cope with two things that happen to it constantly and that nothing tests: being run more than once, and being cut off half way. This adds a check for each, to the Docker development environment, alongside the existing
install-forum.shandreset.sh.rerun-upgrade.shupgrades a 2.1 baseline, upgrades it again, and reports what the second run changed. It should change nothing. Running the upgrader twice is not the exotic case: it is what an admin does when a page times out, and it is what every 3.0 patch upgrade does, sinceVERSION_MAPkeys on an upper bound and3.0.99selects the v3_0 migrations for any 3.0.x forum.interrupt-upgrade.shkills the upgrader at a chosen substep, starts it again, and reports whether the forum it ends up with is the one an uninterrupted upgrade would have built.--at Npicks one substep;--points 10,50a set; the default sweeps five points. Kill points are given as a percentage of an uninterrupted run's substeps, so the same numbers mean the same places whatever the baseline is and however many migrations the version grows.Recovery here means starting again from the top, over a database in neither the old shape nor the new one. The step, substep and start position live in
$_GETand nowhere else;maintenance_tool_progressinSettings.phpis the only thing written to disk, it records the version the run started from rather than where it had reached, and it is written bypreExit(), which a killed process never runs. Every migration therefore has to cope with a half-migrated database, which is a stronger requirement than merely being safe to repeat over a finished one.--backupturns on the backup step, which the command line otherwise skips. It is worth turning on precisely because a retry is what endangers what it produces:backup_table()opens withDROP TABLE IF EXISTS, so a second pass replaces a good pre-upgrade copy with whatever the database holds by then.The readings
Four, each written so the same database always produces the same bytes, since anything varying on its own would be reported as a change the upgrade made. Between them they separate the ways this goes wrong:
ALTERthat ran when it should not have, a table left half alteredINSERTwith no guard on it, run twicebackup_tablesTimestamps that move on their own are filtered, as are the
\restrictlines pg_dump 17 brackets its output with, which carry a key regenerated on every run.What it found
Each of these has its own pull request:
HolidaysToEventshas droppedcalendar_holidays, on both enginesnormalize()compares a column property,generated_expression, that exists under neither that name in the schema classes nor in what the database reportsIt also needs #9302, without which a 2.1 → 3.0 upgrade does not complete at all on the current
release-3.0and neither script has anything to measure.Not reachable from the unit suite
These drive
upgrade.phpagainst real MySQL and PostgreSQL databases in the Docker environment.tests/bootstrap.phpprovides no database and no request, so none of this is expressible as a PHPUnit test; it is a pair of scripts a person or CI runs, in the same shape as.docker/ci.sh. Nothing here is part of the shipped forum — it lives in.docker/, which thecheck-smf-index.phpandcheck-smf-license.phpchecks skip.Issues References (Fixes|Related|Closes)