Page MenuHomePhabricator

addWiki.php fails with CannotReplaceActiveServiceException
Closed, ResolvedPublic

Description

zabe@mwmaint1002:/srv/mediawiki$ mwscript extensions/WikimediaMaintenance/addWiki.php --wiki=muswiki --noedits --skipclusters main,extstore,echo,growth gpe wikipedia gpewiki gpe.wikipedia.org
Creating database gpewiki for gpe.wikipedia (Ghanaian Pidgin)
Wikimedia\Services\CannotReplaceActiveServiceException from line 278 of /srv/mediawiki/php-1.41.0-wmf.16/vendor/wikimedia/services/src/ServiceContainer.php: Cannot replace an active service: RevisionStore
#0 /srv/mediawiki/php-1.41.0-wmf.16/extensions/WikimediaMaintenance/addWiki.php(178): Wikimedia\Services\ServiceContainer->redefineService('RevisionStore', Object(Closure))
#1 /srv/mediawiki/php-1.41.0-wmf.16/maintenance/includes/MaintenanceRunner.php(681): AddWiki->execute()
#2 /srv/mediawiki/php-1.41.0-wmf.16/maintenance/run.php(51): MediaWiki\Maintenance\MaintenanceRunner->run()
#3 /srv/mediawiki/multiversion/MWScript.php(159): require_once('/srv/mediawiki/...')
#4 {main}
zabe@mwmaint1002:/srv/mediawiki$

Event Timeline

It would be nice to have a debug option in MediaWikiServices where for each service it stores the stack trace of when it was initialized the first time, and prints that on conflict.

It would be nice to have a debug option in MediaWikiServices where for each service it stores the stack trace of when it was initialized the first time, and prints that on conflict.

Good idea, we are doing something similar to detect cyclic dependencies.

The offending code is:

		// T212881: Redefine the RevisionStore service to explicitly use the new DB name.
		// Otherwise, ExternalStoreDB would be instantiated with an implicit database domain,
		// causing it to use the DB name of the wiki the script is running on due to T200471.
		$services->redefineService(
			'RevisionStore',
			static function ( MediaWikiServices $services ) use ( $dbName ) : RevisionStore {
				return $services->getRevisionStoreFactory()->getRevisionStore( $dbName );
			}
		);

Calling redefineService() is generally unsafe. It would be much better to call MediaWikiServices::resetGlobalInstance() and provide it with a configuration that matches the new wiki, in particular with respect to the database name.

The installer and MediaWikiIntegrationTestCase perform this kind of reset. We could get inspiration there.

Working on this is made difficult by the fact that this script is very WMF specific and it'S impossible to test it locally.

Pinging @Ladsgroup.

daniel triaged this task as High priority.Jul 18 2023, 4:29 PM

Bumping to "high" since this is blocking the deployment of wikifunctions which is scheduled for next week.

Change 939350 had a related patch set uploaded (by Daniel Kinzler; author: Daniel Kinzler):

[mediawiki/extensions/WikimediaMaintenance@master] DRAFT: Reset services after creating the database

https://gerrit.wikimedia.org/r/939350

I made a patch that shows how I think this should be approached.
I have no way to safely test this. @Ladsgroup do you have thoughts on how to figure out if this works, without breaking stuff?

I made a patch that shows how I think this should be approached.
I have no way to safely test this. @Ladsgroup do you have thoughts on how to figure out if this works, without breaking stuff?

The general way is to reserve a window, pull this patch onto an mwmaint server, and try to do the next one on the list (so probably T335216?). Which is… not ideal.

I wonder if it's possible to just split out "create the main MediaWiki DB" into a separate script, so the main script can be called with the actual wiki as its --wiki parameter.

The general way is to reserve a window, pull this patch onto an mwmaint server, and try to do the next one on the list (so probably T335216?). Which is… not ideal.

