Merge "Deprecate wfWaitForSlaves() with LBFactory::waitForReplication()"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 20 Jan 2016 00:10:39 +0000 (00:10 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 20 Jan 2016 00:10:39 +0000 (00:10 +0000)
1  2 
autoload.php
includes/GlobalFunctions.php
includes/db/loadbalancer/LBFactory.php

diff --combined autoload.php
@@@ -72,7 -72,6 +72,7 @@@ $wgAutoloadLocalClasses = array
        'ApiQueryAllMessages' => __DIR__ . '/includes/api/ApiQueryAllMessages.php',
        'ApiQueryAllPages' => __DIR__ . '/includes/api/ApiQueryAllPages.php',
        'ApiQueryAllRevisions' => __DIR__ . '/includes/api/ApiQueryAllRevisions.php',
 +      'ApiQueryMyStashedFiles' => __DIR__ . '/includes/api/ApiQueryMyStashedFiles.php',
        'ApiQueryAllUsers' => __DIR__ . '/includes/api/ApiQueryAllUsers.php',
        'ApiQueryBacklinks' => __DIR__ . '/includes/api/ApiQueryBacklinks.php',
        'ApiQueryBacklinksprop' => __DIR__ . '/includes/api/ApiQueryBacklinksprop.php',
        'Article' => __DIR__ . '/includes/page/Article.php',
        'AssembleUploadChunksJob' => __DIR__ . '/includes/jobqueue/jobs/AssembleUploadChunksJob.php',
        'AtomFeed' => __DIR__ . '/includes/Feed.php',
 +      'AtomicSectionUpdate' => __DIR__ . '/includes/deferred/AtomicSectionUpdate.php',
        'AttachLatest' => __DIR__ . '/maintenance/attachLatest.php',
        'AuthPlugin' => __DIR__ . '/includes/AuthPlugin.php',
        'AuthPluginUser' => __DIR__ . '/includes/AuthPlugin.php',
        'BlockListPager' => __DIR__ . '/includes/specials/SpecialBlockList.php',
        'BlockLogFormatter' => __DIR__ . '/includes/logging/BlockLogFormatter.php',
        'BmpHandler' => __DIR__ . '/includes/media/BMP.php',
 +      'BotPassword' => __DIR__ . '/includes/user/BotPassword.php',
        'BrokenRedirectsPage' => __DIR__ . '/includes/specials/SpecialBrokenRedirects.php',
        'BufferingStatsdDataFactory' => __DIR__ . '/includes/libs/BufferingStatsdDataFactory.php',
        'CLIParser' => __DIR__ . '/maintenance/parse.php',
        'DBMasterPos' => __DIR__ . '/includes/db/DatabaseUtility.php',
        'DBQueryError' => __DIR__ . '/includes/db/DatabaseError.php',
        'DBReadOnlyError' => __DIR__ . '/includes/db/DatabaseError.php',
+       'DBReplicationWaitError' => __DIR__ . '/includes/db/loadbalancer/LBFactory.php',
        'DBSiteStore' => __DIR__ . '/includes/site/DBSiteStore.php',
        'DBTransactionError' => __DIR__ . '/includes/db/DatabaseError.php',
        'DBUnexpectedError' => __DIR__ . '/includes/db/DatabaseError.php',
        'DeprecatedGlobal' => __DIR__ . '/includes/DeprecatedGlobal.php',
        'DeprecatedInterfaceFinder' => __DIR__ . '/maintenance/findDeprecated.php',
        'DerivativeContext' => __DIR__ . '/includes/context/DerivativeContext.php',
 -      'DerivativeRequest' => __DIR__ . '/includes/WebRequest.php',
 +      'DerivativeRequest' => __DIR__ . '/includes/DerivativeRequest.php',
        'DerivativeResourceLoaderContext' => __DIR__ . '/includes/resourceloader/DerivativeResourceLoaderContext.php',
        'DescribeFileOp' => __DIR__ . '/includes/filebackend/FileOp.php',
        'Diff' => __DIR__ . '/includes/diff/DairikiDiff.php',
        'DoubleReplacer' => __DIR__ . '/includes/libs/replacers/DoubleReplacer.php',
        'DummyLinker' => __DIR__ . '/includes/Linker.php',
        'DummyTermColorer' => __DIR__ . '/maintenance/term/MWTerm.php',
 -      'Dump7ZipOutput' => __DIR__ . '/includes/Export.php',
 -      'DumpBZip2Output' => __DIR__ . '/includes/Export.php',
 -      'DumpDBZip2Output' => __DIR__ . '/maintenance/backup.inc',
 -      'DumpFileOutput' => __DIR__ . '/includes/Export.php',
 -      'DumpFilter' => __DIR__ . '/includes/Export.php',
 -      'DumpGZipOutput' => __DIR__ . '/includes/Export.php',
 +      'Dump7ZipOutput' => __DIR__ . '/includes/export/Dump7ZipOutput.php',
 +      'DumpBZip2Output' => __DIR__ . '/includes/export/DumpBZip2Output.php',
 +      'DumpBackup' => __DIR__ . '/maintenance/dumpBackup.php',
 +      'DumpDBZip2Output' => __DIR__ . '/includes/export/DumpDBZip2Output.php',
 +      'DumpFileOutput' => __DIR__ . '/includes/export/DumpFileOutput.php',
 +      'DumpFilter' => __DIR__ . '/includes/export/DumpFilter.php',
 +      'DumpGZipOutput' => __DIR__ . '/includes/export/DumpGZipOutput.php',
        'DumpIterator' => __DIR__ . '/maintenance/dumpIterator.php',
 -      'DumpLatestFilter' => __DIR__ . '/includes/Export.php',
 +      'DumpLatestFilter' => __DIR__ . '/includes/export/DumpLatestFilter.php',
        'DumpLinks' => __DIR__ . '/maintenance/dumpLinks.php',
        'DumpMessages' => __DIR__ . '/maintenance/language/dumpMessages.php',
 -      'DumpMultiWriter' => __DIR__ . '/includes/Export.php',
 -      'DumpNamespaceFilter' => __DIR__ . '/includes/Export.php',
 -      'DumpNotalkFilter' => __DIR__ . '/includes/Export.php',
 -      'DumpOutput' => __DIR__ . '/includes/Export.php',
 -      'DumpPipeOutput' => __DIR__ . '/includes/Export.php',
 +      'DumpMultiWriter' => __DIR__ . '/includes/export/DumpMultiWriter.php',
 +      'DumpNamespaceFilter' => __DIR__ . '/includes/export/DumpNamespaceFilter.php',
 +      'DumpNotalkFilter' => __DIR__ . '/includes/export/DumpNotalkFilter.php',
 +      'DumpOutput' => __DIR__ . '/includes/export/DumpOutput.php',
 +      'DumpPipeOutput' => __DIR__ . '/includes/export/DumpPipeOutput.php',
        'DumpRenderer' => __DIR__ . '/maintenance/renderDump.php',
        'DumpRev' => __DIR__ . '/maintenance/storage/dumpRev.php',
        'DuplicateJob' => __DIR__ . '/includes/jobqueue/jobs/DuplicateJob.php',
        'EditWatchlistCheckboxSeriesField' => __DIR__ . '/includes/specials/SpecialEditWatchlist.php',
        'EditWatchlistNormalHTMLForm' => __DIR__ . '/includes/specials/SpecialEditWatchlist.php',
        'EmailConfirmation' => __DIR__ . '/includes/specials/SpecialConfirmemail.php',
 -      'EmailInvalidation' => __DIR__ . '/includes/specials/SpecialConfirmemail.php',
 +      'EmailInvalidation' => __DIR__ . '/includes/specials/SpecialEmailInvalidate.php',
        'EmailNotification' => __DIR__ . '/includes/mail/EmailNotification.php',
        'EmaillingJob' => __DIR__ . '/includes/jobqueue/jobs/EmaillingJob.php',
        'EmptyBagOStuff' => __DIR__ . '/includes/libs/objectcache/EmptyBagOStuff.php',
        'FakeResultWrapper' => __DIR__ . '/includes/db/DatabaseUtility.php',
        'Fallback' => __DIR__ . '/includes/Fallback.php',
        'FatalError' => __DIR__ . '/includes/exception/FatalError.php',
 -      'FauxRequest' => __DIR__ . '/includes/WebRequest.php',
 +      'FauxRequest' => __DIR__ . '/includes/FauxRequest.php',
        'FauxResponse' => __DIR__ . '/includes/WebResponse.php',
        'FeedItem' => __DIR__ . '/includes/Feed.php',
        'FeedUtils' => __DIR__ . '/includes/FeedUtils.php',
        'ImportReporter' => __DIR__ . '/includes/specials/SpecialImport.php',
        'ImportSiteScripts' => __DIR__ . '/maintenance/importSiteScripts.php',
        'ImportSites' => __DIR__ . '/maintenance/importSites.php',
 -      'ImportSource' => __DIR__ . '/includes/Import.php',
 -      'ImportStreamSource' => __DIR__ . '/includes/Import.php',
 -      'ImportStringSource' => __DIR__ . '/includes/Import.php',
 +      'ImportSource' => __DIR__ . '/includes/import/ImportSource.php',
 +      'ImportStreamSource' => __DIR__ . '/includes/import/ImportStreamSource.php',
 +      'ImportStringSource' => __DIR__ . '/includes/import/ImportStringSource.php',
 +      'ImportTextFiles' => __DIR__ . '/maintenance/importTextFiles.php',
        'ImportTitleFactory' => __DIR__ . '/includes/title/ImportTitleFactory.php',
        'IncludableSpecialPage' => __DIR__ . '/includes/specialpage/IncludableSpecialPage.php',
        'IndexPager' => __DIR__ . '/includes/pager/IndexPager.php',
        'JobQueueError' => __DIR__ . '/includes/jobqueue/JobQueue.php',
        'JobQueueFederated' => __DIR__ . '/includes/jobqueue/JobQueueFederated.php',
        'JobQueueGroup' => __DIR__ . '/includes/jobqueue/JobQueueGroup.php',
 +      'JobQueueMemory' => __DIR__ . '/includes/jobqueue/JobQueueMemory.php',
        'JobQueueRedis' => __DIR__ . '/includes/jobqueue/JobQueueRedis.php',
        'JobRunner' => __DIR__ . '/includes/jobqueue/JobRunner.php',
        'JobSpecification' => __DIR__ . '/includes/jobqueue/JobSpecification.php',
        'MWDocGen' => __DIR__ . '/maintenance/mwdocgen.php',
        'MWException' => __DIR__ . '/includes/exception/MWException.php',
        'MWExceptionHandler' => __DIR__ . '/includes/exception/MWExceptionHandler.php',
 +      'MWGrants' => __DIR__ . '/includes/utils/MWGrants.php',
        'MWHttpRequest' => __DIR__ . '/includes/HttpFunctions.php',
        'MWMemcached' => __DIR__ . '/includes/compat/MemcachedClientCompat.php',
        'MWMessagePack' => __DIR__ . '/includes/libs/MWMessagePack.php',
        'MWNamespace' => __DIR__ . '/includes/MWNamespace.php',
        'MWOldPassword' => __DIR__ . '/includes/password/MWOldPassword.php',
 +      'MWRestrictions' => __DIR__ . '/includes/utils/MWRestrictions.php',
        'MWSaltedPassword' => __DIR__ . '/includes/password/MWSaltedPassword.php',
        'MWTidy' => __DIR__ . '/includes/parser/MWTidy.php',
        'MWTimestamp' => __DIR__ . '/includes/MWTimestamp.php',
        'MediaWiki\\Logger\\Monolog\\WikiProcessor' => __DIR__ . '/includes/debug/logger/monolog/WikiProcessor.php',
        'MediaWiki\\Logger\\NullSpi' => __DIR__ . '/includes/debug/logger/NullSpi.php',
        'MediaWiki\\Logger\\Spi' => __DIR__ . '/includes/debug/logger/Spi.php',
 +      'MediaWiki\\Session\\BotPasswordSessionProvider' => __DIR__ . '/includes/session/BotPasswordSessionProvider.php',
 +      'MediaWiki\\Session\\CookieSessionProvider' => __DIR__ . '/includes/session/CookieSessionProvider.php',
 +      'MediaWiki\\Session\\ImmutableSessionProviderWithCookie' => __DIR__ . '/includes/session/ImmutableSessionProviderWithCookie.php',
 +      'MediaWiki\\Session\\PHPSessionHandler' => __DIR__ . '/includes/session/PHPSessionHandler.php',
 +      'MediaWiki\\Session\\Session' => __DIR__ . '/includes/session/Session.php',
 +      'MediaWiki\\Session\\SessionBackend' => __DIR__ . '/includes/session/SessionBackend.php',
 +      'MediaWiki\\Session\\SessionId' => __DIR__ . '/includes/session/SessionId.php',
 +      'MediaWiki\\Session\\SessionInfo' => __DIR__ . '/includes/session/SessionInfo.php',
 +      'MediaWiki\\Session\\SessionManager' => __DIR__ . '/includes/session/SessionManager.php',
 +      'MediaWiki\\Session\\SessionManagerInterface' => __DIR__ . '/includes/session/SessionManagerInterface.php',
 +      'MediaWiki\\Session\\SessionProvider' => __DIR__ . '/includes/session/SessionProvider.php',
 +      'MediaWiki\\Session\\SessionProviderInterface' => __DIR__ . '/includes/session/SessionProviderInterface.php',
 +      'MediaWiki\\Session\\UserInfo' => __DIR__ . '/includes/session/UserInfo.php',
 +      'MediaWiki\\Site\\MediaWikiPageNameNormalizer' => __DIR__ . '/includes/site/MediaWikiPageNameNormalizer.php',
        'MediaWiki\\Tidy\\Html5Depurate' => __DIR__ . '/includes/tidy/Html5Depurate.php',
        'MediaWiki\\Tidy\\RaggettBase' => __DIR__ . '/includes/tidy/RaggettBase.php',
        'MediaWiki\\Tidy\\RaggettExternal' => __DIR__ . '/includes/tidy/RaggettExternal.php',
        'ORAField' => __DIR__ . '/includes/db/DatabaseOracle.php',
        'ORAResult' => __DIR__ . '/includes/db/DatabaseOracle.php',
        'ObjectCache' => __DIR__ . '/includes/objectcache/ObjectCache.php',
 -      'ObjectCacheSessionHandler' => __DIR__ . '/includes/objectcache/ObjectCacheSessionHandler.php',
        'ObjectFactory' => __DIR__ . '/includes/libs/ObjectFactory.php',
        'ObjectFileCache' => __DIR__ . '/includes/cache/ObjectFileCache.php',
        'OldChangesList' => __DIR__ . '/includes/changes/OldChangesList.php',
        'PageExists' => __DIR__ . '/maintenance/pageExists.php',
        'PageLangLogFormatter' => __DIR__ . '/includes/logging/PageLangLogFormatter.php',
        'PageLinkRenderer' => __DIR__ . '/includes/title/PageLinkRenderer.php',
 +      'PageProps' => __DIR__ . '/includes/PageProps.php',
        'PageQueryPage' => __DIR__ . '/includes/specialpage/PageQueryPage.php',
        'Pager' => __DIR__ . '/includes/pager/Pager.php',
        'ParameterizedPassword' => __DIR__ . '/includes/password/ParameterizedPassword.php',
        'SearchHighlighter' => __DIR__ . '/includes/search/SearchHighlighter.php',
        'SearchMssql' => __DIR__ . '/includes/search/SearchMssql.php',
        'SearchMySQL' => __DIR__ . '/includes/search/SearchMySQL.php',
 -      'SearchNearMatchResultSet' => __DIR__ . '/includes/search/SearchResultSet.php',
 +      'SearchNearMatchResultSet' => __DIR__ . '/includes/search/SearchNearMatchResultSet.php',
        'SearchOracle' => __DIR__ . '/includes/search/SearchOracle.php',
        'SearchPostgres' => __DIR__ . '/includes/search/SearchPostgres.php',
        'SearchResult' => __DIR__ . '/includes/search/SearchResult.php',
        'SpecialBlock' => __DIR__ . '/includes/specials/SpecialBlock.php',
        'SpecialBlockList' => __DIR__ . '/includes/specials/SpecialBlockList.php',
        'SpecialBookSources' => __DIR__ . '/includes/specials/SpecialBooksources.php',
 +      'SpecialBotPasswords' => __DIR__ . '/includes/specials/SpecialBotPasswords.php',
        'SpecialCachedPage' => __DIR__ . '/includes/specials/SpecialCachedPage.php',
        'SpecialCategories' => __DIR__ . '/includes/specials/SpecialCategories.php',
        'SpecialChangeContentModel' => __DIR__ . '/includes/specials/SpecialChangeContentModel.php',
        'SpecialListAdmins' => __DIR__ . '/includes/specials/SpecialListusers.php',
        'SpecialListBots' => __DIR__ . '/includes/specials/SpecialListusers.php',
        'SpecialListFiles' => __DIR__ . '/includes/specials/SpecialListfiles.php',
 +      'SpecialListGrants' => __DIR__ . '/includes/specials/SpecialListgrants.php',
        'SpecialListGroupRights' => __DIR__ . '/includes/specials/SpecialListgrouprights.php',
        'SpecialListUsers' => __DIR__ . '/includes/specials/SpecialListusers.php',
        'SpecialLockdb' => __DIR__ . '/includes/specials/SpecialLockdb.php',
        'SpecialProtectedtitles' => __DIR__ . '/includes/specials/SpecialProtectedtitles.php',
        'SpecialRandomInCategory' => __DIR__ . '/includes/specials/SpecialRandomInCategory.php',
        'SpecialRandomredirect' => __DIR__ . '/includes/specials/SpecialRandomredirect.php',
 +      'SpecialRandomrootpage' => __DIR__ . '/includes/specials/SpecialRandomrootpage.php',
        'SpecialRecentChanges' => __DIR__ . '/includes/specials/SpecialRecentchanges.php',
        'SpecialRecentChangesLinked' => __DIR__ . '/includes/specials/SpecialRecentchangeslinked.php',
        'SpecialRedirect' => __DIR__ . '/includes/specials/SpecialRedirect.php',
        'SpecialWhatLinksHere' => __DIR__ . '/includes/specials/SpecialWhatlinkshere.php',
        'SqlBagOStuff' => __DIR__ . '/includes/objectcache/SqlBagOStuff.php',
        'SqlDataUpdate' => __DIR__ . '/includes/deferred/SqlDataUpdate.php',
 -      'SqlSearchResultSet' => __DIR__ . '/includes/search/SearchResultSet.php',
 +      'SqlSearchResultSet' => __DIR__ . '/includes/search/SqlSearchResultSet.php',
        'Sqlite' => __DIR__ . '/maintenance/sqlite.inc',
        'SqliteInstaller' => __DIR__ . '/includes/installer/SqliteInstaller.php',
        'SqliteMaintenance' => __DIR__ . '/maintenance/sqlite.php',
        'TestFileOpPerformance' => __DIR__ . '/maintenance/fileOpPerfTest.php',
        'TextContent' => __DIR__ . '/includes/content/TextContent.php',
        'TextContentHandler' => __DIR__ . '/includes/content/TextContentHandler.php',
 -      'TextPassDumper' => __DIR__ . '/maintenance/backupTextPass.inc',
 +      'TextPassDumper' => __DIR__ . '/maintenance/dumpTextPass.php',
        'TextStatsOutput' => __DIR__ . '/maintenance/language/StatOutputs.php',
        'TgConverter' => __DIR__ . '/languages/classes/LanguageTg.php',
        'ThrottledError' => __DIR__ . '/includes/exception/ThrottledError.php',
        'UploadFromUrl' => __DIR__ . '/includes/upload/UploadFromUrl.php',
        'UploadFromUrlJob' => __DIR__ . '/includes/jobqueue/jobs/UploadFromUrlJob.php',
        'UploadLogFormatter' => __DIR__ . '/includes/logging/UploadLogFormatter.php',
 -      'UploadSourceAdapter' => __DIR__ . '/includes/Import.php',
 +      'UploadSourceAdapter' => __DIR__ . '/includes/import/UploadSourceAdapter.php',
        'UploadSourceField' => __DIR__ . '/includes/specials/SpecialUpload.php',
        'UploadStash' => __DIR__ . '/includes/upload/UploadStash.php',
        'UploadStashBadPathException' => __DIR__ . '/includes/upload/UploadStash.php',
        'UserCache' => __DIR__ . '/includes/cache/UserCache.php',
        'UserDupes' => __DIR__ . '/maintenance/userDupes.inc',
        'UserMailer' => __DIR__ . '/includes/mail/UserMailer.php',
 +      'UserNamePrefixSearch' => __DIR__ . '/includes/user/UserNamePrefixSearch.php',
        'UserNotLoggedIn' => __DIR__ . '/includes/exception/UserNotLoggedIn.php',
        'UserOptions' => __DIR__ . '/maintenance/userOptions.inc',
        'UserPasswordPolicy' => __DIR__ . '/includes/password/UserPasswordPolicy.php',
        'VirtualRESTService' => __DIR__ . '/includes/libs/virtualrest/VirtualRESTService.php',
        'VirtualRESTServiceClient' => __DIR__ . '/includes/libs/virtualrest/VirtualRESTServiceClient.php',
        'WANObjectCache' => __DIR__ . '/includes/libs/objectcache/WANObjectCache.php',
 -      'WaitForSlave' => __DIR__ . '/maintenance/waitForSlave.php',
        'WantedCategoriesPage' => __DIR__ . '/includes/specials/SpecialWantedcategories.php',
        'WantedFilesPage' => __DIR__ . '/includes/specials/SpecialWantedfiles.php',
        'WantedPagesPage' => __DIR__ . '/includes/specials/SpecialWantedpages.php',
        'WatchedItem' => __DIR__ . '/includes/WatchedItem.php',
        'WatchlistCleanup' => __DIR__ . '/maintenance/cleanupWatchlist.php',
        'WebInstaller' => __DIR__ . '/includes/installer/WebInstaller.php',
 -      'WebInstallerComplete' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 -      'WebInstallerCopying' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 -      'WebInstallerDBConnect' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 -      'WebInstallerDBSettings' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 -      'WebInstallerDocument' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 -      'WebInstallerExistingWiki' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 -      'WebInstallerInstall' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 -      'WebInstallerLanguage' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 -      'WebInstallerName' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 -      'WebInstallerOptions' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 +      'WebInstallerComplete' => __DIR__ . '/includes/installer/WebInstallerComplete.php',
 +      'WebInstallerCopying' => __DIR__ . '/includes/installer/WebInstallerCopying.php',
 +      'WebInstallerDBConnect' => __DIR__ . '/includes/installer/WebInstallerDBConnect.php',
 +      'WebInstallerDBSettings' => __DIR__ . '/includes/installer/WebInstallerDBSettings.php',
 +      'WebInstallerDocument' => __DIR__ . '/includes/installer/WebInstallerDocument.php',
 +      'WebInstallerExistingWiki' => __DIR__ . '/includes/installer/WebInstallerExistingWiki.php',
 +      'WebInstallerInstall' => __DIR__ . '/includes/installer/WebInstallerInstall.php',
 +      'WebInstallerLanguage' => __DIR__ . '/includes/installer/WebInstallerLanguage.php',
 +      'WebInstallerName' => __DIR__ . '/includes/installer/WebInstallerName.php',
 +      'WebInstallerOptions' => __DIR__ . '/includes/installer/WebInstallerOptions.php',
        'WebInstallerOutput' => __DIR__ . '/includes/installer/WebInstallerOutput.php',
        'WebInstallerPage' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 -      'WebInstallerReadme' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 -      'WebInstallerReleaseNotes' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 -      'WebInstallerRestart' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 -      'WebInstallerUpgrade' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 -      'WebInstallerUpgradeDoc' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 -      'WebInstallerWelcome' => __DIR__ . '/includes/installer/WebInstallerPage.php',
 +      'WebInstallerReadme' => __DIR__ . '/includes/installer/WebInstallerReadme.php',
 +      'WebInstallerReleaseNotes' => __DIR__ . '/includes/installer/WebInstallerReleaseNotes.php',
 +      'WebInstallerRestart' => __DIR__ . '/includes/installer/WebInstallerRestart.php',
 +      'WebInstallerUpgrade' => __DIR__ . '/includes/installer/WebInstallerUpgrade.php',
 +      'WebInstallerUpgradeDoc' => __DIR__ . '/includes/installer/WebInstallerUpgradeDoc.php',
 +      'WebInstallerWelcome' => __DIR__ . '/includes/installer/WebInstallerWelcome.php',
        'WebPHandler' => __DIR__ . '/includes/media/WebP.php',
        'WebRequest' => __DIR__ . '/includes/WebRequest.php',
        'WebRequestUpload' => __DIR__ . '/includes/WebRequestUpload.php',
        'WebResponse' => __DIR__ . '/includes/WebResponse.php',
        'WikiCategoryPage' => __DIR__ . '/includes/page/WikiCategoryPage.php',
        'WikiDiff3' => __DIR__ . '/includes/diff/WikiDiff3.php',
 -      'WikiExporter' => __DIR__ . '/includes/Export.php',
 +      'WikiExporter' => __DIR__ . '/includes/export/WikiExporter.php',
        'WikiFilePage' => __DIR__ . '/includes/page/WikiFilePage.php',
 -      'WikiImporter' => __DIR__ . '/includes/Import.php',
 +      'WikiImporter' => __DIR__ . '/includes/import/WikiImporter.php',
        'WikiMap' => __DIR__ . '/includes/WikiMap.php',
        'WikiPage' => __DIR__ . '/includes/page/WikiPage.php',
        'WikiReference' => __DIR__ . '/includes/WikiMap.php',
 -      'WikiRevision' => __DIR__ . '/includes/Import.php',
 +      'WikiRevision' => __DIR__ . '/includes/import/WikiRevision.php',
        'WikiStatsOutput' => __DIR__ . '/maintenance/language/StatOutputs.php',
        'WikitextContent' => __DIR__ . '/includes/content/WikitextContent.php',
        'WikitextContentHandler' => __DIR__ . '/includes/content/WikitextContentHandler.php',
        'XMPValidate' => __DIR__ . '/includes/media/XMPValidate.php',
        'Xhprof' => __DIR__ . '/includes/libs/Xhprof.php',
        'Xml' => __DIR__ . '/includes/Xml.php',
 -      'XmlDumpWriter' => __DIR__ . '/includes/Export.php',
 +      'XmlDumpWriter' => __DIR__ . '/includes/export/XmlDumpWriter.php',
        'XmlJsCode' => __DIR__ . '/includes/Xml.php',
        'XmlSelect' => __DIR__ . '/includes/XmlSelect.php',
        'XmlTypeCheck' => __DIR__ . '/includes/libs/XmlTypeCheck.php',
@@@ -26,7 -26,6 +26,7 @@@ if ( !defined( 'MEDIAWIKI' ) ) 
  
  use Liuggio\StatsdClient\Sender\SocketSender;
  use MediaWiki\Logger\LoggerFactory;
 +use MediaWiki\Session\SessionManager;
  
  // Hide compatibility functions from Doxygen
  /// @cond
@@@ -1039,12 -1038,7 +1039,12 @@@ function wfMatchesDomainList( $url, $do
   * @since 1.25 support for additional context data
   *
   * @param string $text
 - * @param string|bool $dest Unused
 + * @param string|bool $dest Destination of the message:
 + *     - 'all': both to the log and HTML (debug toolbar or HTML comments)
 + *     - 'private': excluded from HTML output
 + *   For backward compatibility, it can also take a boolean:
 + *     - true: same as 'all'
 + *     - false: same as 'private'
   * @param array $context Additional logging context data
   */
  function wfDebug( $text, $dest = 'all', array $context = array() ) {
        if ( $wgDebugLogPrefix !== '' ) {
                $context['prefix'] = $wgDebugLogPrefix;
        }
 +      $context['private'] = ( $dest === false || $dest === 'private' );
  
        $logger = LoggerFactory::getInstance( 'wfDebug' );
        $logger->debug( $text, $context );
@@@ -1133,6 -1126,7 +1133,6 @@@ function wfDebugMem( $exact = false ) 
   * @param string $text
   * @param string|bool $dest Destination of the message:
   *     - 'all': both to the log and HTML (debug toolbar or HTML comments)
 - *     - 'log': only to the log and not in HTML
   *     - 'private': only to the specific log if set in $wgDebugLogGroups and
   *       discarded otherwise
   *   For backward compatibility, it can also take a boolean:
  function wfDebugLog(
        $logGroup, $text, $dest = 'all', array $context = array()
  ) {
 -      // Turn $dest into a string if it's a boolean (for b/c)
 -      if ( $dest === true ) {
 -              $dest = 'all';
 -      } elseif ( $dest === false ) {
 -              $dest = 'private';
 -      }
 -
        $text = trim( $text );
  
        $logger = LoggerFactory::getInstance( $logGroup );
 -      $context['private'] = ( $dest === 'private' );
 +      $context['private'] = ( $dest === false || $dest === 'private' );
        $logger->info( $text, $context );
  }
  
@@@ -1461,6 -1462,159 +1461,6 @@@ function wfMessageFallback( /*...*/ ) 
        return call_user_func_array( 'Message::newFallbackSequence', $args );
  }
  
 -/**
 - * Get a message from anywhere, for the current user language.
 - *
 - * Use wfMsgForContent() instead if the message should NOT
 - * change depending on the user preferences.
 - *
 - * @deprecated since 1.18
 - *
 - * @param string $key Lookup key for the message, usually
 - *    defined in languages/Language.php
 - *
 - * Parameters to the message, which can be used to insert variable text into
 - * it, can be passed to this function in the following formats:
 - * - One per argument, starting at the second parameter
 - * - As an array in the second parameter
 - * These are not shown in the function definition.
 - *
 - * @return string
 - */
 -function wfMsg( $key ) {
 -      wfDeprecated( __METHOD__, '1.21' );
 -
 -      $args = func_get_args();
 -      array_shift( $args );
 -      return wfMsgReal( $key, $args );
 -}
 -
 -/**
 - * Same as above except doesn't transform the message
 - *
 - * @deprecated since 1.18
 - *
 - * @param string $key
 - * @return string
 - */
 -function wfMsgNoTrans( $key ) {
 -      wfDeprecated( __METHOD__, '1.21' );
 -
 -      $args = func_get_args();
 -      array_shift( $args );
 -      return wfMsgReal( $key, $args, true, false, false );
 -}
 -
 -/**
 - * Get a message from anywhere, for the current global language
 - * set with $wgLanguageCode.
 - *
 - * Use this if the message should NOT change dependent on the
 - * language set in the user's preferences. This is the case for
 - * most text written into logs, as well as link targets (such as
 - * the name of the copyright policy page). Link titles, on the
 - * other hand, should be shown in the UI language.
 - *
 - * Note that MediaWiki allows users to change the user interface
 - * language in their preferences, but a single installation
 - * typically only contains content in one language.
 - *
 - * Be wary of this distinction: If you use wfMsg() where you should
 - * use wfMsgForContent(), a user of the software may have to
 - * customize potentially hundreds of messages in
 - * order to, e.g., fix a link in every possible language.
 - *
 - * @deprecated since 1.18
 - *
 - * @param string $key Lookup key for the message, usually
 - *     defined in languages/Language.php
 - * @return string
 - */
 -function wfMsgForContent( $key ) {
 -      wfDeprecated( __METHOD__, '1.21' );
 -
 -      global $wgForceUIMsgAsContentMsg;
 -      $args = func_get_args();
 -      array_shift( $args );
 -      $forcontent = true;
 -      if ( is_array( $wgForceUIMsgAsContentMsg )
 -              && in_array( $key, $wgForceUIMsgAsContentMsg )
 -      ) {
 -              $forcontent = false;
 -      }
 -      return wfMsgReal( $key, $args, true, $forcontent );
 -}
 -
 -/**
 - * Same as above except doesn't transform the message
 - *
 - * @deprecated since 1.18
 - *
 - * @param string $key
 - * @return string
 - */
 -function wfMsgForContentNoTrans( $key ) {
 -      wfDeprecated( __METHOD__, '1.21' );
 -
 -      global $wgForceUIMsgAsContentMsg;
 -      $args = func_get_args();
 -      array_shift( $args );
 -      $forcontent = true;
 -      if ( is_array( $wgForceUIMsgAsContentMsg )
 -              && in_array( $key, $wgForceUIMsgAsContentMsg )
 -      ) {
 -              $forcontent = false;
 -      }
 -      return wfMsgReal( $key, $args, true, $forcontent, false );
 -}
 -
 -/**
 - * Really get a message
 - *
 - * @deprecated since 1.18
 - *
 - * @param string $key Key to get.
 - * @param array $args
 - * @param bool $useDB
 - * @param string|bool $forContent Language code, or false for user lang, true for content lang.
 - * @param bool $transform Whether or not to transform the message.
 - * @return string The requested message.
 - */
 -function wfMsgReal( $key, $args, $useDB = true, $forContent = false, $transform = true ) {
 -      wfDeprecated( __METHOD__, '1.21' );
 -
 -      $message = wfMsgGetKey( $key, $useDB, $forContent, $transform );
 -      $message = wfMsgReplaceArgs( $message, $args );
 -      return $message;
 -}
 -
 -/**
 - * Fetch a message string value, but don't replace any keys yet.
 - *
 - * @deprecated since 1.18
 - *
 - * @param string $key
 - * @param bool $useDB
 - * @param string|bool $langCode Code of the language to get the message for, or
 - *   behaves as a content language switch if it is a boolean.
 - * @param bool $transform Whether to parse magic words, etc.
 - * @return string
 - */
 -function wfMsgGetKey( $key, $useDB = true, $langCode = false, $transform = true ) {
 -      wfDeprecated( __METHOD__, '1.21' );
 -
 -      Hooks::run( 'NormalizeMessageKey', array( &$key, &$useDB, &$langCode, &$transform ) );
 -
 -      $cache = MessageCache::singleton();
 -      $message = $cache->get( $key, $useDB, $langCode );
 -      if ( $message === false ) {
 -              $message = '&lt;' . htmlspecialchars( $key ) . '&gt;';
 -      } elseif ( $transform ) {
 -              $message = $cache->transform( $message );
 -      }
 -      return $message;
 -}
 -
  /**
   * Replace message parameter keys on the given formatted output.
   *
@@@ -1489,6 -1643,159 +1489,6 @@@ function wfMsgReplaceArgs( $message, $a
        return $message;
  }
  
 -/**
 - * Return an HTML-escaped version of a message.
 - * Parameter replacements, if any, are done *after* the HTML-escaping,
 - * so parameters may contain HTML (eg links or form controls). Be sure
 - * to pre-escape them if you really do want plaintext, or just wrap
 - * the whole thing in htmlspecialchars().
 - *
 - * @deprecated since 1.18
 - *
 - * @param string $key
 - * @param string $args,... Parameters
 - * @return string
 - */
 -function wfMsgHtml( $key ) {
 -      wfDeprecated( __METHOD__, '1.21' );
 -
 -      $args = func_get_args();
 -      array_shift( $args );
 -      return wfMsgReplaceArgs( htmlspecialchars( wfMsgGetKey( $key ) ), $args );
 -}
 -
 -/**
 - * Return an HTML version of message
 - * Parameter replacements, if any, are done *after* parsing the wiki-text message,
 - * so parameters may contain HTML (eg links or form controls). Be sure
 - * to pre-escape them if you really do want plaintext, or just wrap
 - * the whole thing in htmlspecialchars().
 - *
 - * @deprecated since 1.18
 - *
 - * @param string $key
 - * @param string $args,... Parameters
 - * @return string
 - */
 -function wfMsgWikiHtml( $key ) {
 -      wfDeprecated( __METHOD__, '1.21' );
 -
 -      $args = func_get_args();
 -      array_shift( $args );
 -      return wfMsgReplaceArgs(
 -              MessageCache::singleton()->parse( wfMsgGetKey( $key ), null,
 -              /* can't be set to false */ true, /* interface */ true )->getText(),
 -              $args );
 -}
 -
 -/**
 - * Returns message in the requested format
 - *
 - * @deprecated since 1.18
 - *
 - * @param string $key Key of the message
 - * @param array $options Processing rules.
 - *   Can take the following options:
 - *     parse: parses wikitext to HTML
 - *     parseinline: parses wikitext to HTML and removes the surrounding
 - *       p's added by parser or tidy
 - *     escape: filters message through htmlspecialchars
 - *     escapenoentities: same, but allows entity references like &#160; through
 - *     replaceafter: parameters are substituted after parsing or escaping
 - *     parsemag: transform the message using magic phrases
 - *     content: fetch message for content language instead of interface
 - *   Also can accept a single associative argument, of the form 'language' => 'xx':
 - *     language: Language object or language code to fetch message for
 - *       (overridden by content).
 - * Behavior for conflicting options (e.g., parse+parseinline) is undefined.
 - *
 - * @return string
 - */
 -function wfMsgExt( $key, $options ) {
 -      wfDeprecated( __METHOD__, '1.21' );
 -
 -      $args = func_get_args();
 -      array_shift( $args );
 -      array_shift( $args );
 -      $options = (array)$options;
 -      $validOptions = array( 'parse', 'parseinline', 'escape', 'escapenoentities', 'replaceafter',
 -              'parsemag', 'content' );
 -
 -      foreach ( $options as $arrayKey => $option ) {
 -              if ( !preg_match( '/^[0-9]+|language$/', $arrayKey ) ) {
 -                      // An unknown index, neither numeric nor "language"
 -                      wfWarn( "wfMsgExt called with incorrect parameter key $arrayKey", 1, E_USER_WARNING );
 -              } elseif ( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option, $validOptions ) ) {
 -                      // A numeric index with unknown value
 -                      wfWarn( "wfMsgExt called with incorrect parameter $option", 1, E_USER_WARNING );
 -              }
 -      }
 -
 -      if ( in_array( 'content', $options, true ) ) {
 -              $forContent = true;
 -              $langCode = true;
 -              $langCodeObj = null;
 -      } elseif ( array_key_exists( 'language', $options ) ) {
 -              $forContent = false;
 -              $langCode = wfGetLangObj( $options['language'] );
 -              $langCodeObj = $langCode;
 -      } else {
 -              $forContent = false;
 -              $langCode = false;
 -              $langCodeObj = null;
 -      }
 -
 -      $string = wfMsgGetKey( $key, /*DB*/true, $langCode, /*Transform*/false );
 -
 -      if ( !in_array( 'replaceafter', $options, true ) ) {
 -              $string = wfMsgReplaceArgs( $string, $args );
 -      }
 -
 -      $messageCache = MessageCache::singleton();
 -      $parseInline = in_array( 'parseinline', $options, true );
 -      if ( in_array( 'parse', $options, true ) || $parseInline ) {
 -              $string = $messageCache->parse( $string, null, true, !$forContent, $langCodeObj );
 -              if ( $string instanceof ParserOutput ) {
 -                      $string = $string->getText();
 -              }
 -
 -              if ( $parseInline ) {
 -                      $string = Parser::stripOuterParagraph( $string );
 -              }
 -      } elseif ( in_array( 'parsemag', $options, true ) ) {
 -              $string = $messageCache->transform( $string,
 -                              !$forContent, $langCodeObj );
 -      }
 -
 -      if ( in_array( 'escape', $options, true ) ) {
 -              $string = htmlspecialchars( $string );
 -      } elseif ( in_array( 'escapenoentities', $options, true ) ) {
 -              $string = Sanitizer::escapeHtmlAllowEntities( $string );
 -      }
 -
 -      if ( in_array( 'replaceafter', $options, true ) ) {
 -              $string = wfMsgReplaceArgs( $string, $args );
 -      }
 -
 -      return $string;
 -}
 -
 -/**
 - * Since wfMsg() and co suck, they don't return false if the message key they
 - * looked up didn't exist but instead the key wrapped in <>'s, this function checks for the
 - * nonexistence of messages by checking the MessageCache::get() result directly.
 - *
 - * @deprecated since 1.18. Use Message::isDisabled().
 - *
 - * @param string $key The message key looked up
 - * @return bool True if the message *doesn't* exist.
 - */
 -function wfEmptyMsg( $key ) {
 -      wfDeprecated( __METHOD__, '1.21' );
 -
 -      return MessageCache::singleton()->get( $key, /*useDB*/true, /*content*/false ) === false;
 -}
 -
  /**
   * Fetch server name for use in error reporting etc.
   * Use real server name if available, so we know which machine
@@@ -1707,6 -2014,21 +1707,6 @@@ function wfClientAcceptsGzip( $force = 
        return $result;
  }
  
 -/**
 - * Obtain the offset and limit values from the request string;
 - * used in special pages
 - *
 - * @param int $deflimit Default limit if none supplied
 - * @param string $optionname Name of a user preference to check against
 - * @return array
 - * @deprecated since 1.24, just call WebRequest::getLimitOffset() directly
 - */
 -function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) {
 -      global $wgRequest;
 -      wfDeprecated( __METHOD__, '1.24' );
 -      return $wgRequest->getLimitOffset( $deflimit, $optionname );
 -}
 -
  /**
   * Escapes the given text so that it may be output using addWikiText()
   * without any linking, formatting, etc. making its way through. This
@@@ -3008,12 -3330,9 +3008,12 @@@ function wfBaseConvert( $input, $source
  /**
   * Check if there is sufficient entropy in php's built-in session generation
   *
 + * @deprecated since 1.27, PHP's session generation isn't used with
 + *  MediaWiki\\Session\\SessionManager
   * @return bool True = there is sufficient entropy
   */
  function wfCheckEntropy() {
 +      wfDeprecated( __FUNCTION__, '1.27' );
        return (
                        ( wfIsWindows() && version_compare( PHP_VERSION, '5.3.3', '>=' ) )
                        || ini_get( 'session.entropy_file' )
  }
  
  /**
 - * Override session_id before session startup if php's built-in
 - * session generation code is not secure.
 + * @deprecated since 1.27, PHP's session generation isn't used with
 + *  MediaWiki\\Session\\SessionManager
   */
  function wfFixSessionID() {
 -      // If the cookie or session id is already set we already have a session and should abort
 -      if ( isset( $_COOKIE[session_name()] ) || session_id() ) {
 -              return;
 -      }
 -
 -      // PHP's built-in session entropy is enabled if:
 -      // - entropy_file is set or you're on Windows with php 5.3.3+
 -      // - AND entropy_length is > 0
 -      // We treat it as disabled if it doesn't have an entropy length of at least 32
 -      $entropyEnabled = wfCheckEntropy();
 -
 -      // If built-in entropy is not enabled or not sufficient override PHP's
 -      // built in session id generation code
 -      if ( !$entropyEnabled ) {
 -              wfDebug( __METHOD__ . ": PHP's built in entropy is disabled or not sufficient, " .
 -                      "overriding session id generation using our cryptrand source.\n" );
 -              session_id( MWCryptRand::generateHex( 32 ) );
 -      }
 +      wfDeprecated( __FUNCTION__, '1.27' );
  }
  
  /**
 - * Reset the session_id
 + * Reset the session id
   *
 + * @deprecated since 1.27, use MediaWiki\\Session\\SessionManager instead
   * @since 1.22
   */
  function wfResetSessionID() {
 -      global $wgCookieSecure;
 -      $oldSessionId = session_id();
 -      $cookieParams = session_get_cookie_params();
 -      if ( wfCheckEntropy() && $wgCookieSecure == $cookieParams['secure'] ) {
 -              session_regenerate_id( false );
 -      } else {
 -              $tmp = $_SESSION;
 -              session_destroy();
 -              wfSetupSession( MWCryptRand::generateHex( 32 ) );
 -              $_SESSION = $tmp;
 +      wfDeprecated( __FUNCTION__, '1.27' );
 +      $session = SessionManager::getGlobalSession();
 +      $delay = $session->delaySave();
 +
 +      $session->resetId();
 +
 +      // Make sure a session is started, since that's what the old
 +      // wfResetSessionID() did.
 +      if ( session_id() !== $session->getId() ) {
 +              wfSetupSession( $session->getId() );
        }
 -      $newSessionId = session_id();
 +
 +      ScopedCallback::consume( $delay );
  }
  
  /**
   * Initialise php session
   *
 - * @param bool $sessionId
 + * @deprecated since 1.27, use MediaWiki\\Session\\SessionManager instead.
 + *  Generally, "using" SessionManager will be calling ->getSessionById() or
 + *  ::getGlobalSession() (depending on whether you were passing $sessionId
 + *  here), then calling $session->persist().
 + * @param bool|string $sessionId
   */
  function wfSetupSession( $sessionId = false ) {
 -      global $wgSessionsInObjectCache, $wgSessionHandler;
 -      global $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly;
 +      wfDeprecated( __FUNCTION__, '1.27' );
  
 -      if ( $wgSessionsInObjectCache ) {
 -              ObjectCacheSessionHandler::install();
 -      } elseif ( $wgSessionHandler && $wgSessionHandler != ini_get( 'session.save_handler' ) ) {
 -              # Only set this if $wgSessionHandler isn't null and session.save_handler
 -              # hasn't already been set to the desired value (that causes errors)
 -              ini_set( 'session.save_handler', $wgSessionHandler );
 +      // If they're calling this, they probably want our session management even
 +      // if NO_SESSION was set for Setup.php.
 +      if ( !MediaWiki\Session\PHPSessionHandler::isInstalled() ) {
 +              MediaWiki\Session\PHPSessionHandler::install( SessionManager::singleton() );
        }
  
 -      session_set_cookie_params(
 -              0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly );
 -      session_cache_limiter( 'private, must-revalidate' );
        if ( $sessionId ) {
                session_id( $sessionId );
 -      } else {
 -              wfFixSessionID();
        }
  
 -      MediaWiki\suppressWarnings();
 -      session_start();
 -      MediaWiki\restoreWarnings();
 +      $session = SessionManager::getGlobalSession();
 +      $session->persist();
  
 -      if ( $wgSessionsInObjectCache ) {
 -              ObjectCacheSessionHandler::renewCurrentSession();
 +      if ( session_id() !== $session->getId() ) {
 +              session_id( $session->getId() );
        }
 +
 +      MediaWiki\quietCall( 'session_start' );
  }
  
  /**
@@@ -3346,55 -3683,35 +3346,35 @@@ function wfGetNull() 
   * @param string|bool $cluster Cluster name accepted by LBFactory. Default: false.
   * @param int|null $timeout Max wait time. Default: 1 day (cli), ~10 seconds (web)
   * @return bool Success (able to connect and no timeouts reached)
+  * @deprecated since 1.27 Use LBFactory::waitForReplication
   */
  function wfWaitForSlaves(
        $ifWritesSince = null, $wiki = false, $cluster = false, $timeout = null
  ) {
-       // B/C: first argument used to be "max seconds of lag"; ignore such values
-       $ifWritesSince = ( $ifWritesSince > 1e9 ) ? $ifWritesSince : null;
        if ( $timeout === null ) {
                $timeout = ( PHP_SAPI === 'cli' ) ? 86400 : 10;
        }
  
-       // Figure out which clusters need to be checked
-       /** @var LoadBalancer[] $lbs */
-       $lbs = array();
        if ( $cluster === '*' ) {
-               wfGetLBFactory()->forEachLB( function ( LoadBalancer $lb ) use ( &$lbs ) {
-                       $lbs[] = $lb;
-               } );
-       } elseif ( $cluster !== false ) {
-               $lbs[] = wfGetLBFactory()->getExternalLB( $cluster );
-       } else {
-               $lbs[] = wfGetLB( $wiki );
-       }
-       // Get all the master positions of applicable DBs right now.
-       // This can be faster since waiting on one cluster reduces the
-       // time needed to wait on the next clusters.
-       $masterPositions = array_fill( 0, count( $lbs ), false );
-       foreach ( $lbs as $i => $lb ) {
-               if ( $lb->getServerCount() <= 1 ) {
-                       // Bug 27975 - Don't try to wait for slaves if there are none
-                       // Prevents permission error when getting master position
-                       continue;
-               } elseif ( $ifWritesSince && $lb->lastMasterChangeTimestamp() < $ifWritesSince ) {
-                       continue; // no writes since the last wait
-               }
-               $masterPositions[$i] = $lb->getMasterPos();
+               $cluster = false;
+               $wiki = false;
+       } elseif ( $wiki === false ) {
+               $wiki = wfWikiID();
        }
  
-       $ok = true;
-       foreach ( $lbs as $i => $lb ) {
-               if ( $masterPositions[$i] ) {
-                       // The DBMS may not support getMasterPos() or the whole
-                       // load balancer might be fake (e.g. $wgAllDBsAreLocalhost).
-                       $ok = $lb->waitForAll( $masterPositions[$i], $timeout ) && $ok;
-               }
+       try {
+               wfGetLBFactory()->waitForReplication( array(
+                       'wiki' => $wiki,
+                       'cluster' => $cluster,
+                       'timeout' => $timeout,
+                       // B/C: first argument used to be "max seconds of lag"; ignore such values
+                       'ifWritesSince' => ( $ifWritesSince > 1e9 ) ? $ifWritesSince : null
+               ) );
+       } catch ( DBReplicationWaitError $e ) {
+               return false;
        }
  
-       return $ok;
+       return true;
  }
  
  /**
@@@ -21,9 -21,6 +21,9 @@@
   * @ingroup Database
   */
  
 +use Psr\Log\LoggerInterface;
 +use MediaWiki\Logger\LoggerFactory;
 +
  /**
   * An interface for generating database load balancers
   * @ingroup Database
@@@ -32,12 -29,6 +32,12 @@@ abstract class LBFactory 
        /** @var ChronologyProtector */
        protected $chronProt;
  
 +      /** @var TransactionProfiler */
 +      protected $trxProfiler;
 +
 +      /** @var LoggerInterface */
 +      protected $logger;
 +
        /** @var LBFactory */
        private static $instance;
  
@@@ -56,8 -47,6 +56,8 @@@
                }
  
                $this->chronProt = $this->newChronologyProtector();
 +              $this->trxProfiler = Profiler::instance()->getTransactionProfiler();
 +              $this->logger = LoggerFactory::getInstance( 'DBTransaction' );
        }
  
        /**
         * @param array $args
         */
        private function forEachLBCallMethod( $methodName, array $args = array() ) {
 -              $this->forEachLB( function ( LoadBalancer $loadBalancer, $methodName, array $args ) {
 -                      call_user_func_array( array( $loadBalancer, $methodName ), $args );
 -              }, array( $methodName, $args ) );
 +              $this->forEachLB(
 +                      function ( LoadBalancer $loadBalancer, $methodName, array $args ) {
 +                              call_user_func_array( array( $loadBalancer, $methodName ), $args );
 +                      },
 +                      array( $methodName, $args )
 +              );
        }
  
        /**
         * Commit on all connections. Done for two reasons:
         * 1. To commit changes to the masters.
         * 2. To release the snapshot on all connections, master and slave.
 +       * @param string $fname Caller name
         */
 -      public function commitAll() {
 -              $this->forEachLBCallMethod( 'commitAll' );
 +      public function commitAll( $fname = __METHOD__ ) {
 +              $this->logMultiDbTransaction();
 +
 +              $start = microtime( true );
 +              $this->forEachLBCallMethod( 'commitAll', array( $fname ) );
 +              $timeMs = 1000 * ( microtime( true ) - $start );
 +
 +              RequestContext::getMain()->getStats()->timing( "db.commit-all", $timeMs );
        }
  
        /**
         * Commit changes on all master connections
 +       * @param string $fname Caller name
         */
 -      public function commitMasterChanges() {
 +      public function commitMasterChanges( $fname = __METHOD__ ) {
 +              $this->logMultiDbTransaction();
 +
                $start = microtime( true );
 -              $this->forEachLBCallMethod( 'commitMasterChanges' );
 +              $this->forEachLBCallMethod( 'commitMasterChanges', array( $fname ) );
                $timeMs = 1000 * ( microtime( true ) - $start );
 +
                RequestContext::getMain()->getStats()->timing( "db.commit-masters", $timeMs );
        }
  
        /**
         * Rollback changes on all master connections
 +       * @param string $fname Caller name
         * @since 1.23
         */
 -      public function rollbackMasterChanges() {
 -              $this->forEachLBCallMethod( 'rollbackMasterChanges' );
 +      public function rollbackMasterChanges( $fname = __METHOD__ ) {
 +              $this->forEachLBCallMethod( 'rollbackMasterChanges', array( $fname ) );
 +      }
 +
 +      /**
 +       * Log query info if multi DB transactions are going to be committed now
 +       */
 +      private function logMultiDbTransaction() {
 +              $callersByDB = array();
 +              $this->forEachLB( function ( LoadBalancer $lb ) use ( &$callersByDB ) {
 +                      $masterName = $lb->getServerName( $lb->getWriterIndex() );
 +                      $callers = $lb->pendingMasterChangeCallers();
 +                      if ( $callers ) {
 +                              $callersByDB[$masterName] = $callers;
 +                      }
 +              } );
 +
 +              if ( count( $callersByDB ) >= 2 ) {
 +                      $dbs = implode( ', ', array_keys( $callersByDB ) );
 +                      $msg = "Multi-DB transaction [{$dbs}]:\n";
 +                      foreach ( $callersByDB as $db => $callers ) {
 +                              $msg .= "$db: " . implode( '; ', $callers ) . "\n";
 +                      }
 +                      $this->logger->info( $msg );
 +              }
        }
  
        /**
                return $ret;
        }
  
+       /**
+        * Waits for the slave DBs to catch up to the current master position
+        *
+        * Use this when updating very large numbers of rows, as in maintenance scripts,
+        * to avoid causing too much lag. Of course, this is a no-op if there are no slaves.
+        *
+        * By default this waits on all DB clusters actually used in this request.
+        * This makes sense when lag being waiting on is caused by the code that does this check.
+        * In that case, setting "ifWritesSince" can avoid the overhead of waiting for clusters
+        * that were not changed since the last wait check. To forcefully wait on a specific cluster
+        * for a given wiki, use the 'wiki' parameter. To forcefully wait on an "external" cluster,
+        * use the "cluster" parameter.
+        *
+        * Never call this function after a large DB write that is *still* in a transaction.
+        * It only makes sense to call this after the possible lag inducing changes were committed.
+        *
+        * @param array $opts Optional fields that include:
+        *   - wiki : wait on the load balancer DBs that handles the given wiki
+        *   - cluster : wait on the given external load balancer DBs
+        *   - timeout : Max wait time. Default: ~60 seconds
+        *   - ifWritesSince: Only wait if writes were done since this UNIX timestamp
+        * @throws DBReplicationWaitError If a timeout or error occured waiting on a DB cluster
+        * @since 1.27
+        */
+       public function waitForReplication( array $opts = array() ) {
+               $opts += array(
+                       'wiki' => false,
+                       'cluster' => false,
+                       'timeout' => 60,
+                       'ifWritesSince' => null
+               );
+               // Figure out which clusters need to be checked
+               /** @var LoadBalancer[] $lbs */
+               $lbs = array();
+               if ( $opts['cluster'] !== false ) {
+                       $lbs[] = $this->getExternalLB( $opts['cluster'] );
+               } elseif ( $opts['wiki'] !== false ) {
+                       $lbs[] = $this->getMainLB( $opts['wiki'] );
+               } else {
+                       $this->forEachLB( function ( LoadBalancer $lb ) use ( &$lbs ) {
+                               $lbs[] = $lb;
+                       } );
+                       if ( !$lbs ) {
+                               return; // nothing actually used
+                       }
+               }
+               // Get all the master positions of applicable DBs right now.
+               // This can be faster since waiting on one cluster reduces the
+               // time needed to wait on the next clusters.
+               $masterPositions = array_fill( 0, count( $lbs ), false );
+               foreach ( $lbs as $i => $lb ) {
+                       if ( $lb->getServerCount() <= 1 ) {
+                               // Bug 27975 - Don't try to wait for slaves if there are none
+                               // Prevents permission error when getting master position
+                               continue;
+                       } elseif ( $opts['ifWritesSince']
+                               && $lb->lastMasterChangeTimestamp() < $opts['ifWritesSince']
+                       ) {
+                               continue; // no writes since the last wait
+                       }
+                       $masterPositions[$i] = $lb->getMasterPos();
+               }
+               $failed = array();
+               foreach ( $lbs as $i => $lb ) {
+                       if ( $masterPositions[$i] ) {
+                               // The DBMS may not support getMasterPos() or the whole
+                               // load balancer might be fake (e.g. $wgAllDBsAreLocalhost).
+                               if ( !$lb->waitForAll( $masterPositions[$i], $opts['timeout'] ) ) {
+                                       $failed[] = $lb->getServerName( $lb->getWriterIndex() );
+                               }
+                       }
+               }
+               if ( $failed ) {
+                       throw new DBReplicationWaitError(
+                               "Could not wait for slaves to catch up to " .
+                               implode( ', ', $failed )
+                       );
+               }
+       }
        /**
         * Disable the ChronologyProtector for all load balancers
         *
@@@ -378,3 -413,9 +462,9 @@@ class DBAccessError extends MWExceptio
                        "This is not allowed." );
        }
  }
+ /**
+  * Exception class for replica DB wait timeouts
+  */
+ class DBReplicationWaitError extends Exception {
+ }