* Introduced a new system for localisation caching. The system is based around fast fetches of individual messages, minimising memory overhead and startup time in the typical case. It handles both core messages (formerly in Language.php) and extension messages (formerly in MessageCache.php). Profiling indicates a significant win for average throughput.
* The serialized message cache, which would have been redundant, has been removed. Similar performance characteristics can be achieved with $wgLocalisationCacheConf['manualRecache'] = true;
* Added a maintenance script rebuildLocalisationCache.php for offline rebuilding of the localisation cache.
* Extension i18n files can now contain any of the variables which can be set in Messages*.php. It is possible, and recommended, to use this feature instead of the hooks for special page aliases and magic words.
* $wgExtensionAliasesFiles, LanguageGetMagic and LanguageGetSpecialPageAliases are retained for backwards compatibility. $wgMessageCache->addMessages() and related functions have been removed. wfLoadExtensionMessages() is a no-op and can continue to be called for b/c.
* Introduced $wgCacheDirectory as a default location for the various local caches that have accumulated. Suggested $IP/cache as a good place for it in the default LocalSettings.php and created this directory with a deny-all .htaccess.
* Patched Exception.php to avoid using the message cache when an exception is thrown from within LocalisationCache, since this tends to fail horribly.
* Removed Language::getLocalisationArray(), Language::loadLocalisation(), Language::load()
* Fixed FileDependency::__sleep()
* In Cdb.php, fixed newlines in debug messages
In MessageCache::get():
* Replaced calls to $wgContLang capitalisation functions with plain PHP functions, reducing the typical case from 99us to 93us. Message cache keys are already documented as being restricted to ASCII.
* Implemented a more efficient way to filter out bogus language codes, reducing the "foo/en" case from 430us to 101us
* Optimised wfRunHooks() in the typical do-nothing case, from ~30us to ~3us. This reduced MessageCache::get() typical case time from 93us to 38us.
* Removed hook MessageNotInMwNs to save an extra 3us per cache hit. Reimplemented the only user (LocalisationUpdate) using the new hook LocalisationCacheRecache.
2009-06-28 07:11:43 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Rebuild the localisation cache. Useful if you disabled automatic updates
|
|
|
|
|
* using $wgLocalisationCacheConf['manualRecache'] = true;
|
|
|
|
|
*
|
|
|
|
|
* Usage:
|
2009-08-13 16:42:44 +00:00
|
|
|
* php rebuildLocalisationCache.php [--force] [--threads=N]
|
* Introduced a new system for localisation caching. The system is based around fast fetches of individual messages, minimising memory overhead and startup time in the typical case. It handles both core messages (formerly in Language.php) and extension messages (formerly in MessageCache.php). Profiling indicates a significant win for average throughput.
* The serialized message cache, which would have been redundant, has been removed. Similar performance characteristics can be achieved with $wgLocalisationCacheConf['manualRecache'] = true;
* Added a maintenance script rebuildLocalisationCache.php for offline rebuilding of the localisation cache.
* Extension i18n files can now contain any of the variables which can be set in Messages*.php. It is possible, and recommended, to use this feature instead of the hooks for special page aliases and magic words.
* $wgExtensionAliasesFiles, LanguageGetMagic and LanguageGetSpecialPageAliases are retained for backwards compatibility. $wgMessageCache->addMessages() and related functions have been removed. wfLoadExtensionMessages() is a no-op and can continue to be called for b/c.
* Introduced $wgCacheDirectory as a default location for the various local caches that have accumulated. Suggested $IP/cache as a good place for it in the default LocalSettings.php and created this directory with a deny-all .htaccess.
* Patched Exception.php to avoid using the message cache when an exception is thrown from within LocalisationCache, since this tends to fail horribly.
* Removed Language::getLocalisationArray(), Language::loadLocalisation(), Language::load()
* Fixed FileDependency::__sleep()
* In Cdb.php, fixed newlines in debug messages
In MessageCache::get():
* Replaced calls to $wgContLang capitalisation functions with plain PHP functions, reducing the typical case from 99us to 93us. Message cache keys are already documented as being restricted to ASCII.
* Implemented a more efficient way to filter out bogus language codes, reducing the "foo/en" case from 430us to 101us
* Optimised wfRunHooks() in the typical do-nothing case, from ~30us to ~3us. This reduced MessageCache::get() typical case time from 93us to 38us.
* Removed hook MessageNotInMwNs to save an extra 3us per cache hit. Reimplemented the only user (LocalisationUpdate) using the new hook LocalisationCacheRecache.
2009-06-28 07:11:43 +00:00
|
|
|
*
|
|
|
|
|
* Use --force to rebuild all files, even the ones that are not out of date.
|
2009-07-31 09:57:59 +00:00
|
|
|
* Use --threads=N to fork more threads.
|
2009-08-02 19:35:17 +00:00
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
|
|
|
*
|
2012-08-09 16:06:18 +00:00
|
|
|
* @file
|
2009-08-02 19:35:17 +00:00
|
|
|
* @ingroup Maintenance
|
* Introduced a new system for localisation caching. The system is based around fast fetches of individual messages, minimising memory overhead and startup time in the typical case. It handles both core messages (formerly in Language.php) and extension messages (formerly in MessageCache.php). Profiling indicates a significant win for average throughput.
* The serialized message cache, which would have been redundant, has been removed. Similar performance characteristics can be achieved with $wgLocalisationCacheConf['manualRecache'] = true;
* Added a maintenance script rebuildLocalisationCache.php for offline rebuilding of the localisation cache.
* Extension i18n files can now contain any of the variables which can be set in Messages*.php. It is possible, and recommended, to use this feature instead of the hooks for special page aliases and magic words.
* $wgExtensionAliasesFiles, LanguageGetMagic and LanguageGetSpecialPageAliases are retained for backwards compatibility. $wgMessageCache->addMessages() and related functions have been removed. wfLoadExtensionMessages() is a no-op and can continue to be called for b/c.
* Introduced $wgCacheDirectory as a default location for the various local caches that have accumulated. Suggested $IP/cache as a good place for it in the default LocalSettings.php and created this directory with a deny-all .htaccess.
* Patched Exception.php to avoid using the message cache when an exception is thrown from within LocalisationCache, since this tends to fail horribly.
* Removed Language::getLocalisationArray(), Language::loadLocalisation(), Language::load()
* Fixed FileDependency::__sleep()
* In Cdb.php, fixed newlines in debug messages
In MessageCache::get():
* Replaced calls to $wgContLang capitalisation functions with plain PHP functions, reducing the typical case from 99us to 93us. Message cache keys are already documented as being restricted to ASCII.
* Implemented a more efficient way to filter out bogus language codes, reducing the "foo/en" case from 430us to 101us
* Optimised wfRunHooks() in the typical do-nothing case, from ~30us to ~3us. This reduced MessageCache::get() typical case time from 93us to 38us.
* Removed hook MessageNotInMwNs to save an extra 3us per cache hit. Reimplemented the only user (LocalisationUpdate) using the new hook LocalisationCacheRecache.
2009-06-28 07:11:43 +00:00
|
|
|
*/
|
|
|
|
|
|
2019-05-01 13:56:41 +00:00
|
|
|
use MediaWiki\Config\ServiceOptions;
|
2021-12-05 20:03:21 +00:00
|
|
|
use MediaWiki\Languages\LanguageNameUtils;
|
2019-05-01 13:56:41 +00:00
|
|
|
use MediaWiki\Logger\LoggerFactory;
|
2022-04-27 15:42:24 +00:00
|
|
|
use MediaWiki\MainConfigNames;
|
2019-05-01 13:56:41 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2022-05-06 09:09:56 +00:00
|
|
|
use MediaWiki\ResourceLoader\MessageBlobStore;
|
2022-01-26 17:46:06 +00:00
|
|
|
use MediaWiki\Settings\SettingsBuilder;
|
2019-05-01 13:56:41 +00:00
|
|
|
|
2013-05-17 00:16:59 +00:00
|
|
|
require_once __DIR__ . '/Maintenance.php';
|
* Introduced a new system for localisation caching. The system is based around fast fetches of individual messages, minimising memory overhead and startup time in the typical case. It handles both core messages (formerly in Language.php) and extension messages (formerly in MessageCache.php). Profiling indicates a significant win for average throughput.
* The serialized message cache, which would have been redundant, has been removed. Similar performance characteristics can be achieved with $wgLocalisationCacheConf['manualRecache'] = true;
* Added a maintenance script rebuildLocalisationCache.php for offline rebuilding of the localisation cache.
* Extension i18n files can now contain any of the variables which can be set in Messages*.php. It is possible, and recommended, to use this feature instead of the hooks for special page aliases and magic words.
* $wgExtensionAliasesFiles, LanguageGetMagic and LanguageGetSpecialPageAliases are retained for backwards compatibility. $wgMessageCache->addMessages() and related functions have been removed. wfLoadExtensionMessages() is a no-op and can continue to be called for b/c.
* Introduced $wgCacheDirectory as a default location for the various local caches that have accumulated. Suggested $IP/cache as a good place for it in the default LocalSettings.php and created this directory with a deny-all .htaccess.
* Patched Exception.php to avoid using the message cache when an exception is thrown from within LocalisationCache, since this tends to fail horribly.
* Removed Language::getLocalisationArray(), Language::loadLocalisation(), Language::load()
* Fixed FileDependency::__sleep()
* In Cdb.php, fixed newlines in debug messages
In MessageCache::get():
* Replaced calls to $wgContLang capitalisation functions with plain PHP functions, reducing the typical case from 99us to 93us. Message cache keys are already documented as being restricted to ASCII.
* Implemented a more efficient way to filter out bogus language codes, reducing the "foo/en" case from 430us to 101us
* Optimised wfRunHooks() in the typical do-nothing case, from ~30us to ~3us. This reduced MessageCache::get() typical case time from 93us to 38us.
* Removed hook MessageNotInMwNs to save an extra 3us per cache hit. Reimplemented the only user (LocalisationUpdate) using the new hook LocalisationCacheRecache.
2009-06-28 07:11:43 +00:00
|
|
|
|
2012-08-09 16:06:18 +00:00
|
|
|
/**
|
|
|
|
|
* Maintenance script to rebuild the localisation cache.
|
|
|
|
|
*
|
|
|
|
|
* @ingroup Maintenance
|
|
|
|
|
*/
|
2009-08-02 19:35:17 +00:00
|
|
|
class RebuildLocalisationCache extends Maintenance {
|
|
|
|
|
public function __construct() {
|
|
|
|
|
parent::__construct();
|
2016-01-30 02:48:47 +00:00
|
|
|
$this->addDescription( 'Rebuild the localisation cache' );
|
2009-08-02 19:35:17 +00:00
|
|
|
$this->addOption( 'force', 'Rebuild all files, even ones not out of date' );
|
|
|
|
|
$this->addOption( 'threads', 'Fork more than one thread', false, true );
|
2012-08-09 16:06:18 +00:00
|
|
|
$this->addOption( 'outdir', 'Override the output directory (normally $wgCacheDirectory)',
|
2012-02-22 02:09:05 +00:00
|
|
|
false, true );
|
2013-04-30 14:50:20 +00:00
|
|
|
$this->addOption( 'lang', 'Only rebuild these languages, comma separated.',
|
|
|
|
|
false, true );
|
2019-12-22 21:36:08 +00:00
|
|
|
$this->addOption(
|
|
|
|
|
'store-class',
|
|
|
|
|
'Override the LC store class (normally $wgLocalisationCacheConf[\'storeClass\'])',
|
|
|
|
|
false,
|
|
|
|
|
true
|
|
|
|
|
);
|
2020-11-24 23:42:38 +00:00
|
|
|
$this->addOption(
|
rebuildLocalisationCache: Add --skip-message-purge and accompanying script
== Adding --skip-message-purge
Follows-up d91c6627a929. Per CR and Phab comments, I don't think we
can (yet) promise a generic "--offline" mode. This seems too easy to
misuse. Both for users of the script, as well as for future development
to the script. It can fall out out of sync with requirements of the
overall system and leaves no natural place to discover what
responsibilities are being deferred and how the operator should fulfill
those duties.
We know LCStoreDB doesn't work offline, but there are likely other
aspects of this that don't yet work offline for some users. It would
require a much more rigorous refactor (and dropping signifant extension
hooks) to make this reliably offline. I'd welcome a standalone script
using only vendor libs, that does nothing other than scan directories
and build JSON/PHP/CDB files, but that's not what it is today.
What we can do is skip the one operation we know requires a live
DB connection, and (my main motivation for this commit) then document
that skipping it, require the user to perform the purge by other means
instead, since the purge itself is not actually optional.
Also, make this not a WMF-specific option in MW core, by committing
the accompanying script needed for this to work. This was previously
in the WikimediaMaintenance extension as "refreshMessageBlobs.php".
== Keeping --offline (as --no-database)
As far as I know, the purge is the only action we ran into that needed
a database connection, and important to note was that it's not optional.
I think for WMF we did not run into any other logic in our configuration
that uses a database, so --skip-message-purge should suffice. However,
I've kept the option for two reasons:
1. To recognise in some way the business need for WMF to use this
script in an offline manner, and thus document that the script
should at least have a way to work offline, even if there may be
site configurations and extensions that make this impossible, from
a core perspective we want to (experimentally) try to support this.
2. There is unimportant setup logic that happens in doMaintenance.php
where the service wiring for LBFactory is activated by default.
This seems avoidable and technical debt, but at least for now
we do need a way to skip that, so this option will continue to
have that effect.
However, I've renamed the option and inverted its promise. This is not
a promise from core *to* the user to offer an offline mode. Rather,
it is a promise *from* the user that they think nothing DB-needy is in
use.
Bug: T268698
Bug: T263872
Change-Id: I7878fdf4a901fb5e75da540293bb9df9fb508c20
2021-04-06 22:17:25 +00:00
|
|
|
'no-database',
|
|
|
|
|
'EXPERIMENTAL: Disable the database backend. Setting this option will result in an error ' .
|
|
|
|
|
'if you have extensions or use site configuration that need the database. This is an ' .
|
|
|
|
|
'experimental feature to allow offline building of the localisation cache. Known limitations:' .
|
|
|
|
|
"\n" .
|
|
|
|
|
'* Incompatible with LCStoreDB, which always requires a database. ' . "\n" .
|
|
|
|
|
'* The message purge may require a database. See --skip-message-purge.'
|
|
|
|
|
);
|
|
|
|
|
// T237148: The Gadget extension (bundled with MediaWiki by default) requires a database`
|
|
|
|
|
// connection to register its modules for MessageBlobStore.
|
|
|
|
|
$this->addOption(
|
|
|
|
|
'skip-message-purge',
|
|
|
|
|
'Skip purging of MessageBlobStore. The purge operation may require a database, depending ' .
|
|
|
|
|
'on the configuration and extensions on this wiki. If skipping the purge now, you need to ' .
|
|
|
|
|
'run purgeMessageBlobStore.php shortly after deployment.'
|
2021-03-02 00:08:42 +00:00
|
|
|
);
|
2022-02-10 22:25:08 +00:00
|
|
|
$this->addOption(
|
|
|
|
|
'no-progress',
|
|
|
|
|
"Don't print a message for each rebuilt language file. Use this instead of " .
|
|
|
|
|
"--quiet to get a brief summary of the operation."
|
|
|
|
|
);
|
2009-08-02 19:35:17 +00:00
|
|
|
}
|
2010-12-04 03:20:14 +00:00
|
|
|
|
2022-01-26 17:46:06 +00:00
|
|
|
public function finalSetup( SettingsBuilder $settingsBuilder = null ) {
|
2021-06-29 11:24:37 +00:00
|
|
|
# This script needs to be run to build the initial l10n cache. But if
|
2022-01-26 17:46:06 +00:00
|
|
|
# LanguageCode is not 'en', it won't be able to run because there is
|
|
|
|
|
# no l10n cache. Break the cycle by forcing the LanguageCode setting to 'en'.
|
2022-04-27 15:42:24 +00:00
|
|
|
$settingsBuilder->putConfigValue( MainConfigNames::LanguageCode, 'en' );
|
2022-01-26 17:46:06 +00:00
|
|
|
parent::finalSetup( $settingsBuilder );
|
2013-01-10 20:32:40 +00:00
|
|
|
}
|
|
|
|
|
|
2009-08-02 19:35:17 +00:00
|
|
|
public function execute() {
|
2019-05-01 13:56:41 +00:00
|
|
|
global $wgLocalisationCacheConf, $wgCacheDirectory;
|
* Introduced a new system for localisation caching. The system is based around fast fetches of individual messages, minimising memory overhead and startup time in the typical case. It handles both core messages (formerly in Language.php) and extension messages (formerly in MessageCache.php). Profiling indicates a significant win for average throughput.
* The serialized message cache, which would have been redundant, has been removed. Similar performance characteristics can be achieved with $wgLocalisationCacheConf['manualRecache'] = true;
* Added a maintenance script rebuildLocalisationCache.php for offline rebuilding of the localisation cache.
* Extension i18n files can now contain any of the variables which can be set in Messages*.php. It is possible, and recommended, to use this feature instead of the hooks for special page aliases and magic words.
* $wgExtensionAliasesFiles, LanguageGetMagic and LanguageGetSpecialPageAliases are retained for backwards compatibility. $wgMessageCache->addMessages() and related functions have been removed. wfLoadExtensionMessages() is a no-op and can continue to be called for b/c.
* Introduced $wgCacheDirectory as a default location for the various local caches that have accumulated. Suggested $IP/cache as a good place for it in the default LocalSettings.php and created this directory with a deny-all .htaccess.
* Patched Exception.php to avoid using the message cache when an exception is thrown from within LocalisationCache, since this tends to fail horribly.
* Removed Language::getLocalisationArray(), Language::loadLocalisation(), Language::load()
* Fixed FileDependency::__sleep()
* In Cdb.php, fixed newlines in debug messages
In MessageCache::get():
* Replaced calls to $wgContLang capitalisation functions with plain PHP functions, reducing the typical case from 99us to 93us. Message cache keys are already documented as being restricted to ASCII.
* Implemented a more efficient way to filter out bogus language codes, reducing the "foo/en" case from 430us to 101us
* Optimised wfRunHooks() in the typical do-nothing case, from ~30us to ~3us. This reduced MessageCache::get() typical case time from 93us to 38us.
* Removed hook MessageNotInMwNs to save an extra 3us per cache hit. Reimplemented the only user (LocalisationUpdate) using the new hook LocalisationCacheRecache.
2009-06-28 07:11:43 +00:00
|
|
|
|
2010-05-22 16:50:39 +00:00
|
|
|
$force = $this->hasOption( 'force' );
|
2009-08-02 19:35:17 +00:00
|
|
|
$threads = $this->getOption( 'threads', 1 );
|
2010-05-22 16:50:39 +00:00
|
|
|
if ( $threads < 1 || $threads != intval( $threads ) ) {
|
2009-08-19 00:26:59 +00:00
|
|
|
$this->output( "Invalid thread count specified; running single-threaded.\n" );
|
|
|
|
|
$threads = 1;
|
|
|
|
|
}
|
2010-05-22 16:50:39 +00:00
|
|
|
if ( $threads > 1 && wfIsWindows() ) {
|
2009-08-19 00:26:59 +00:00
|
|
|
$this->output( "Threaded rebuild is not supported on Windows; running single-threaded.\n" );
|
|
|
|
|
$threads = 1;
|
|
|
|
|
}
|
2022-03-03 16:09:10 +00:00
|
|
|
if ( $threads > 1 && ( !extension_loaded( 'sockets' ) || !function_exists( 'pcntl_fork' ) ) ) {
|
|
|
|
|
$this->output( "Threaded rebuild requires ext-pcntl and ext-sockets; running single-threaded.\n" );
|
2009-08-19 00:26:59 +00:00
|
|
|
$threads = 1;
|
|
|
|
|
}
|
2009-07-31 09:57:59 +00:00
|
|
|
|
2009-08-02 19:35:17 +00:00
|
|
|
$conf = $wgLocalisationCacheConf;
|
2020-05-19 22:10:27 +00:00
|
|
|
// Allow fallbacks to create CDB files
|
|
|
|
|
$conf['manualRecache'] = false;
|
2019-05-01 13:56:41 +00:00
|
|
|
$conf['forceRecache'] = $force || !empty( $conf['forceRecache'] );
|
2012-02-22 02:09:05 +00:00
|
|
|
if ( $this->hasOption( 'outdir' ) ) {
|
|
|
|
|
$conf['storeDirectory'] = $this->getOption( 'outdir' );
|
|
|
|
|
}
|
2019-12-22 21:36:08 +00:00
|
|
|
|
|
|
|
|
if ( $this->hasOption( 'store-class' ) ) {
|
|
|
|
|
$conf['storeClass'] = $this->getOption( 'store-class' );
|
|
|
|
|
}
|
2021-03-02 00:08:42 +00:00
|
|
|
|
2019-05-01 13:56:41 +00:00
|
|
|
// XXX Copy-pasted from ServiceWiring.php. Do we need a factory for this one caller?
|
2020-09-16 04:56:44 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
2019-05-01 13:56:41 +00:00
|
|
|
$lc = new LocalisationCacheBulkLoad(
|
|
|
|
|
new ServiceOptions(
|
2019-10-08 18:25:30 +00:00
|
|
|
LocalisationCache::CONSTRUCTOR_OPTIONS,
|
2019-05-01 13:56:41 +00:00
|
|
|
$conf,
|
2020-09-16 04:56:44 +00:00
|
|
|
$services->getMainConfig()
|
2019-05-01 13:56:41 +00:00
|
|
|
),
|
|
|
|
|
LocalisationCache::getStoreFromConf( $conf, $wgCacheDirectory ),
|
|
|
|
|
LoggerFactory::getInstance( 'localisation' ),
|
rebuildLocalisationCache: Add --skip-message-purge and accompanying script
== Adding --skip-message-purge
Follows-up d91c6627a929. Per CR and Phab comments, I don't think we
can (yet) promise a generic "--offline" mode. This seems too easy to
misuse. Both for users of the script, as well as for future development
to the script. It can fall out out of sync with requirements of the
overall system and leaves no natural place to discover what
responsibilities are being deferred and how the operator should fulfill
those duties.
We know LCStoreDB doesn't work offline, but there are likely other
aspects of this that don't yet work offline for some users. It would
require a much more rigorous refactor (and dropping signifant extension
hooks) to make this reliably offline. I'd welcome a standalone script
using only vendor libs, that does nothing other than scan directories
and build JSON/PHP/CDB files, but that's not what it is today.
What we can do is skip the one operation we know requires a live
DB connection, and (my main motivation for this commit) then document
that skipping it, require the user to perform the purge by other means
instead, since the purge itself is not actually optional.
Also, make this not a WMF-specific option in MW core, by committing
the accompanying script needed for this to work. This was previously
in the WikimediaMaintenance extension as "refreshMessageBlobs.php".
== Keeping --offline (as --no-database)
As far as I know, the purge is the only action we ran into that needed
a database connection, and important to note was that it's not optional.
I think for WMF we did not run into any other logic in our configuration
that uses a database, so --skip-message-purge should suffice. However,
I've kept the option for two reasons:
1. To recognise in some way the business need for WMF to use this
script in an offline manner, and thus document that the script
should at least have a way to work offline, even if there may be
site configurations and extensions that make this impossible, from
a core perspective we want to (experimentally) try to support this.
2. There is unimportant setup logic that happens in doMaintenance.php
where the service wiring for LBFactory is activated by default.
This seems avoidable and technical debt, but at least for now
we do need a way to skip that, so this option will continue to
have that effect.
However, I've renamed the option and inverted its promise. This is not
a promise from core *to* the user to offer an offline mode. Rather,
it is a promise *from* the user that they think nothing DB-needy is in
use.
Bug: T268698
Bug: T263872
Change-Id: I7878fdf4a901fb5e75da540293bb9df9fb508c20
2021-04-06 22:17:25 +00:00
|
|
|
$this->hasOption( 'skip-message-purge' ) ? [] :
|
2021-02-10 22:31:02 +00:00
|
|
|
[ static function () use ( $services ) {
|
2020-11-24 23:42:38 +00:00
|
|
|
MessageBlobStore::clearGlobalCacheEntry( $services->getMainWANObjectCache() );
|
|
|
|
|
} ],
|
2020-09-16 04:56:44 +00:00
|
|
|
$services->getLanguageNameUtils(),
|
|
|
|
|
$services->getHookContainer()
|
2019-05-01 13:56:41 +00:00
|
|
|
);
|
2009-07-31 09:57:59 +00:00
|
|
|
|
2020-09-16 04:56:44 +00:00
|
|
|
$allCodes = array_keys( $services
|
2020-01-03 23:03:14 +00:00
|
|
|
->getLanguageNameUtils()
|
2021-12-05 20:03:21 +00:00
|
|
|
->getLanguageNames( LanguageNameUtils::AUTONYMS, LanguageNameUtils::SUPPORTED ) );
|
2013-05-09 17:48:10 +00:00
|
|
|
if ( $this->hasOption( 'lang' ) ) {
|
2013-04-30 14:50:20 +00:00
|
|
|
# Validate requested languages
|
|
|
|
|
$codes = array_intersect( $allCodes,
|
|
|
|
|
explode( ',', $this->getOption( 'lang' ) ) );
|
|
|
|
|
# Bailed out if nothing is left
|
2013-05-09 17:48:10 +00:00
|
|
|
if ( count( $codes ) == 0 ) {
|
2017-11-20 00:36:54 +00:00
|
|
|
$this->fatalError( 'None of the languages specified exists.' );
|
2013-04-30 14:50:20 +00:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
# By default get all languages
|
|
|
|
|
$codes = $allCodes;
|
|
|
|
|
}
|
2009-08-02 19:35:17 +00:00
|
|
|
sort( $codes );
|
2009-07-31 09:57:59 +00:00
|
|
|
|
2009-08-02 19:35:17 +00:00
|
|
|
$numRebuilt = 0;
|
2010-05-22 16:50:39 +00:00
|
|
|
$total = count( $codes );
|
2015-03-16 22:39:06 +00:00
|
|
|
$parentStatus = 0;
|
2022-02-11 17:05:18 +00:00
|
|
|
|
|
|
|
|
if ( $threads <= 1 ) {
|
|
|
|
|
// Single-threaded implementation
|
|
|
|
|
$numRebuilt += $this->doRebuild( $codes, $lc, $force );
|
|
|
|
|
} else {
|
|
|
|
|
// Multi-threaded implementation
|
|
|
|
|
$chunks = array_chunk( $codes, ceil( count( $codes ) / $threads ) );
|
2022-02-16 12:51:42 +00:00
|
|
|
// Map from PID to readable socket
|
|
|
|
|
$sockets = [];
|
2022-02-11 17:05:18 +00:00
|
|
|
|
|
|
|
|
foreach ( $chunks as $codes ) {
|
|
|
|
|
$socketpair = [];
|
|
|
|
|
// Create a pair of sockets so that the child can communicate
|
|
|
|
|
// the number of rebuilt langs to the parent.
|
|
|
|
|
if ( socket_create_pair( AF_UNIX, SOCK_STREAM, 0, $socketpair ) === false ) {
|
|
|
|
|
$this->fatalError( 'socket_create_pair failed' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$pid = pcntl_fork();
|
|
|
|
|
|
|
|
|
|
if ( $pid === -1 ) {
|
|
|
|
|
$this->fatalError( ' pcntl_fork failed' );
|
|
|
|
|
} elseif ( $pid === 0 ) {
|
|
|
|
|
// Child, reseed because there is no bug in PHP:
|
|
|
|
|
// https://bugs.php.net/bug.php?id=42465
|
|
|
|
|
mt_srand( getmypid() );
|
|
|
|
|
|
|
|
|
|
$numRebuilt = $this->doRebuild( $codes, $lc, $force );
|
|
|
|
|
// Report the number of rebuilt langs to the parent.
|
|
|
|
|
$msg = strval( $numRebuilt ) . "\n";
|
|
|
|
|
socket_write( $socketpair[1], $msg, strlen( $msg ) );
|
|
|
|
|
// Child exits.
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
// Main thread
|
|
|
|
|
$sockets[$pid] = $socketpair[0];
|
|
|
|
|
}
|
2009-08-02 19:35:17 +00:00
|
|
|
}
|
2022-02-11 17:05:18 +00:00
|
|
|
|
|
|
|
|
// Wait for all children
|
2022-02-16 12:51:42 +00:00
|
|
|
foreach ( $sockets as $pid => $socket ) {
|
2022-02-11 17:05:18 +00:00
|
|
|
$status = 0;
|
|
|
|
|
pcntl_waitpid( $pid, $status );
|
|
|
|
|
|
|
|
|
|
if ( pcntl_wifexited( $status ) ) {
|
|
|
|
|
$code = pcntl_wexitstatus( $status );
|
|
|
|
|
if ( $code ) {
|
|
|
|
|
$this->output( "Pid $pid exited with status $code !!\n" );
|
|
|
|
|
} else {
|
|
|
|
|
// Good exit status from child. Read the number of rebuilt langs from it.
|
2022-02-16 12:51:42 +00:00
|
|
|
$res = socket_read( $socket, 512, PHP_NORMAL_READ );
|
2022-02-11 17:05:18 +00:00
|
|
|
if ( $res === false ) {
|
|
|
|
|
$this->output( "socket_read failed in parent\n" );
|
|
|
|
|
} else {
|
|
|
|
|
$numRebuilt += intval( $res );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mush all child statuses into a single value in the parent.
|
|
|
|
|
$parentStatus |= $code;
|
|
|
|
|
} elseif ( pcntl_wifsignaled( $status ) ) {
|
|
|
|
|
$signum = pcntl_wtermsig( $status );
|
|
|
|
|
$this->output( "Pid $pid terminated by signal $signum !!\n" );
|
|
|
|
|
$parentStatus |= 1;
|
2021-02-10 23:18:58 +00:00
|
|
|
}
|
2015-03-16 22:39:06 +00:00
|
|
|
}
|
2009-08-13 16:42:44 +00:00
|
|
|
}
|
* Introduced a new system for localisation caching. The system is based around fast fetches of individual messages, minimising memory overhead and startup time in the typical case. It handles both core messages (formerly in Language.php) and extension messages (formerly in MessageCache.php). Profiling indicates a significant win for average throughput.
* The serialized message cache, which would have been redundant, has been removed. Similar performance characteristics can be achieved with $wgLocalisationCacheConf['manualRecache'] = true;
* Added a maintenance script rebuildLocalisationCache.php for offline rebuilding of the localisation cache.
* Extension i18n files can now contain any of the variables which can be set in Messages*.php. It is possible, and recommended, to use this feature instead of the hooks for special page aliases and magic words.
* $wgExtensionAliasesFiles, LanguageGetMagic and LanguageGetSpecialPageAliases are retained for backwards compatibility. $wgMessageCache->addMessages() and related functions have been removed. wfLoadExtensionMessages() is a no-op and can continue to be called for b/c.
* Introduced $wgCacheDirectory as a default location for the various local caches that have accumulated. Suggested $IP/cache as a good place for it in the default LocalSettings.php and created this directory with a deny-all .htaccess.
* Patched Exception.php to avoid using the message cache when an exception is thrown from within LocalisationCache, since this tends to fail horribly.
* Removed Language::getLocalisationArray(), Language::loadLocalisation(), Language::load()
* Fixed FileDependency::__sleep()
* In Cdb.php, fixed newlines in debug messages
In MessageCache::get():
* Replaced calls to $wgContLang capitalisation functions with plain PHP functions, reducing the typical case from 99us to 93us. Message cache keys are already documented as being restricted to ASCII.
* Implemented a more efficient way to filter out bogus language codes, reducing the "foo/en" case from 430us to 101us
* Optimised wfRunHooks() in the typical do-nothing case, from ~30us to ~3us. This reduced MessageCache::get() typical case time from 93us to 38us.
* Removed hook MessageNotInMwNs to save an extra 3us per cache hit. Reimplemented the only user (LocalisationUpdate) using the new hook LocalisationCacheRecache.
2009-06-28 07:11:43 +00:00
|
|
|
|
2022-02-11 17:05:18 +00:00
|
|
|
$this->output( "$numRebuilt languages rebuilt out of $total\n" );
|
|
|
|
|
if ( $numRebuilt === 0 ) {
|
|
|
|
|
$this->output( "Use --force to rebuild the caches which are still fresh.\n" );
|
2015-03-16 22:39:06 +00:00
|
|
|
}
|
|
|
|
|
if ( $parentStatus ) {
|
2021-08-30 18:54:47 +00:00
|
|
|
$this->fatalError( 'Failed.', $parentStatus );
|
2009-07-31 09:57:59 +00:00
|
|
|
}
|
|
|
|
|
}
|
2009-08-02 19:56:24 +00:00
|
|
|
|
|
|
|
|
/**
|
2009-08-13 16:42:44 +00:00
|
|
|
* Helper function to rebuild list of languages codes. Prints the code
|
|
|
|
|
* for each language which is rebuilt.
|
2020-03-17 09:00:22 +00:00
|
|
|
* @param string[] $codes List of language codes to rebuild.
|
|
|
|
|
* @param LocalisationCache $lc
|
2014-04-17 20:48:32 +00:00
|
|
|
* @param bool $force Rebuild up-to-date languages
|
2011-11-01 07:34:56 +00:00
|
|
|
* @return int Number of rebuilt languages
|
2009-08-02 19:56:24 +00:00
|
|
|
*/
|
2009-08-13 16:42:44 +00:00
|
|
|
private function doRebuild( $codes, $lc, $force ) {
|
|
|
|
|
$numRebuilt = 0;
|
2009-08-02 19:56:24 +00:00
|
|
|
foreach ( $codes as $code ) {
|
|
|
|
|
if ( $force || $lc->isExpired( $code ) ) {
|
2022-02-10 22:25:08 +00:00
|
|
|
if ( !$this->hasOption( 'no-progress' ) ) {
|
|
|
|
|
$this->output( "Rebuilding $code...\n" );
|
|
|
|
|
}
|
2009-08-02 19:56:24 +00:00
|
|
|
$lc->recache( $code );
|
|
|
|
|
$numRebuilt++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-04-23 18:09:26 +00:00
|
|
|
|
2009-08-13 16:42:44 +00:00
|
|
|
return $numRebuilt;
|
2009-08-02 19:56:24 +00:00
|
|
|
}
|
2011-11-01 07:34:56 +00:00
|
|
|
|
rebuildLocalisationCache: Add --skip-message-purge and accompanying script
== Adding --skip-message-purge
Follows-up d91c6627a929. Per CR and Phab comments, I don't think we
can (yet) promise a generic "--offline" mode. This seems too easy to
misuse. Both for users of the script, as well as for future development
to the script. It can fall out out of sync with requirements of the
overall system and leaves no natural place to discover what
responsibilities are being deferred and how the operator should fulfill
those duties.
We know LCStoreDB doesn't work offline, but there are likely other
aspects of this that don't yet work offline for some users. It would
require a much more rigorous refactor (and dropping signifant extension
hooks) to make this reliably offline. I'd welcome a standalone script
using only vendor libs, that does nothing other than scan directories
and build JSON/PHP/CDB files, but that's not what it is today.
What we can do is skip the one operation we know requires a live
DB connection, and (my main motivation for this commit) then document
that skipping it, require the user to perform the purge by other means
instead, since the purge itself is not actually optional.
Also, make this not a WMF-specific option in MW core, by committing
the accompanying script needed for this to work. This was previously
in the WikimediaMaintenance extension as "refreshMessageBlobs.php".
== Keeping --offline (as --no-database)
As far as I know, the purge is the only action we ran into that needed
a database connection, and important to note was that it's not optional.
I think for WMF we did not run into any other logic in our configuration
that uses a database, so --skip-message-purge should suffice. However,
I've kept the option for two reasons:
1. To recognise in some way the business need for WMF to use this
script in an offline manner, and thus document that the script
should at least have a way to work offline, even if there may be
site configurations and extensions that make this impossible, from
a core perspective we want to (experimentally) try to support this.
2. There is unimportant setup logic that happens in doMaintenance.php
where the service wiring for LBFactory is activated by default.
This seems avoidable and technical debt, but at least for now
we do need a way to skip that, so this option will continue to
have that effect.
However, I've renamed the option and inverted its promise. This is not
a promise from core *to* the user to offer an offline mode. Rather,
it is a promise *from* the user that they think nothing DB-needy is in
use.
Bug: T268698
Bug: T263872
Change-Id: I7878fdf4a901fb5e75da540293bb9df9fb508c20
2021-04-06 22:17:25 +00:00
|
|
|
/** @inheritDoc */
|
2021-03-02 00:08:42 +00:00
|
|
|
public function getDbType() {
|
rebuildLocalisationCache: Add --skip-message-purge and accompanying script
== Adding --skip-message-purge
Follows-up d91c6627a929. Per CR and Phab comments, I don't think we
can (yet) promise a generic "--offline" mode. This seems too easy to
misuse. Both for users of the script, as well as for future development
to the script. It can fall out out of sync with requirements of the
overall system and leaves no natural place to discover what
responsibilities are being deferred and how the operator should fulfill
those duties.
We know LCStoreDB doesn't work offline, but there are likely other
aspects of this that don't yet work offline for some users. It would
require a much more rigorous refactor (and dropping signifant extension
hooks) to make this reliably offline. I'd welcome a standalone script
using only vendor libs, that does nothing other than scan directories
and build JSON/PHP/CDB files, but that's not what it is today.
What we can do is skip the one operation we know requires a live
DB connection, and (my main motivation for this commit) then document
that skipping it, require the user to perform the purge by other means
instead, since the purge itself is not actually optional.
Also, make this not a WMF-specific option in MW core, by committing
the accompanying script needed for this to work. This was previously
in the WikimediaMaintenance extension as "refreshMessageBlobs.php".
== Keeping --offline (as --no-database)
As far as I know, the purge is the only action we ran into that needed
a database connection, and important to note was that it's not optional.
I think for WMF we did not run into any other logic in our configuration
that uses a database, so --skip-message-purge should suffice. However,
I've kept the option for two reasons:
1. To recognise in some way the business need for WMF to use this
script in an offline manner, and thus document that the script
should at least have a way to work offline, even if there may be
site configurations and extensions that make this impossible, from
a core perspective we want to (experimentally) try to support this.
2. There is unimportant setup logic that happens in doMaintenance.php
where the service wiring for LBFactory is activated by default.
This seems avoidable and technical debt, but at least for now
we do need a way to skip that, so this option will continue to
have that effect.
However, I've renamed the option and inverted its promise. This is not
a promise from core *to* the user to offer an offline mode. Rather,
it is a promise *from* the user that they think nothing DB-needy is in
use.
Bug: T268698
Bug: T263872
Change-Id: I7878fdf4a901fb5e75da540293bb9df9fb508c20
2021-04-06 22:17:25 +00:00
|
|
|
if ( $this->hasOption( 'no-database' ) ) {
|
2021-03-02 00:08:42 +00:00
|
|
|
return Maintenance::DB_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return parent::getDbType();
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-01 07:34:56 +00:00
|
|
|
/**
|
|
|
|
|
* Sets whether a run of this maintenance script has the force parameter set
|
|
|
|
|
*
|
|
|
|
|
* @param bool $forced
|
|
|
|
|
*/
|
|
|
|
|
public function setForce( $forced = true ) {
|
|
|
|
|
$this->mOptions['force'] = $forced;
|
|
|
|
|
}
|
2009-07-31 09:57:59 +00:00
|
|
|
}
|
2009-08-02 19:35:17 +00:00
|
|
|
|
2018-01-13 00:02:09 +00:00
|
|
|
$maintClass = RebuildLocalisationCache::class;
|
2013-05-07 23:00:15 +00:00
|
|
|
require_once RUN_MAINTENANCE_IF_MAIN;
|