Especially not ideal since this just might screw up the database of an existing wiki (the one the script "runs on")... Probably not in a catastrophic way, but still.

I wonder if it's possible to just split out "create the main MediaWiki DB" into a separate script, so the main script can be called with the actual wiki as its --wiki parameter.

After all the work we did on overhaulting the config system in MW, it would probably be possible to run the entire script with the actual wiki in the --wiki parameter... Though I'm not so sure, given the way we generate config on the fly in production. We are not really benefitting from the SettingsBuilder system yet.

Generally, this script should work much more like the installer does.

I wonder if it's possible to just split out "create the main MediaWiki DB" into a separate script, so the main script can be called with the actual wiki as its --wiki parameter.

I have been asking and basically begging for years to split the addWiki.php script or at least run it multiple times with stages to make more resilient towards issues like this.

Generally, this script should work much more like the installer does.

Theoretically yes but installer works for a small localhost MW installation. This script creates databases, sets the external storage, sets swift container, sets elastic search, ... This is extremely fragile.

Managed to reproduce this in beta:

ladsgroup@deployment-deploy03:~$ mwscript eval.php --wiki=aawiki
> $services = \MediaWiki\MediaWikiServices::getInstance();

> $languageNames = $services->getLanguageNameUtils()->getLanguageNames();

> $lbFactory = $services->getDBLoadBalancerFactory();

> $localLb = $lbFactory->getMainLB();

> $services->redefineService( 'RevisionStore', static function ( $services ) {return $services->getRevisionStoreFactory()->getRevisionStore( 'gpewiki' );}  );

Caught exception Wikimedia\Services\CannotReplaceActiveServiceException: Cannot replace an active service: RevisionStore
#0 /srv/mediawiki-staging/php-master/maintenance/eval.php(126) : eval()'d code(1): Wikimedia\Services\ServiceContainer->redefineService('RevisionStore', Object(Closure))
#1 /srv/mediawiki-staging/php-master/maintenance/eval.php(126): eval()
#2 /srv/mediawiki-staging/php-master/maintenance/includes/MaintenanceRunner.php(681): MWEval->execute()
#3 /srv/mediawiki-staging/php-master/maintenance/run.php(51): MediaWiki\Maintenance\MaintenanceRunner->run()
#4 /srv/mediawiki-staging/multiversion/MWScript.php(159): require_once('/srv/mediawiki-...')
#5 {main}

Let me see what I can do now. Locally it works, which means probably a service in an extension is being naughty.

I see what I can do because:
Macro shits-on-fire:

(I always looked for an excuse to use this macro)

Change 939749 had a related patch set uploaded (by Ladsgroup; author: Amir Sarabadani):

[mediawiki/extensions/Wikibase@master] client: Avoid dynmaically registering hook handlers

https://gerrit.wikimedia.org/r/939749

Loaded the patch and in deploy03 and that seems to make it work

ladsgroup@deployment-deploy03:/srv/mediawiki/php-master$ mwscript eval.php --wiki=aawiki
> $services = \MediaWiki\MediaWikiServices::getInstance();
> $languageNames = $services->getLanguageNameUtils()->getLanguageNames();
> $lbFactory = $services->getDBLoadBalancerFactory();
> $localLb = $lbFactory->getMainLB();
> $services->redefineService( 'RevisionStore', static function ( $services ) {return $services->getRevisionStoreFactory()->getRevisionStore( 'gpewiki' );}  );

Change 939749 merged by jenkins-bot:

[mediawiki/extensions/Wikibase@master] client: Avoid dynamically registering hook handlers

https://gerrit.wikimedia.org/r/939749

Change 939764 had a related patch set uploaded (by Daniel Kinzler; author: Daniel Kinzler):

[mediawiki/core@master] HookContainer: avoid instantiation of handlers when calling register()

https://gerrit.wikimedia.org/r/939764

Change 939764 merged by jenkins-bot:

