Page MenuHomePhabricator

Simplify $wgConf->get to "just work" without mistakes (automate suffix and tags, siteParamsCallback?)
Open, LowPublic

Description

Background: Parameter replacements

On wiki farms, settings that vary between wikis or that must be retreived cross-wiki are defined via the $wgConf object. Settings are generally exported in LocalSettings.php using extract( $wgConf->getAll( $wgDBname ) ); (see mw:Wiki_farm). Cross-wiki retreival means wiki A retreiving a setting of wiki B, such as when dealing with interwiki (WikiMap, wgServer, wgArticlePath), notifications (Echo, wgSitename), CentralAuth, etc. (Codesearch query)

This is used as follows:

[ $site, $lang ] = $wgConf->siteFromDB( 'enwiki' );
print $wgConf->get( 'wgServer', 'enwiki', $site, [ 'site' => $site, 'lang' => $lang ] );
//> "//en.wikipedia.org"

The last argument, $params, controls variable replacements in setting values. This is used by stable settings such as language codes, domain names, and URLs. The majority of wikis in a wiki farm follow the same structure for these settings. These "replacements" avoid having to maintain hundreds of near-duplicate values, and increases confidence and ease in code review for wmf-config changes. For example, a wiki like "nlwiktionary" the resolves the below values without listing it, or the other 1000 wikis, separately one by one:

# 'nl'
'wgLanguageCode' => [
	'default' => '$lang',
],

# '//nl.wiktionary.org'
'wgServer' => [
	'wikipedia' => '//$lang.wikipedia.org',
	'wikibooks' => '//$lang.wikibooks.org',
	'wikimedia' => '//$lang.wikimedia.org',
	'wikinews' => '//$lang.wikinews.org',
	'wikiquote' => '//$lang.wikiquote.org',
	'wikisource' => '//$lang.wikisource.org',
	'wikiversity' => '//$lang.wikiversity.org',
	'wikivoyage' => '//$lang.wikivoyage.org',
	'wiktionary' => '//$lang.wiktionary.org',
],

# 'Special:UploadWizard?uselang=nl'
'wgUploadNavigationUrl' => [
	'default' => '//commons.wikimedia.org/wiki/Special:UploadWizard?uselang=$lang',
]

For performance reasons, the SiteConfiguration class supports an optional '@replaceableSettings' key in $wgConf (as we do in wmf-config), which avoids overhead of parameter in SiteConfiguration for configuration values that don't contain variables. This saves significant overhead when computing and exporting thousands of configuration variables during the start of every web request (via WmfConfig::getConfigGlobals).

Problem 1: Parameters are a trap

The above example for "wgServer" is why calls to SiteConfiguration->get must first call $wgConf->siteFromDB and construct the right $params array. If this is not passed correctly, then the return value will be subtly wrong. It will seem to work fine in most installs and in local development where parameter replacements are not commonly used, but then silently fail in production by returning an incomplete value. Or worse, it may initially work for one setting (if wmf-config happens to not use variables in that setting, on that wiki), but then break after a refactor the extension to you ask for a different setting, or if you enable the extension on a different wiki, or if wmf-config is edited to use a parameter there (and correctly include it in @replaceableSettings ). All tests will pass, CI will be green, and breakage may still not affect the wiki you're staging on.

This is error-prone. The solution today is the above boilerplate, but this is undocumented and should not be needed.

I suggest we automate this inside SiteConfiguration. This has to be done carefully, so as to not cause a performance regression. This is a hot code path (thousands of calls, early and unconditionally on all requests to all wikis in all entrypoints).

This can be done in SiteConfiguration->mergeParams where we already perform other default computations. The performance-sensitive batch method SiteConfiguration->getAll already ensures this is called only once. Other methods like SiteConfiguration->get are not performance sensitive and already to for each call. We simply move from the caller to inside the method, after which we can remove the boilerplate.

Background: Wiki tags

By default, $wgConf supports two kinds of shared keys. "default", which is self-explanatory, and "site" tags for sharing a setting value across a wiki family with the same dbname suffix (e.g. "wiktionary", "wikiquote", "wiki" => "wikipedia", etc).

A site administrator can define additional tags for any other arbitrary group of wikis. In wmf-config we do this via dblists. This gives us tags such as s1, group0, commonsuploads, etc.

