Page MenuHomePhabricator

Wikibase circular dependency CI break: LanguageFactory -> NamespaceInfo -> UserNameUtils -> ContentLanguage -> LanguageFactory
Closed, ResolvedPublicBUG REPORT

Description

Breaks CI for all patches in GrowthExperiments at least. Example: https://integration.wikimedia.org/ci/job/quibble-vendor-mysql-php73-noselenium-docker/56960/console

10:26:38 1) Wikibase\Repo\Tests\Hooks\Formatters\EntityLinkFormatterFactoryTest::testGivenSameTypeAndLanguage_getLinkFormatterCachesResult
10:26:38 Wikimedia\Services\RecursiveServiceDependencyException: Recursive service instantiation: Circular dependency when creating service! LanguageFactory -> NamespaceInfo -> UserNameUtils -> ContentLanguage -> LanguageFactory
10:26:38 
10:26:38 /workspace/src/vendor/wikimedia/services/src/ServiceContainer.php:435
10:26:38 /workspace/src/vendor/wikimedia/services/src/ServiceContainer.php:411
10:26:38 /workspace/src/includes/MediaWikiServices.php:299
10:26:38 /workspace/src/includes/MediaWikiServices.php:1117
10:26:38 /workspace/src/includes/ServiceWiring.php:450
10:26:38 /workspace/src/vendor/wikimedia/services/src/ServiceContainer.php:447
10:26:38 /workspace/src/vendor/wikimedia/services/src/ServiceContainer.php:411
10:26:38 /workspace/src/includes/MediaWikiServices.php:299
10:26:38 /workspace/src/includes/MediaWikiServices.php:853
10:26:38 /workspace/src/includes/ServiceWiring.php:2047
10:26:38 /workspace/src/vendor/wikimedia/services/src/ServiceContainer.php:447
10:26:38 /workspace/src/vendor/wikimedia/services/src/ServiceContainer.php:411
10:26:38 /workspace/src/includes/MediaWikiServices.php:299
10:26:38 /workspace/src/includes/MediaWikiServices.php:1922
10:26:38 /workspace/src/includes/session/SessionBackend.php:765
10:26:38 /workspace/src/includes/session/SessionBackend.php:236
10:26:38 /workspace/src/includes/session/Session.php:75
10:26:38 /workspace/src/includes/ServiceWiring.php:1131
10:26:38 /workspace/src/vendor/wikimedia/services/src/ServiceContainer.php:447
10:26:38 /workspace/src/vendor/wikimedia/services/src/ServiceContainer.php:411
10:26:38 /workspace/src/includes/MediaWikiServices.php:299
10:26:38 /workspace/src/includes/MediaWikiServices.php:1320
10:26:38 /workspace/src/includes/ServiceWiring.php:803
10:26:38 /workspace/src/vendor/wikimedia/services/src/ServiceContainer.php:447
10:26:38 /workspace/src/vendor/wikimedia/services/src/ServiceContainer.php:411
10:26:38 /workspace/src/includes/MediaWikiServices.php:299
10:26:38 /workspace/src/includes/MediaWikiServices.php:1117
10:26:38 /workspace/src/includes/language/Language.php:299
10:26:38 /workspace/src/extensions/Wikibase/repo/tests/phpunit/includes/Hooks/Formatters/EntityLinkFormatterFactoryTest.php:59
10:26:38 /workspace/src/tests/phpunit/MediaWikiIntegrationTestCase.php:498

At a glance patches for other extensions seem to be passing so maybe this is somehow specific to GrowthExperiments. It is possible for one extension to interfere with another's tests via hooks, but it's not obvious to be how this would be happening here.

Event Timeline

Restricted Application added a subscriber: Aklapper. · View Herald Transcript
Tgr edited subscribers, added: Urbanecm_WMF; removed: Urbanecm.

Change 824293 had a related patch set uploaded (by Gergő Tisza; author: Gergő Tisza):

[mediawiki/extensions/GrowthExperiments@master] [DNM] CI test

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

Change 824293 abandoned by Gergő Tisza:

[mediawiki/extensions/GrowthExperiments@master] [DNM] CI test

Reason:

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

So it seems EarlyLifeCycleHooks is somehow causing this or contributing to it. Presumably because it adds a UserOptionsLookup dependency to language-related things (more specifically anything that would do message loading during initialization), but then why doesn't UserOptionsLookup appear in the dependency chain? And none of the constructors of the services indicated in the error do anything that would trigger the MessageCache__get hook.

Change 824293 restored by Gergő Tisza:

[mediawiki/extensions/GrowthExperiments@master] [DNM] CI test

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

