%PDF- %PDF-
Direktori : /www/varak.net/wiki.varak.net/extensions/CirrusSearch/includes/Job/ |
Current File : /www/varak.net/wiki.varak.net/extensions/CirrusSearch/includes/Job/Job.php |
<?php namespace CirrusSearch\Job; use CirrusSearch\Connection; use CirrusSearch\Updater; use CirrusSearch\SearchConfig; use Job as MWJob; use JobQueueGroup; use MediaWiki\Logger\LoggerFactory; use MediaWiki\MediaWikiServices; use Title; /** * Abstract job class used by all CirrusSearch*Job classes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html */ abstract class Job extends MWJob { /** * @var Connection */ protected $connection; /** * @var SearchConfig */ protected $searchConfig; /** * @var bool should we retry if this job failed */ private $allowRetries = true; /** * Job constructor. * * @param Title $title * @param array $params */ public function __construct( $title, $params ) { $params += [ 'cluster' => null ]; // eg: DeletePages -> cirrusSearchDeletePages $jobName = 'cirrusSearch' . str_replace( 'CirrusSearch\\Job\\', '', static::class ); parent::__construct( $jobName, $title, $params ); // All CirrusSearch jobs are reasonably expensive. Most involve parsing and it // is ok to remove duplicate _unclaimed_ cirrus jobs. Once a cirrus job is claimed // it can't be deduplicated or else the search index will end up with out of date // data. Luckily, this is how the JobQueue implementations work. $this->removeDuplicates = true; $this->searchConfig = MediaWikiServices::getInstance() ->getConfigFactory() ->makeConfig( 'CirrusSearch' ); // When the 'cluster' parameter is provided the job must only operate on // the specified cluster, take special care to ensure nested jobs get the // correct cluster set. When set to null all clusters should be written to. $this->connection = Connection::getPool( $this->searchConfig, $params['cluster'] ); } public function setConnection( Connection $connection ) { $this->connection = $connection; } /** * Some boilerplate stuff for all jobs goes here */ public function run() { global $wgDisableSearchUpdate, $wgPoolCounterConf; if ( $wgDisableSearchUpdate ) { return; } // Make sure we don't flood the pool counter. This is safe since this is only used // to batch update wikis and we don't want to subject those to the pool counter. $backupPoolCounterSearch = null; if ( isset( $wgPoolCounterConf['CirrusSearch-Search'] ) ) { $backupPoolCounterSearch = $wgPoolCounterConf['CirrusSearch-Search']; unset( $wgPoolCounterConf['CirrusSearch-Search'] ); } $backupPoolCounterPerUser = null; if ( isset( $wgPoolCounterConf['CirrusSearch-PerUser'] ) ) { $backupPoolCounterPerUser = $wgPoolCounterConf['CirrusSearch-PerUser']; unset( $wgPoolCounterConf['CirrusSearch-PerUser'] ); } try { $ret = $this->doJob(); } finally { // Restore the pool counter settings in case other jobs need them if ( $backupPoolCounterSearch ) { $wgPoolCounterConf['CirrusSearch-Search'] = $backupPoolCounterSearch; } if ( $backupPoolCounterPerUser ) { $wgPoolCounterConf['CirrusSearch-PerUser'] = $backupPoolCounterSearch; } } return $ret; } /** * Set a delay for this job. Note that this might not be possible the JobQueue * implementation handling this job doesn't support it (JobQueueDB) but is possible * for the high performance JobQueueRedis. Note also that delays are minimums - * at least JobQueueRedis makes no effort to remove the delay as soon as possible * after it has expired. By default it only checks every five minutes or so. * Note yet again that if another delay has been set that is longer then this one * then the _longer_ delay stays. * * @param int $delay seconds to delay this job if possible */ public function setDelay( $delay ) { $jobQueue = JobQueueGroup::singleton()->get( $this->getType() ); if ( !$delay || !$jobQueue->delayedJobsEnabled() ) { return; } $oldTime = $this->getReleaseTimestamp(); $newTime = time() + $delay; if ( $oldTime != null && $oldTime >= $newTime ) { return; } $this->params[ 'jobReleaseTimestamp' ] = $newTime; } /** * Create an Updater instance that will respect cluster configuration * settings of this job. * * @return Updater */ protected function createUpdater() { $flags = []; if ( isset( $this->params['cluster'] ) ) { $flags[] = 'same-cluster'; } return new Updater( $this->connection, $this->searchConfig, $flags ); } /** * Actually perform the labor of the job */ abstract protected function doJob(); /** * {@inheritDoc} */ public function allowRetries() { return $this->allowRetries; } /** * @param bool $allowRetries Whether this job should be retried if it fails */ protected function setAllowRetries( $allowRetries ) { $this->allowRetries = $allowRetries; } /** * @param int $retryCount The number of times the job has errored out. * @return int Number of seconds to delay. With the default minimum exponent * of 6 the possible return values are 64, 128, 256, 512 and 1024 giving a * maximum delay of 17 minutes. */ public static function backoffDelay( $retryCount ) { global $wgCirrusSearchWriteBackoffExponent; return ceil( pow( 2, $wgCirrusSearchWriteBackoffExponent + rand(0, min( $retryCount, 4 ) ) ) ); } /** * Construct the list of connections suited for this job. * NOTE: only suited for jobs that work on multiple clusters by * inspecting the 'cluster' job param * * @return Connection[] indexed by cluster name */ protected function decideClusters() { $cluster = isset ( $this->params['cluster'] ) ? $this->params['cluster'] : null; if ( $cluster === null ) { $conns = Connection::getWritableClusterConnections( $this->searchConfig ); } else { if ( !$this->searchConfig->canWriteToCluster( $cluster ) ) { // Just in case a job is present in the queue but its cluster // has been removed from the config file. LoggerFactory::getInstance( 'CirrusSearch' )->warning( "Received {command} job for unwritable cluster {cluster}", [ 'command' => $this->command, 'cluster' => $cluster ] ); // this job does not allow retries so we just need to throw an exception throw new \RuntimeException( "Received {$this->command} job for an unwritable cluster $cluster." ); } $conns = [ $cluster => Connection::getPool( $this->searchConfig, $cluster ) ]; } $timeout = $this->searchConfig->get( 'CirrusSearchClientSideUpdateTimeout' ); foreach ( $conns as $connection ) { $connection->setTimeout( $timeout ); } return $conns; } }