An important caveat with these, is that SiteConfiguration in MediaWiki core has no knowledge of these additional tags today. They must be passed to individual $wgConf->get calls at runtime. This is okay for the extract( getAll() ) call in wmf-config, which is next to the code that needs knowledge of dblists either way. But, it would be akward for callers anywhere else, such as in MediaWiki core or an extension. Those have no way of knowing how to get the appropriate dblists of tags for the current wiki (such logic woudl be WMF-specific), and so the status quo is that nobody else passes these. The way you might pass them is like so. You can try this using mwscript eval.php testwiki in the Beta Cluster.

$wiki = 'arwiktionary';
$tags = [ 'commonsuploads']; // somehow

[ $site, $lang ] = $wgConf->siteFromDB( $wiki );

print $wgConf->get( 'wgUploadNavigationUrl', '$wiki, $site, [ 'site' => $site, 'lang' => $lang ], $tags );
//commons.wikimedia.org/wiki/Special:UploadWizard?uselang=ar

Problem 2: Tags are a trap

During a request for ar.wiktionary.org, wmf-config correctly exports all variables for that wiki, including variable replacements and additional wiki tags. But, when any code separately retreives wgUploadNavigationUrl at runtime for another wiki, the the status quo boilerplate does not work:

[ $site, $lang ] = $wgConf->siteFromDB( 'arwiktionary' );
return $wgConf->get( 'wgUploadNavigationUrl', 'arwiktionary', $site, [ 'site' => $site, 'lang' => $lang ] );
bool(false)

It returns false (source: wmf-config/InitialiseSettings.php instead of the correct URL because neither the caller nor the wgConf object knew that arwiktionary is in the commonsuploads dblist.

'wgUploadNavigationUrl' => [
	// Projects
	'default' => false,
	'commonsuploads' => '//commons.wikimedia.org/wiki/Special:UploadWizard?uselang=$lang',

Background: siteParamsCallback

The SiteConfiguration class was introduced in 2004 (r3670, git). The "tags" feature was added in Feb 2008 (r30628, git). A few months after that, the $wgConf->siteParamsCallback feature was added to address both of these traps. (r39954, r40023, git).

In production, we've don't use $wgConf->siteParamsCallback. (Or we optimized it away before 2011? The initial commit of the wmf-config Git repo does not set it.)

Proposal

Params:

  • Document boilerplate in SiteConfiguration::get docblock.
  • Improve SiteConfiguration->mergeParams to automate siteFromDB, and backport to stable releases.
  • Remove boilerplate siteFromDB from callers everywhere in MW core, MW exts, wmf-config, class docs, and on-wiki docs.

Tags:

  • Introduce a SiteConfiguration property for mapping dbnames to tags, and use it in mergeParams to automatically apply the right tags.
  • (After train) In wmf-config, assign the dblist-index to this new $wgConf property. This removes the need to call WmfConfig::getTagsForWiki when calling $wgConf->getAll.

Alternatively we could solve Tags by adopting $wgConf->siteParamsCallback in production (Limited to only returning tags and no other array keys. We don't want to compute site/lang, because the default logic in SiteConfiguration is great for that, automated based on the $wgConf->suffixes field, which could otherwise be out of sync). Calling $wgConf->siteFromDB for those keys would not work, as that would cause infinite recursion. This might be performant enough given the data is only computed once in the $wgConf->getAll batch method.

For third parties, we'd likely want to keep that function existing either way, so that simple things stay simple, and does not impose a requirement to have a complete array index for tags in memory (i.e. can retreive just what is needed for one wiki, for even larger farms like Fandom, Miraheze, Wikibase Cloud)

Language tag:

  • Determine if the two-letter language code tags are used in wmf-config.
  • Remove from wmf-config if unused, or consider making it work by default in SiteConfiguration.

Event Timeline

Restricted Application added subscribers: hubaishan, Reception123, Aklapper. · View Herald Transcript

The whole interface is a monstrosity that shouldn't exist, IMO. It should just be an internal detail of the (Wikimedia-specific implementation of the) Configuration object, with a factory for fetching configuration objects for other wikis, and some mechanism to expose which configuration settings support being accessed that way. Extensions should not be allowed to access SiteConfiguration directly.

Of course that is complicated by another monstrosity, which is that every extension can in theory have its own configuration object (but some don't), and accessing the same configuration variable through different configuration objects can in theory lead to different results (although most of the time they will just read the same global variable). Probably manageable though, the Configuration objects for the same wiki could just share the same SiteConfiguration object.