What a mess. The test calls Language::factory, which needs NamespaceInfo, which tries to initialize a bunch of configuration settings. Somehow something inside new ServiceOptions( NamespaceInfo::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ) triggers session handling (no idea how that would happen, and the stack trace doesn't tell anything about that). When exiting the method, the session destructor is called. The session is dirty (which I think shouldn't happen at this point, but then this is a unit test so who knows how many components behave in unusual ways) and validates the user's name, which requires UserNameUtils, which needs the content language, which would be created by the language factory.

Why does that standard-looking ServiceOptions call trigger session handling, when a pretty much identical call in the LanguageFactory wiring doesn't? And what does any of it have to with the GrowthExperiments extension? Seems mystical to me.

Also, why is this only affecting a single testcase in a single test file? It doesn't seem to be doing anything special, and plenty of other tests call Language::factory without breaking.

Change 824293 abandoned by Gergő Tisza:

[mediawiki/extensions/GrowthExperiments@master] [DNM] CI test

Reason:

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

I guess the nice thing to would be to properly dependency-inject UserNameInfo into SessionBackend, so what dependency loading order we end with does not depend on when session saving happens to be triggered.

kostajh triaged this task as High priority.Aug 22 2022, 8:56 AM
kostajh subscribed.

Blocking patches for GrowthExperiments so upping the priority for our team.

Change 825297 had a related patch set uploaded (by Kosta Harlan; author: Kosta Harlan):

[mediawiki/extensions/Wikibase@master] EntityLinkFormatterFactoryTest: Use mock for language

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

@kostajh shouldn't it even be Unbreak Now? As it essentially blocks your work as I understand it?
Thanks for the Wikibase patch, asking WMDE engineers to have a look today if still feasible. Feel free to merge it by your team if we do not get to it quickly enough. We can always revert and redo it, no reason to block your team.

kostajh raised the priority of this task from High to Unbreak Now!.Aug 22 2022, 3:21 PM

@kostajh shouldn't it even be Unbreak Now? As it essentially blocks your work as I understand it?

Yeah, I think that's right.

Thanks for the Wikibase patch, asking WMDE engineers to have a look today if still feasible. Feel free to merge it by your team if we do not get to it quickly enough. We can always revert and redo it, no reason to block your team.

Thanks! We might merge it and a more comprehensive fix could come later from one of our teams.

Change 825297 merged by jenkins-bot:

[mediawiki/extensions/Wikibase@master] EntityLinkFormatterFactoryTest: Use mock for language

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

Urbanecm_WMF lowered the priority of this task from Unbreak Now! to Needs Triage.Aug 22 2022, 5:38 PM

Bandaid fix was merged, and CI is no longer broken: https://gerrit.wikimedia.org/r/c/mediawiki/extensions/GrowthExperiments/+/819657. Resetting priority.

A new, similar CI break:

18:24:47 1) Wikibase\Repo\Tests\Hooks\Formatters\EntityLinkFormatterFactoryTest::testGivenNotArrayOfCallbacks_throwsException with data set #0 (array(null))
18:24:47 Wikimedia\Services\RecursiveServiceDependencyException: Recursive service instantiation: Circular dependency when creating service! JobQueueGroupFactory -> MainWANObjectCache -> UserNameUtils -> TitleParser -> _MediaWikiTitleCodec -> InterwikiLookup -> MainWANObjectCache

The dependency circle is different but it is again caused by Session::__destruct() being invoked at some random place (here, apparently, which makes even less sense than the previous instance).

A new, similar CI break:

18:24:47 1) Wikibase\Repo\Tests\Hooks\Formatters\EntityLinkFormatterFactoryTest::testGivenNotArrayOfCallbacks_throwsException with data set #0 (array(null))
18:24:47 Wikimedia\Services\RecursiveServiceDependencyException: Recursive service instantiation: Circular dependency when creating service! JobQueueGroupFactory -> MainWANObjectCache -> UserNameUtils -> TitleParser -> _MediaWikiTitleCodec -> InterwikiLookup -> MainWANObjectCache

The dependency circle is different but it is again caused by Session::__destruct() being invoked at some random place (here, apparently, which makes even less sense than the previous instance).

Attaching CI's output and patch (@Tgr, please correct me if this is incorrect).

Not happening anymore, as far as I know, so removing from our current sprint.

I think we can probably close this task? The specific circular service error doesn’t happen at the moment as far as I’m aware, and while it occasionally (and seemingly almost at random) pops up again, I don’t think having this task open in various backlogs is very useful.

(For anyone else encountering these errors: as far as I can tell, the successful mitigation strategy seems to be to look at related test code and find some “integrated” MediaWiki objects that can be replaced by simpler mocks.)