/** * Shut down, close connections and destroy the cached instance. * */ static function destroyInstance() { if (self::$instance) { self::$instance->shutdown(); self::$instance->forEachLBCallMethod('closeAll'); self::$instance = null; } }
/** * @dataProvider getLBFactoryClassProvider */ public function testGetLBFactoryClass($expected, $deprecated) { $mockDB = $this->getMockBuilder('DatabaseMysql')->disableOriginalConstructor()->getMock(); $config = array('class' => $deprecated, 'connection' => $mockDB, 'sectionsByDB' => array(), 'sectionLoads' => array(), 'serverTemplate' => array()); $this->hideDeprecated('$wgLBFactoryConf must be updated. See RELEASE-NOTES for details'); $result = LBFactory::getLBFactoryClass($config); $this->assertEquals($expected, $result); }
/** * @param array $conf An associative array with one member: * - connection: The IDatabase connection object */ public function __construct(array $conf) { parent::__construct($conf); if (!isset($conf['connection'])) { throw new InvalidArgumentException("Missing 'connection' argument."); } $lb = new LoadBalancerSingle(array_merge($this->baseLoadBalancerParams(), $conf)); $this->initLoadBalancer($lb); $this->lb = $lb; }
/** * @see LBFactory::__construct() * @param array $conf Parameters of LBFactory::__construct() as well as: * - servers : list of server configuration maps to Database::factory(). * Additionally, the server maps should have a 'load' key, which is used to decide * how often clients connect to one server verses the others. A 'max lag' key should * also be set on server maps, indicating how stale the data can be before the load * balancer tries to avoid using it. The map can have 'is static' set to disable blocking * replication sync checks (intended for archive servers with unchanging data). * - externalClusters : map of cluster names to server arrays. The servers arrays have the * same format as "servers" above. */ public function __construct(array $conf) { parent::__construct($conf); $this->servers = isset($conf['servers']) ? $conf['servers'] : []; foreach ($this->servers as $i => $server) { if ($i == 0) { $this->servers[$i]['master'] = true; } else { $this->servers[$i]['replica'] = true; } } $this->externalClusters = isset($conf['externalClusters']) ? $conf['externalClusters'] : []; $this->loadMonitorClass = isset($conf['loadMonitorClass']) ? $conf['loadMonitorClass'] : 'LoadMonitor'; }
/** * Handle some last-minute setup here. */ public function finalSetup() { global $wgCommandLineMode, $wgShowSQLErrors, $wgServer; global $wgDBadminuser, $wgDBadminpassword; global $wgDBuser, $wgDBpassword, $wgDBservers, $wgLBFactoryConf; # Turn off output buffering again, it might have been turned on in the settings files if (ob_get_level()) { ob_end_flush(); } # Same with these $wgCommandLineMode = true; # Override $wgServer if ($this->hasOption('server')) { $wgServer = $this->getOption('server', $wgServer); } # If these were passed, use them if ($this->mDbUser) { $wgDBadminuser = $this->mDbUser; } if ($this->mDbPass) { $wgDBadminpassword = $this->mDbPass; } if ($this->getDbType() == self::DB_ADMIN && isset($wgDBadminuser)) { $wgDBuser = $wgDBadminuser; $wgDBpassword = $wgDBadminpassword; if ($wgDBservers) { /** * @var $wgDBservers array */ foreach ($wgDBservers as $i => $server) { $wgDBservers[$i]['user'] = $wgDBuser; $wgDBservers[$i]['password'] = $wgDBpassword; } } if (isset($wgLBFactoryConf['serverTemplate'])) { $wgLBFactoryConf['serverTemplate']['user'] = $wgDBuser; $wgLBFactoryConf['serverTemplate']['password'] = $wgDBpassword; } LBFactory::destroyInstance(); } $this->afterFinalSetup(); $wgShowSQLErrors = true; @set_time_limit(0); $this->adjustMemoryLimit(); }
/** * @param array $conf An associative array with one member: * - connection: The DatabaseBase connection object */ public function __construct(array $conf) { parent::__construct($conf); $this->lb = new LoadBalancerSingle(['readOnlyReason' => $this->readOnlyReason, 'trxProfiler' => $this->trxProfiler] + $conf); }
/** * @param DeferrableUpdate $update * @param LBFactory $lbFactory * @param integer $stage * @return ErrorPageError|null */ private static function runUpdate(DeferrableUpdate $update, LBFactory $lbFactory, $stage) { $guiError = null; try { $fnameTrxOwner = get_class($update) . '::doUpdate'; $lbFactory->beginMasterChanges($fnameTrxOwner); $update->doUpdate(); $lbFactory->commitMasterChanges($fnameTrxOwner); } catch (Exception $e) { // Reporting GUI exceptions does not work post-send if ($e instanceof ErrorPageError && $stage === self::PRESEND) { $guiError = $e; } MWExceptionHandler::rollbackMasterChangesAndLog($e); } return $guiError; }
/** * Handle some last-minute setup here. */ public function finalSetup() { global $wgCommandLineMode, $wgShowSQLErrors, $wgServer; global $wgDBadminuser, $wgDBadminpassword; global $wgDBuser, $wgDBpassword, $wgDBservers, $wgLBFactoryConf; # Turn off output buffering again, it might have been turned on in the settings files if (ob_get_level()) { ob_end_flush(); } # Same with these $wgCommandLineMode = true; # Override $wgServer if ($this->hasOption('server')) { $wgServer = $this->getOption('server', $wgServer); } # If these were passed, use them if ($this->mDbUser) { $wgDBadminuser = $this->mDbUser; } if ($this->mDbPass) { $wgDBadminpassword = $this->mDbPass; } if ($this->getDbType() == self::DB_ADMIN && isset($wgDBadminuser)) { $wgDBuser = $wgDBadminuser; $wgDBpassword = $wgDBadminpassword; if ($wgDBservers) { /** * @var $wgDBservers array */ foreach ($wgDBservers as $i => $server) { $wgDBservers[$i]['user'] = $wgDBuser; $wgDBservers[$i]['password'] = $wgDBpassword; } } if (isset($wgLBFactoryConf['serverTemplate'])) { $wgLBFactoryConf['serverTemplate']['user'] = $wgDBuser; $wgLBFactoryConf['serverTemplate']['password'] = $wgDBpassword; } LBFactory::destroyInstance(); } $this->afterFinalSetup(); $wgShowSQLErrors = true; // @codingStandardsIgnoreStart Allow error supppression. wfSuppressWarnings() // is not avaiable. @set_time_limit(0); // @codingStandardsIgnoreStart $this->adjustMemoryLimit(); // Per-script profiling; useful for debugging $forcedProfiler = $this->getOption('profiler'); if ($forcedProfiler === 'text') { Profiler::setInstance(new ProfilerSimpleText(array())); Profiler::instance()->setTemplated(true); } elseif ($forcedProfiler === 'trace') { Profiler::setInstance(new ProfilerSimpleTrace(array())); Profiler::instance()->setTemplated(true); } }
protected function exportGroup(MessageGroup $group, $multi = false) { // Make sure all existing connections are dead, // we can't use them in forked children. LBFactory::destroyInstance(); $server = TTMServer::primary(); $id = $group->getId(); $sourceLanguage = $group->getSourceLanguage(); if ($multi) { $stats = MessageGroupStats::forGroup($id); $this->statusLine("Loaded stats for {$id}\n"); } else { $this->statusLine("Loading stats... ", 4); $stats = MessageGroupStats::forGroup($id); $this->output("done!", 4); $this->statusLine("Inserting sources: ", 5); } $collection = $group->initCollection($sourceLanguage); $collection->filter('ignored'); $collection->filter('optional'); $collection->initMessages(); $sids = array(); $counter = 0; foreach ($collection->keys() as $mkey => $title) { $def = $collection[$mkey]->definition(); $sids[$mkey] = $server->insertSource($title, $sourceLanguage, $def); if (++$counter % $this->mBatchSize === 0 && !$multi) { wfWaitForSlaves(10); $this->output('.', 5); } } $total = count($sids); if ($multi) { $this->statusLine("Inserted {$total} source entries for {$id}\n"); } else { $this->output("{$total} entries", 5); $this->statusLine("Inserting translations...", 6); } $dbw = $server->getDB(DB_MASTER); foreach ($stats as $targetLanguage => $numbers) { if ($targetLanguage === $sourceLanguage) { continue; } if ($numbers[MessageGroupStats::TRANSLATED] === 0) { continue; } if (!$multi) { $this->output(sprintf("%19s ", $targetLanguage), $targetLanguage); } $collection->resetForNewLanguage($targetLanguage); $collection->filter('ignored'); $collection->filter('optional'); $collection->filter('translated', false); $collection->loadTranslations(); $inserts = array(); foreach ($collection->keys() as $mkey => $title) { $inserts[] = array('tmt_sid' => $sids[$mkey], 'tmt_lang' => $targetLanguage, 'tmt_text' => $collection[$mkey]->translation()); } do { $batch = array_splice($inserts, 0, $this->mBatchSize); $dbw->insert('translate_tmt', $batch, __METHOD__); if (!$multi) { $this->output('.', $targetLanguage); } wfWaitForSlaves(10); } while (count($inserts)); } if ($multi) { $this->statusLine("Inserted translations for {$id}\n"); } }
/** * Get the load balancer factory object * * @return LBFactory */ function wfGetLBFactory() { return LBFactory::singleton(); }
protected function resetStateForFork() { // Child, reseed because there is no bug in PHP: // http://bugs.php.net/bug.php?id=42465 mt_srand(getmypid()); // Make sure all existing connections are dead, // we can't use them in forked children. LBFactory::destroyInstance(); }
private function scriptDone($script) { global $wgDBuser, $wgDBpassword, $wgDBadminuser, $wgDBadminpassword, $wgDBuserold, $wgDBpasswordold; if ($script->getDbType() === Maintenance::DB_ADMIN && isset($wgDBadminuser)) { $wgDBuser = $wgDBuserold; $wgDBpassword = $wgDBpasswordold; unset($GLOBALS['wgDBuserold'], $GLOBALS['wgDBpasswordold']); LBFactory::destroyInstance(); } $goptions = $this->metadata[$this->type]['option']; $gargs = $this->metadata[$this->type]['arg']; if ($goptions != array()) { foreach ($goptions as $a) { if ($a['type'] == 'textarea' && $a['tmpfile'] && file_exists($a['tmpfile'])) { unlink($a['tmpfile']); } } } if ($gargs != array()) { foreach ($gargs as $a) { if ($a['type'] == 'textarea' && $a['tmpfile'] && file_exists($a['tmpfile'])) { unlink($a['tmpfile']); } } } }
/** * @see LBFactory::__construct() * * Template override precedence (highest => lowest): * - templateOverridesByServer * - masterTemplateOverrides * - templateOverridesBySection/templateOverridesByCluster * - externalTemplateOverrides * - serverTemplate * Overrides only work on top level keys (so nested values will not be merged). * * Server configuration maps should be of the format Database::factory() requires. * Additionally, a 'max lag' key should also be set on server maps, indicating how stale the * data can be before the load balancer tries to avoid using it. The map can have 'is static' * set to disable blocking replication sync checks (intended for archive servers with * unchanging data). * * @param array $conf Parameters of LBFactory::__construct() as well as: * - sectionsByDB Map of database names to section names. * - sectionLoads 2-d map. For each section, gives a map of server names to * load ratios. For example: * [ * 'section1' => [ * 'db1' => 100, * 'db2' => 100 * ] * ] * - serverTemplate Server configuration map intended for Database::factory(). * Note that "host", "hostName" and "load" entries will be * overridden by "sectionLoads" and "hostsByName". * - groupLoadsBySection 3-d map giving server load ratios for each section/group. * For example: * [ * 'section1' => [ * 'group1' => [ * 'db1' => 100, * 'db2' => 100 * ] * ] * ] * - groupLoadsByDB 3-d map giving server load ratios by DB name. * - hostsByName Map of hostname to IP address. * - externalLoads Map of external storage cluster name to server load map. * - externalTemplateOverrides Set of server configuration maps overriding * "serverTemplate" for external storage. * - templateOverridesByServer 2-d map overriding "serverTemplate" and * "externalTemplateOverrides" on a server-by-server basis. * Applies to both core and external storage. * - templateOverridesBySection 2-d map overriding the server configuration maps by section. * - templateOverridesByCluster 2-d map overriding the server configuration maps by external * storage cluster. * - masterTemplateOverrides Server configuration map overrides for all master servers. * - loadMonitorClass Name of the LoadMonitor class to always use. * - readOnlyBySection A map of section name to read-only message. * Missing or false for read/write. */ public function __construct(array $conf) { parent::__construct($conf); $this->conf = $conf; $required = ['sectionsByDB', 'sectionLoads', 'serverTemplate']; $optional = ['groupLoadsBySection', 'groupLoadsByDB', 'hostsByName', 'externalLoads', 'externalTemplateOverrides', 'templateOverridesByServer', 'templateOverridesByCluster', 'templateOverridesBySection', 'masterTemplateOverrides', 'readOnlyBySection', 'loadMonitorClass']; foreach ($required as $key) { if (!isset($conf[$key])) { throw new InvalidArgumentException(__CLASS__ . ": {$key} is required."); } $this->{$key} = $conf[$key]; } foreach ($optional as $key) { if (isset($conf[$key])) { $this->{$key} = $conf[$key]; } } }
* * @note As of version 1.27, MediaWiki is only beginning to use dependency injection. * The services defined here do not yet fully represent all services used by core, * much of the code still relies on global state for this accessing services. * * @since 1.27 * * @see docs/injection.txt for an overview of using dependency injection in the * MediaWiki code base. */ use MediaWiki\Interwiki\ClassicInterwikiLookup; use MediaWiki\Linker\LinkRendererFactory; use MediaWiki\MediaWikiServices; return ['DBLoadBalancerFactory' => function (MediaWikiServices $services) { $config = $services->getMainConfig()->get('LBFactoryConf'); $class = LBFactory::getLBFactoryClass($config); if (!isset($config['readOnlyReason'])) { // TODO: replace the global wfConfiguredReadOnlyReason() with a service. $config['readOnlyReason'] = wfConfiguredReadOnlyReason(); } return new $class($config); }, 'DBLoadBalancer' => function (MediaWikiServices $services) { // just return the default LB from the DBLoadBalancerFactory service return $services->getDBLoadBalancerFactory()->getMainLB(); }, 'SiteStore' => function (MediaWikiServices $services) { $rawSiteStore = new DBSiteStore($services->getDBLoadBalancer()); // TODO: replace wfGetCache with a CacheFactory service. // TODO: replace wfIsHHVM with a capabilities service. $cache = wfGetCache(wfIsHHVM() ? CACHE_ACCEL : CACHE_ANYTHING); return new CachingSiteStore($rawSiteStore, $cache); }, 'SiteLookup' => function (MediaWikiServices $services) {
function tearDown() { LBFactory::destroyInstance(); }
/** * @param array $conf An associative array with one member: * - connection: The DatabaseBase connection object */ public function __construct(array $conf) { parent::__construct($conf); $this->lb = new LoadBalancerSingle($conf); }
/** * @param array $conf An associative array with one member: * - connection: The DatabaseBase connection object */ public function __construct(array $conf) { parent::__construct($conf); $conf['readOnlyReason'] = $this->readOnlyReason; $this->lb = new LoadBalancerSingle($conf); }
} $dbw = wfGetDB(DB_SLAVE); // define socket which listens for a break signal $socket = socket_create_listen("9876"); // port is freely chosen socket_set_nonblock($socket); // max number of threads to be considered to calculate sleeping time define('MAX_THREADS_CONSIDERED', 10); global $wgLoadBalancer; print "-------------------------------------------------\n"; print " Running jobs... ({$rate} jobs/second) \t\t \n"; print "-------------------------------------------------\n"; for (;;) { // determine the most lagged slave // if $lag == -1, there's no slave. list($host, $lag) = LBFactory::singleton()->getMainLB()->getMaxLag(); if ($lag == -1) { // make sleeping time adaptive to database load. $runningThreads = smwfGetNumOfRunningThreads($dbw); $runningThreads = $runningThreads <= MAX_THREADS_CONSIDERED ? $runningThreads : MAX_THREADS_CONSIDERED; // wait depending on user-defined $rate and server load sleep(1 / $rate + $runningThreads); } else { // wait for most lagged slave to be *below* 1/$rate + 3 seconds lag time. wfWaitForSlaves(1 / $rate + 3); } // get next job $job = Job::pop(); // is there a break signal? $accept_sock = @socket_accept($socket); if ($accept_sock !== false) {
public function __construct(array $conf) { parent::__construct($conf); $this->loadMonitorClass = isset($conf['loadMonitorClass']) ? $conf['loadMonitorClass'] : null; }
/** * execute * * Main entry point for class * * @author Krzysztof Krzyżaniak <*****@*****.**> * * @return integer: wikia id or null if wikia is not handled by WikiFactory */ public function execute() { wfProfileIn(__METHOD__); global $wgCityId, $wgDevelEnvironment, $wgDBservers, $wgLBFactoryConf, $wgDBserver, $wgContLang; /** * Hook to allow extensions to alter the initialization. For example, * setting the mCityID then returning true will override which wiki * to use. * * @author Sean Colombo */ if (!wfRunHooks('WikiFactory::execute', array(&$this))) { wfProfileOut(__METHOD__); return $this->mWikiID; } /** * load balancer uses one method which demand wgContLang defined * See BugId: 12474 */ $wgContLang = new StubObject('wgContLang'); /** * local cache, change to CACHE_ACCEL for local */ global $wgWikiFactoryCacheType; $oMemc = wfGetCache($wgWikiFactoryCacheType); if (empty($this->mAlwaysFromDB)) { /** * remember! for http requests we only known $this->mServerName * (domain), $this->mCityId is unknown (set to false in constructor) */ wfProfileIn(__METHOD__ . "-domaincache"); $key = WikiFactory::getDomainKey($this->mServerName); $this->mDomain = $oMemc->get($key); $this->mDomain = isset($this->mDomain["id"]) ? $this->mDomain : array(); $this->debug("reading from cache, key {$key}"); wfProfileOut(__METHOD__ . "-domaincache"); } if (!isset($this->mDomain["id"]) || $this->mAlwaysFromDB) { wfProfileIn(__METHOD__ . "-domaindb"); /** * first run or cache expired */ $dbr = $this->getDB(); /** * interactive/cmdline case. We know city_id so we don't have to * ask city_domains table */ if ($this->mCityID || $this->mCityDB) { $oRow = $dbr->selectRow(array("city_list"), array("city_id", "city_public", "city_factory_timestamp", "city_url", "city_dbname"), $this->mCityID ? array("city_list.city_id" => $this->mCityID) : array("city_list.city_dbname" => $this->mCityDB), __METHOD__ . '::domaindb'); if (isset($oRow->city_id)) { preg_match("/http[s]*\\:\\/\\/(.+)\$/", $oRow->city_url, $matches); $host = rtrim($matches[1], "/"); $this->mCityID = $oRow->city_id; $this->mWikiID = $oRow->city_id; $this->mIsWikiaActive = $oRow->city_public; $this->mCityHost = $host; $this->mCityDB = $oRow->city_dbname; $this->mTimestamp = $oRow->city_factory_timestamp; $this->mDomain = array("id" => $oRow->city_id, "host" => $host, "active" => $oRow->city_public, "time" => $oRow->city_factory_timestamp, "db" => $this->mCityDB); } } else { /** * request from HTTPD case. We only know server name so we * have to ask city_domains table */ $oRow = $dbr->selectRow(array("city_domains", "city_list"), array("city_list.city_id", "city_public", "city_factory_timestamp", "city_domain", "city_url", "city_dbname"), array("city_domains.city_id = city_list.city_id", "city_domains.city_domain" => $this->mServerName), __METHOD__ . '::servername'); if (isset($oRow->city_id) && $oRow->city_id > 0) { $oRow->city_domain = strtolower($oRow->city_domain); preg_match("/http[s]*\\:\\/\\/(.+)\$/", $oRow->city_url, $matches); $host = rtrim($matches[1], "/"); if ($oRow->city_domain == $this->mServerName && $this->mServerName) { $this->mWikiID = $oRow->city_id; $this->mIsWikiaActive = $oRow->city_public; $this->mCityHost = $host; $this->mCityDB = $oRow->city_dbname; $this->mTimestamp = $oRow->city_factory_timestamp; $this->mDomain = array("id" => $oRow->city_id, "host" => $host, "active" => $oRow->city_public, "time" => $oRow->city_factory_timestamp, "db" => $oRow->city_dbname); } } } if (empty($this->mAlwaysFromDB) && !empty($this->mWikiID)) { /** * store value in cache */ $oMemc->set(WikiFactory::getDomainKey($this->mServerName), $this->mDomain, $this->mExpireDomainCacheTimeout); } $this->debug("city_id={$this->mWikiID}, reading from database key {$this->mServerName}"); wfProfileOut(__METHOD__ . "-domaindb"); } else { /** * data taken from cache */ $this->mWikiID = $this->mDomain["id"]; $this->mCityHost = $this->mDomain["host"]; $this->mIsWikiaActive = $this->mDomain["active"]; $this->mTimestamp = isset($this->mDomain["time"]) ? $this->mDomain["time"] : null; $this->mCityDB = isset($this->mDomain["db"]) ? $this->mDomain["db"] : false; } /** * save default var values for Special:WikiFactory * @todo this should be smarter... */ if ($this->mWikiID == 177) { $this->mSaveDefaults = true; } /** * redirection to another url */ if ($this->mIsWikiaActive == 2) { $this->debug("city_id={$this->mWikiID};city_public={$this->mIsWikiaActive}), redirected to {$this->mCityHost}"); header("X-Redirected-By-WF: 2"); header("Location: http://{$this->mCityHost}/", true, 301); wfProfileOut(__METHOD__); exit(0); } /** * if $this->mCityURL different from city_url we redirect to city_url * (as main server) * * mCityHost may contain path after url (memory-alpha, dofus), we just * split this for comparing hosts. */ list($host, $path) = array_pad(explode("/", $this->mCityHost, 2), 2, false); /** * check if domain from browser is different than main domain for wiki */ $cond1 = !empty($host) && !empty($this->mServerName) && strtolower($host) != $this->mServerName; /** * check if not additional domain was used (then we redirect anyway) */ $cond2 = !empty($host) && $this->mAlternativeDomainUsed && $host != $this->mOldServerName; if (($cond1 || $cond2) && empty($wgDevelEnvironment)) { $url = wfGetCurrentUrl(); /** * now recombine url from parts */ if (preg_match("!^/{$path}!", $url["path"]) == 0) { $url["path"] = "/{$path}" . $url["path"]; } $target = $url["scheme"] . "://" . $host . $url["path"]; $target = isset($url["query"]) ? $target . "?" . $url["query"] : $target; $this->debug("redirected from {$url["url"]} to {$target}"); header("X-Redirected-By-WF: NotPrimary"); header("Location: {$target}", true, 301); wfProfileOut(__METHOD__); exit(0); } /** * if wikia is not defined or is marked for closing we redirect to * Not_a_valid_Wikia * @todo the -1 status should probably be removed or defined more precisely */ if (empty($this->mWikiID) || $this->mIsWikiaActive == -1) { if (!$this->mCommandLine) { global $wgNotAValidWikia; $this->debug("redirected to {$wgNotAValidWikia}, {$this->mWikiID} {$this->mIsWikiaActive}"); if ($this->mIsWikiaActive < 0) { header("X-Redirected-By-WF: MarkedForClosing"); } else { header("X-Redirected-By-WF: NotAValidWikia"); } header("Location: {$wgNotAValidWikia}"); wfProfileOut(__METHOD__); exit(0); } } /** * if wikia is disabled and is not Commandline mode we redirect it to * dump directory. */ if (empty($this->mIsWikiaActive) || $this->mIsWikiaActive == -2) { if (!$this->mCommandLine) { global $wgNotAValidWikia; if ($this->mCityDB) { $database = strtolower($this->mCityDB); $redirect = sprintf("http://%s/wiki/Special:CloseWiki/information/%s", $wgDevelEnvironment ? "www.awc.wikia-inc.com" : "community.wikia.com", $database); } else { $redirect = $wgNotAValidWikia; } $this->debug("disabled and not commandline, redirected to {$redirect}, {$this->mWikiID} {$this->mIsWikiaActive}"); header("X-Redirected-By-WF: Dump"); header("Location: {$redirect}"); wfProfileOut(__METHOD__); exit(0); } } /** * for yellowikis.wikia check geolocation and for GB -> redirect to owikis * @author Przemek Piotrowski (Nef) */ if (0 === strpos($this->mServerName, 'yellowikis.')) { header("X-Redirected-By-WF: Geo"); global $wgLocationOfGeoIPDatabase; if (!empty($wgLocationOfGeoIPDatabase) && file_exists($wgLocationOfGeoIPDatabase)) { /** * ProxyTools methods cannot be used because PT is not loaded at this point. * PT cannot be just included as it requires a lot to be initialized first )-: * * Order is *important* here! Proxy are added "from the right side" * to the combined HTTP_X_FORWARDED_FOR + REMOTE_ADDR. */ $ips = array(); if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ips = preg_split('/\\s*,\\s*/', $_SERVER['HTTP_X_FORWARDED_FOR']); } if (!empty($_SERVER['REMOTE_ADDR'])) { $ips[] = $_SERVER['REMOTE_ADDR']; } if (!empty($ips[0])) { require_once 'Net/GeoIP.php'; try { $geoip = Net_GeoIP::getInstance($wgLocationOfGeoIPDatabase); if ('GB' == $geoip->lookupCountryCode($ips[0])) { header("X-Redirected-By-WF: Geo"); /** * just exit, no redirect at all */ wfProfileOut(__METHOD__); exit(0); } } catch (Exception $e) { #--- ignore exception, redirect is an option, not a necessity } } } } /** * get info about city variables from memcached and then check, * maybe memcached is down and returned only error code */ if (empty($this->mAlwaysFromDB)) { wfProfileIn(__METHOD__ . "-varscache"); /** * first from serialized file */ $key = WikiFactory::getVarsKey($this->mWikiID); $data = $oMemc->get($key); if (isset($data["stamp"]) && $data["stamp"] == $this->mTimestamp) { $this->mVariables = isset($data["data"]) && is_array($data["data"]) ? $data["data"] : array(); $this->debug("wikifactory: reading from cache, key {$key}, count " . count($this->mVariables)); } else { $this->debug("wikifactory: timestamp doesn't match. Cache expired"); } wfProfileOut(__METHOD__ . "-varscache"); } /** * if wgDBname is not defined we get all variables from database */ if (!isset($this->mVariables["wgDBname"])) { wfProfileIn(__METHOD__ . "-varsdb"); $dbr = $this->getDB(); $oRes = $dbr->select(array("city_variables", "city_variables_pool"), array("cv_name", "cv_value"), array("cv_id = cv_variable_id", "cv_city_id = {$this->mWikiID}"), __METHOD__ . '::varsdb'); while ($oRow = $dbr->fetchObject($oRes)) { #--- some magic, rewritting path, etc legacy data global $_variable_key, $_variable_value; set_error_handler("wfUnserializeHandler"); $_variable_key = $oRow->cv_name; $_variable_value = $oRow->cv_value; $tUnserVal = unserialize($oRow->cv_value); restore_error_handler(); if (!empty($wgDevelEnvironment) && $oRow->cv_name === "wgServer") { /** * skip this variable */ unset($this->mVariables[$oRow->cv_name]); $this->debug("{$oRow->cv_name} with value {$tUnserVal} skipped"); } else { $this->mVariables[$oRow->cv_name] = $tUnserVal; } } $dbr->freeResult($oRes); /** * wgArticlePath */ if (!isset($this->mVariables['wgArticlePath'])) { $this->mVariables['wgArticlePath'] = $GLOBALS['wgArticlePath']; } /** * read tags for this wiki, store in global variable as array * @name $wgWikiFactoryTags */ wfProfileIn(__METHOD__ . "-tagsdb"); $this->mVariables["wgWikiFactoryTags"] = array(); $sth = $dbr->select(array("city_tag", "city_tag_map"), array("id", "name"), array("city_tag.id = city_tag_map.tag_id", "city_id = {$this->mWikiID}"), __METHOD__ . '::tagsdb'); while ($row = $dbr->fetchObject($sth)) { $this->mVariables["wgWikiFactoryTags"][$row->id] = $row->name; } $dbr->freeResult($sth); $this->debug("reading tags from database, id {$this->mWikiID}, count " . count($this->mVariables["wgWikiFactoryTags"])); wfProfileOut(__METHOD__ . "-tagsdb"); if (empty($this->mAlwaysFromDB)) { /** * cache as well some values even if they are not defined in database * it will prevent GlobalTitle from doing empty selects * BugId: 12463 */ foreach ($this->mCacheAnyway as $cvar) { if (!isset($this->mVariables[$cvar]) && isset($GLOBALS[$cvar])) { $this->mVariables[$cvar] = $GLOBALS[$cvar]; } } /** * store values in memcache */ $oMemc->set(WikiFactory::getVarsKey($this->mWikiID), array("stamp" => $this->mTimestamp, "data" => $this->mVariables), $this->mExpireValuesCacheTimeout); } $this->debug("reading from database, id {$this->mWikiID}, count " . count($this->mVariables)); wfProfileOut(__METHOD__ . "-varsdb"); /** * maybe upgrade database to current schema */ if ($this->mCheckUpgrade === true) { $this->maybeUpgrade(); } } // @author macbre wfRunHooks('WikiFactory::executeBeforeTransferToGlobals', array(&$this)); /** * transfer configuration variables from database to GLOBALS */ if (is_array($this->mVariables)) { foreach ($this->mVariables as $key => $value) { $tValue = $value; #--- check, maybe there are variables in variable if (is_string($tValue)) { preg_match_all('/(\\$\\w+)[^\\w]*/', $tValue, $aMatches); if (is_array($aMatches[1])) { foreach ($aMatches[1] as $tKey) { /** * dolar sign in key should be removed * (str_replace is faster than regexp) */ $tKeyParsed = str_replace('$', '', $tKey); if (!is_numeric($tKeyParsed)) { #--- replace only if key is not $1, $2 etc. if (array_key_exists($tKeyParsed, $this->mVariables)) { $tValue = str_replace($tKey, $this->mVariables[$tKeyParsed], $tValue); } else { if (isset($GLOBALS[$tKeyParsed])) { $tValue = str_replace($tKey, $GLOBALS[$tKeyParsed], $tValue); } } } } } } /** * merge local values with global */ switch ($key) { case "wgNamespacesWithSubpagesLocal": $this->LocalToGlobalArray($tValue, $GLOBALS["wgNamespacesWithSubpages"]); break; case "wgExtraNamespacesLocal": $this->LocalToGlobalArray($tValue, $GLOBALS["wgExtraNamespaces"]); break; case "wgFileExtensionsLocal": $this->LocalToGlobalArray($tValue, $GLOBALS["wgFileExtensions"], true); break; case "wgTrustedMediaFormatsLocal": $this->LocalToGlobalArray($tValue, $GLOBALS["wgTrustedMediaFormats"]); break; case "wgFileBlacklistLocal": $this->LocalToGlobalArray($tValue, $GLOBALS["wgFileBlacklist"]); break; } if ($key == 'wgServer') { $headers = Wikia::getAllHeaders(); if (array_key_exists('X-Original-Host', $headers) && !empty($headers['X-Original-Host'])) { global $wgConf; $tValue = 'http://' . $headers['X-Original-Host']; $wgConf->localVHosts = array_merge($wgConf->localVHosts, array($headers['X-Original-Host'])); } } try { if ($this->mSaveDefaults) { $GLOBALS['wgPreWikiFactoryValues'][$key] = $tValue; } $GLOBALS[$key] = $tValue; } catch (Exception $e) { #--- so far do nothing } } } $wgCityId = $this->mWikiID; /** * set/replace $wgDBname in $wgDBservers */ if (isset($wgDBservers) && is_array($wgDBservers) && isset($this->mVariables["wgDBname"])) { foreach ($wgDBservers as $index => $server) { $wgDBservers[$index]["dbname"] = $this->mVariables["wgDBname"]; } } if (isset($wgLBFactoryConf) && is_array($wgLBFactoryConf) && isset($this->mVariables["wgDBname"])) { $wgLBFactoryConf['serverTemplate']['dbname'] = $this->mVariables["wgDBname"]; /** * set wgDBserver for cluster based on $wgLBFactoryConf */ $cluster = isset($this->mVariables["wgDBcluster"]) ? $this->mVariables["wgDBcluster"] : "DEFAULT"; if (isset($wgLBFactoryConf["sectionLoads"][$cluster])) { $keys = array_keys($wgLBFactoryConf["sectionLoads"][$cluster]); $db = array_shift($keys); if (isset($wgLBFactoryConf["hostsByName"][$db])) { $wgDBserver = $wgLBFactoryConf["hostsByName"][$db]; $this->debug("wgDBserver for cluster {$cluster} set to {$wgDBserver}"); } } } wfRunHooks('WikiFactory::onExecuteComplete', array(&$this)); wfProfileOut(__METHOD__); /** * cleanup and finally return wiki id */ LBFactory::destroyInstance(); return $this->mWikiID; }
/** * Constructor, always call this from child classes. */ public function __construct() { global $wgExtensionMessagesFiles, $wgUser; // Disable the i18n cache and LoadBalancer Language::getLocalisationCache()->disableBackend(); LBFactory::disableBackend(); // Load the installer's i18n file. $wgExtensionMessagesFiles['MediawikiInstaller'] = dirname(__FILE__) . '/Installer.i18n.php'; // Having a user with id = 0 safeguards us from DB access via User::loadOptions(). $wgUser = User::newFromId(0); $this->settings = $this->internalDefaults; foreach ($this->defaultVarNames as $var) { $this->settings[$var] = $GLOBALS[$var]; } $compiledDBs = array(); foreach (self::getDBTypes() as $type) { $installer = $this->getDBInstaller($type); if (!$installer->isCompiled()) { continue; } $compiledDBs[] = $type; $defaults = $installer->getGlobalDefaults(); foreach ($installer->getGlobalNames() as $var) { if (isset($defaults[$var])) { $this->settings[$var] = $defaults[$var]; } else { $this->settings[$var] = $GLOBALS[$var]; } } } $this->setVar('_CompiledDBs', $compiledDBs); $this->parserTitle = Title::newFromText('Installer'); $this->parserOptions = new ParserOptions(); // language will be wrong :( $this->parserOptions->setEditSection(false); }
/** * Set up LBFactory so that wfGetDB() etc. works. * We set up a special LBFactory instance which returns the current * installer connection. */ public function enableLB() { $status = $this->getConnection(); if (!$status->isOK()) { throw new MWException(__METHOD__ . ': unexpected DB connection error'); } LBFactory::setInstance(new LBFactory_Single(array('connection' => $status->value))); }
/** * Issue a commit on all masters who are currently in a transaction and have * made changes to the database. It also supports sometimes waiting for the * local wiki's replica DBs to catch up. See the documentation for * $wgJobSerialCommitThreshold for more. * * @param LBFactory $lbFactory * @param Job $job * @param string $fnameTrxOwner * @throws DBError */ private function commitMasterChanges(LBFactory $lbFactory, Job $job, $fnameTrxOwner) { global $wgJobSerialCommitThreshold; $time = false; $lb = $lbFactory->getMainLB(wfWikiID()); if ($wgJobSerialCommitThreshold !== false && $lb->getServerCount() > 1) { // Generally, there is one master connection to the local DB $dbwSerial = $lb->getAnyOpenConnection($lb->getWriterIndex()); // We need natively blocking fast locks if ($dbwSerial && $dbwSerial->namedLocksEnqueue()) { $time = $dbwSerial->pendingWriteQueryDuration($dbwSerial::ESTIMATE_DB_APPLY); if ($time < $wgJobSerialCommitThreshold) { $dbwSerial = false; } } else { $dbwSerial = false; } } else { // There are no replica DBs or writes are all to foreign DB (we don't handle that) $dbwSerial = false; } if (!$dbwSerial) { $lbFactory->commitMasterChanges($fnameTrxOwner); return; } $ms = intval(1000 * $time); $msg = $job->toString() . " COMMIT ENQUEUED [{$ms}ms of writes]"; $this->logger->info($msg); $this->debugCallback($msg); // Wait for an exclusive lock to commit if (!$dbwSerial->lock('jobrunner-serial-commit', __METHOD__, 30)) { // This will trigger a rollback in the main loop throw new DBError($dbwSerial, "Timed out waiting on commit queue."); } $unlocker = new ScopedCallback(function () use($dbwSerial) { $dbwSerial->unlock('jobrunner-serial-commit', __METHOD__); }); // Wait for the replica DBs to catch up $pos = $lb->getMasterPos(); if ($pos) { $lb->waitForAll($pos); } // Actually commit the DB master changes $lbFactory->commitMasterChanges($fnameTrxOwner); ScopedCallback::consume($unlocker); }
/** * Set the instance to be the given object * * @param LBFactory $instance */ static function setInstance($instance) { self::destroyInstance(); self::$instance = $instance; }
/** * @param LBFactory $lbFactory * @param WikiPage $page * @param Revision $newRev * @throws MWException */ protected function notifyUpdatesForRevision(LBFactory $lbFactory, WikiPage $page, Revision $newRev) { $config = RequestContext::getMain()->getConfig(); $title = $page->getTitle(); // Get the new revision if (!$newRev->getContent()) { return; // deleted? } // Get the prior revision (the same for null edits) if ($newRev->getParentId()) { $oldRev = Revision::newFromId($newRev->getParentId(), Revision::READ_LATEST); if (!$oldRev->getContent()) { return; // deleted? } } else { $oldRev = null; } // Parse the new revision and get the categories $categoryChanges = $this->getExplicitCategoriesChanges($title, $newRev, $oldRev); list($categoryInserts, $categoryDeletes) = $categoryChanges; if (!$categoryInserts && !$categoryDeletes) { return; // nothing to do } $catMembChange = new CategoryMembershipChange($title, $newRev); $catMembChange->checkTemplateLinks(); $batchSize = $config->get('UpdateRowsPerQuery'); $insertCount = 0; foreach ($categoryInserts as $categoryName) { $categoryTitle = Title::makeTitle(NS_CATEGORY, $categoryName); $catMembChange->triggerCategoryAddedNotification($categoryTitle); if ($insertCount++ && $insertCount % $batchSize == 0) { $lbFactory->commitAndWaitForReplication(__METHOD__, $this->ticket); } } foreach ($categoryDeletes as $categoryName) { $categoryTitle = Title::makeTitle(NS_CATEGORY, $categoryName); $catMembChange->triggerCategoryRemovedNotification($categoryTitle); if ($insertCount++ && $insertCount++ % $batchSize == 0) { $lbFactory->commitAndWaitForReplication(__METHOD__, $this->ticket); } } }
public function __construct(array $conf) { parent::__construct($conf); $this->chronProt = new ChronologyProtector(); $this->loadMonitorClass = isset($conf['loadMonitorClass']) ? $conf['loadMonitorClass'] : null; }
/** * Constructor, always call this from child classes. */ public function __construct() { global $wgMessagesDirs, $wgUser; // Disable the i18n cache Language::getLocalisationCache()->disableBackend(); // Disable LoadBalancer and wfGetDB etc. LBFactory::disableBackend(); // Disable object cache (otherwise CACHE_ANYTHING will try CACHE_DB and // SqlBagOStuff will then throw since we just disabled wfGetDB) $GLOBALS['wgMemc'] = new EmptyBagOStuff(); ObjectCache::clear(); $emptyCache = array('class' => 'EmptyBagOStuff'); $GLOBALS['wgObjectCaches'] = array(CACHE_NONE => $emptyCache, CACHE_DB => $emptyCache, CACHE_ANYTHING => $emptyCache, CACHE_MEMCACHED => $emptyCache); // Load the installer's i18n. $wgMessagesDirs['MediawikiInstaller'] = __DIR__ . '/i18n'; // Having a user with id = 0 safeguards us from DB access via User::loadOptions(). $wgUser = User::newFromId(0); $this->settings = $this->internalDefaults; foreach ($this->defaultVarNames as $var) { $this->settings[$var] = $GLOBALS[$var]; } $this->doEnvironmentPreps(); $this->compiledDBs = array(); foreach (self::getDBTypes() as $type) { $installer = $this->getDBInstaller($type); if (!$installer->isCompiled()) { continue; } $this->compiledDBs[] = $type; } $this->parserTitle = Title::newFromText('Installer'); $this->parserOptions = new ParserOptions(); // language will be wrong :( $this->parserOptions->setEditSection(false); }
/** * @param array $conf * @throws MWException */ public function __construct(array $conf) { parent::__construct($conf); $this->chronProt = new ChronologyProtector(); $this->conf = $conf; $required = array('sectionsByDB', 'sectionLoads', 'serverTemplate'); $optional = array('groupLoadsBySection', 'groupLoadsByDB', 'hostsByName', 'externalLoads', 'externalTemplateOverrides', 'templateOverridesByServer', 'templateOverridesByCluster', 'masterTemplateOverrides', 'readOnlyBySection', 'loadMonitorClass'); foreach ($required as $key) { if (!isset($conf[$key])) { throw new MWException(__CLASS__ . ": {$key} is required in configuration"); } $this->{$key} = $conf[$key]; } foreach ($optional as $key) { if (isset($conf[$key])) { $this->{$key} = $conf[$key]; } } }
/** * Constructor, always call this from child classes. */ public function __construct() { global $wgMessagesDirs, $wgUser; // Disable the i18n cache and LoadBalancer Language::getLocalisationCache()->disableBackend(); LBFactory::disableBackend(); // Load the installer's i18n. $wgMessagesDirs['MediawikiInstaller'] = __DIR__ . '/i18n'; // Having a user with id = 0 safeguards us from DB access via User::loadOptions(). $wgUser = User::newFromId(0); $this->settings = $this->internalDefaults; foreach ($this->defaultVarNames as $var) { $this->settings[$var] = $GLOBALS[$var]; } $this->compiledDBs = array(); foreach (self::getDBTypes() as $type) { $installer = $this->getDBInstaller($type); if (!$installer->isCompiled()) { continue; } $this->compiledDBs[] = $type; } $this->parserTitle = Title::newFromText('Installer'); $this->parserOptions = new ParserOptions(); // language will be wrong :( $this->parserOptions->setEditSection(false); }