[mediawiki/core@master] HookContainer: avoid instantiation of handlers when calling register()

https://gerrit.wikimedia.org/r/939764

Change 940918 had a related patch set uploaded (by Zabe; author: Daniel Kinzler):

[mediawiki/core@wmf/1.41.0-wmf.18] HookContainer: avoid instantiation of handlers when calling register()

https://gerrit.wikimedia.org/r/940918

Change 940919 had a related patch set uploaded (by Zabe; author: Amir Sarabadani):

[mediawiki/extensions/Wikibase@wmf/1.41.0-wmf.18] client: Avoid dynamically registering hook handlers

https://gerrit.wikimedia.org/r/940919

Change 940919 merged by jenkins-bot:

[mediawiki/extensions/Wikibase@wmf/1.41.0-wmf.18] client: Avoid dynamically registering hook handlers

https://gerrit.wikimedia.org/r/940919

Change 940918 merged by jenkins-bot:

[mediawiki/core@wmf/1.41.0-wmf.18] HookContainer: avoid instantiation of handlers when calling register()

https://gerrit.wikimedia.org/r/940918

Mentioned in SAL (#wikimedia-operations) [2023-07-24T22:36:21Z] <zabe@deploy1002> Started scap: Backport for [[gerrit:940919|client: Avoid dynamically registering hook handlers (T341102)]], [[gerrit:940918|HookContainer: avoid instantiation of handlers when calling register() (T341102 T340113 T339834)]]

Mentioned in SAL (#wikimedia-operations) [2023-07-24T22:37:46Z] <zabe@deploy1002> zabe: Backport for [[gerrit:940919|client: Avoid dynamically registering hook handlers (T341102)]], [[gerrit:940918|HookContainer: avoid instantiation of handlers when calling register() (T341102 T340113 T339834)]] synced to the testservers mwdebug2002.codfw.wmnet, mwdebug2001.codfw.wmnet, mwdebug1001.eqiad.wmnet, mwdebug1002.eqiad.wmnet, and mw-debug kubernetes deployment (accessible via k8s-experime

Mentioned in SAL (#wikimedia-operations) [2023-07-24T22:46:20Z] <zabe@deploy1002> Finished scap: Backport for [[gerrit:940919|client: Avoid dynamically registering hook handlers (T341102)]], [[gerrit:940918|HookContainer: avoid instantiation of handlers when calling register() (T341102 T340113 T339834)]] (duration: 09m 59s)

zabe@mwmaint1002:~$ mwscript extensions/WikimediaMaintenance/addWiki.php --wiki=mhwiktionary btm wiktionary btmwiktionary btm.wiktionary.org
Creating database btmwiktionary for btm.wiktionary (Batak Mandailing)
Initialising tables
Initialising external storage cluster26...
Initialising external storage cluster27...
Writing main page to Alaman_Utamo
Writing sidebar donate link to MediaWiki:Sitesupport-url
done.
Getting sites.
Got 0 sites.
Inserting sites.
Done.
Making sure mwstore://local-multiwrite/local-public exists...making 'mwstore://local-multiwrite/local-public' public...done.
Making sure mwstore://local-multiwrite/local-thumb exists...making 'mwstore://local-multiwrite/local-thumb' public...done.
Making sure mwstore://local-multiwrite/local-transcoded exists...making 'mwstore://local-multiwrite/local-transcoded' public...done.
Making sure mwstore://local-multiwrite/local-temp exists...making 'mwstore://local-multiwrite/local-temp' private...done.
Making sure mwstore://local-multiwrite/local-deleted exists...making 'mwstore://local-multiwrite/local-deleted' private...done.
Making sure mwstore://local-multiwrite/timeline-render exists...making 'mwstore://local-multiwrite/timeline-render' public...done.
Done. sync the config as in https://wikitech.wikimedia.org/wiki/Add_a_wiki#MediaWiki_configuration
zabe@mwmaint1002:~$