Page MenuHomePhabricator

maintenance/update.php complains about locked database when $wgReadOnly is set
Open, Needs TriagePublic

Description

I'm in the process of updating from 1.25 to 1.28, and I find it good measure to set $wgReadOnly before starting the upgrade in order to have consistent backups and exclusive access to the database while maintenance/upgrade.php runs. The Upgrade Manual also advises to do so. And the documentation for $wgReadOnly contains a big red warning that maintenance scripts may ignore this setting :-)

However, the database updater for 1.28 complains in that case, in my opinion unneccessarily:

# php maintenance/update.php
MediaWiki 1.28.0 Updater

Your composer.lock file is up to date with current dependencies!
Going to run database updates for mediawiki_stratum0-mw_
Depending on the size of your database this may take a while!
Abort with control-c in the next five seconds (skip this countdown with --quick) ... 0
Turning off Content Handler DB fields for this part of upgrade.
...have ipb_id field in ipblocks table.
...have ipb_expiry field in ipblocks table.
...already have interwiki table
[…]
...wl_notificationtimestamp is already nullable.
...index times already set on logging table.
...have ipb_range_start field in ipblocks table.
[d78ec32197315f3715770f28] [no req]   DBReadOnlyError from line 837 of /srv/www/mediawiki-1.28.0/includes/libs/rdbms/database/Database.php: Database is read-only: This wiki is currently being upgraded to a newer software version.
Backtrace:
#0 /srv/www/mediawiki-1.28.0/includes/installer/MysqlUpdater.php(823): Database->query(string, string)
#1 [internal function]: MysqlUpdater->doPageRandomUpdate()
#2 /srv/www/mediawiki-1.28.0/includes/installer/DatabaseUpdater.php(472): call_user_func_array(array, array)
#3 /srv/www/mediawiki-1.28.0/includes/installer/DatabaseUpdater.php(433): DatabaseUpdater->runUpdates(array, boolean)
#4 /srv/www/mediawiki-1.28.0/maintenance/update.php(172): DatabaseUpdater->doUpdates(array)
#5 /srv/www/mediawiki-1.28.0/maintenance/doMaintenance.php(111): UpdateMediaWiki->execute()
#6 /srv/www/mediawiki-1.28.0/maintenance/update.php(217): require_once(string)
#7 {main}

Previous updaters ignored the read-only setting, justifiably. I also cannot find anything in the recent Release Notes that this change was made on purpose.

Event Timeline

I've tested this and is still current as of MediaWiki 1.29. This started happening since MediaWiki 1.27.

Have you tied something like this?

$wgReadOnly = ( PHP_SAPI === 'cli' ) ? false : 'This wiki is currently being upgraded to a newer software version.'

$wgReadOnly is enforced at the LB/DB layer in addition to random callers checked wfReadOnly().

Hmmm, may be a good compromise. It will require only to update documentation

Apologies for dredging this up, I just wanted to ask one point of clarification on the code @aaron
provided above:

$wgReadOnly = ( PHP_SAPI === 'cli' ) ? false : 'This wiki is currently being upgraded to a newer software version.'

That'll only enable running update.php from the command line, not using the web updater, correct?

(Context:)

I'm preparing to update the live-upgrades section of Manual:Upgrading to directly present that code as the necessary edit to LocalSettings.php, instead of hiding it behind links to Manual:$wgReadOnly and/or this Phab task. (It's no longer a "workaround for versions since MediaWiki 1.27", since versions older than that aren't supported anyway.)

Currently, after the online-upgrade documentation suggests setting $wgReadOnly, the next item on the list of steps is:

Run the update script or the web updater in the new directory.

But it seems like "or the web updater" needs to be removed from that sentence, correct? Or does the web updater's activity still register as a CLI task?

You could do:

$wgReadOnly = ( PHP_SAPI === 'cli' || is_defined( 'MEDIAWIKI_INSTALL' ) ) ? false : 'This wiki is currently being upgraded to a newer software version.';

Brilliant, thanks! Is there any issue with splitting it up into two lines (actually, two statements on three lines) like this, for readability?

$adminTask = ( PHP_SAPI === 'cli' || is_defined( 'MEDIAWIKI_INSTALL' ) );
$wgReadOnly = $adminTask ? false : 
	'This wiki is currently being upgraded to a newer software version.';

@aaron : Actually, testing locally I'm getting an error in the PHP logs, with is_defined():

[24-Sep-2022 23:46:37 UTC] PHP Fatal error:  Uncaught Error: Call to undefined function is_defined() in /var/www/wiki/LocalSettings.php:190
Stack trace:
#0 /usr/share/mediawiki/includes/Setup.php(204): require_once()
#1 /usr/share/mediawiki/includes/WebStart.php(93): require_once('...')
#2 /usr/share/mediawiki/index.php(44): require('...')
#3 {main}
  thrown in /var/www/wiki/LocalSettings.php on line 190

There's a defined(), but I'm not sure whether that has the same semantics.

EDIT: defined() seems to work as expected, so I'm going to assume that was the intended function to use in checking for MEDIAWIKI_INSTALL.

After I updated the instructions in that FAQ entry to include a version of the code above, including the check to preserve web-updater write access, I realized: The instructions tell the user to run the web updater in their new installation directory, before making it live. Which AFAICT isn't actually possible.

How are you supposed to access the updated version's web interface, when it hasn't been published to the web server yet? It's not as if you can run <new version>/mw-config/index.php from a file:/// URL.

I brought this up on the manual page's talk page as well. I really think the web updater is just not the proper tool for doing this style of low-disruption, online upgrade. People who want to use the web updater instead of update.php will probably just have to accept the tradeoff that it comes with some unavoidable downtime.

@aaron : Actually, testing locally I'm getting an error in the PHP logs, with is_defined():

[24-Sep-2022 23:46:37 UTC] PHP Fatal error:  Uncaught Error: Call to undefined function is_defined() in /var/www/wiki/LocalSettings.php:190
Stack trace:
#0 /usr/share/mediawiki/includes/Setup.php(204): require_once()
#1 /usr/share/mediawiki/includes/WebStart.php(93): require_once('...')
#2 /usr/share/mediawiki/index.php(44): require('...')
#3 {main}
  thrown in /var/www/wiki/LocalSettings.php on line 190

There's a defined(), but I'm not sure whether that has the same semantics.

EDIT: defined() seems to work as expected, so I'm going to assume that was the intended function to use in checking for MEDIAWIKI_INSTALL.

Sorry, I meant to type "defined", not "is_defined".

After I updated the instructions in that FAQ entry to include a version of the code above, including the check to preserve web-updater write access, I realized: The instructions tell the user to run the web updater in their new installation directory, before making it live. Which AFAICT isn't actually possible.

How are you supposed to access the updated version's web interface, when it hasn't been published to the web server yet? It's not as if you can run <new version>/mw-config/index.php from a file:/// URL.

I brought this up on the manual page's talk page as well. I really think the web updater is just not the proper tool for doing this style of low-disruption, online upgrade. People who want to use the web updater instead of update.php will probably just have to accept the tradeoff that it comes with some unavoidable downtime.

Yes, I'm not fond of the web updater and do not use it myself.