41 const PARSE_THRESHOLD_SEC = 1.0;
43 const CLOCK_FUDGE = 10;
45 const LAG_WAIT_TIMEOUT = 15;
50 $this->removeDuplicates = (
56 $this->params += [
'causeAction' =>
'unknown',
'causeAgent' =>
'unknown' ];
58 $this->executionFlags |= self::JOB_NO_EXPLICIT_TRX_ROUND;
68 $job->command =
'refreshLinksPrioritized';
80 $job->command =
'refreshLinksDynamic';
90 if ( !empty( $this->params[
'recursive'] ) ) {
94 if ( !isset( $this->params[
'range'] ) ) {
95 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
96 if ( !$lbFactory->waitForReplication( [
97 'domain' => $lbFactory->getLocalDomainID(),
98 'timeout' => self::LAG_WAIT_TIMEOUT
100 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
101 $stats->increment(
'refreshlinks.lag_wait_failed' );
106 $extraParams[
'triggeredRecursive'] =
true;
108 $extraParams[
'causeAction'] = $this->params[
'causeAction'];
109 $extraParams[
'causeAgent'] = $this->params[
'causeAgent'];
116 [
'params' => $extraParams ]
120 } elseif ( isset( $this->params[
'pages'] ) ) {
121 foreach ( $this->params[
'pages']
as list( $ns, $dbKey ) ) {
143 $services = MediaWikiServices::getInstance();
144 $stats =
$services->getStatsdDataFactory();
145 $lbFactory =
$services->getDBLoadBalancerFactory();
146 $revisionStore =
$services->getRevisionStore();
147 $renderer =
$services->getRevisionRenderer();
148 $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
150 $lbFactory->beginMasterChanges( __METHOD__ );
153 $page->loadPageData( WikiPage::READ_LATEST );
156 $dbw = $lbFactory->getMainLB()->getConnection(
DB_MASTER );
159 if ( $scopedLock ===
null ) {
160 $lbFactory->commitMasterChanges( __METHOD__ );
162 $this->
setLastError(
'LinksUpdate already running for this page, try again later.' );
170 if ( !empty( $this->params[
'triggeringRevisionId'] ) ) {
173 $revision = $revisionStore->getRevisionById(
174 (
int)$this->params[
'triggeringRevisionId'],
175 Revision::READ_LATEST
179 $revision = $revisionStore->getRevisionByTitle(
$title, 0, Revision::READ_LATEST );
183 $lbFactory->commitMasterChanges( __METHOD__ );
184 $stats->increment(
'refreshlinks.rev_not_found' );
185 $this->
setLastError(
"Revision not found for {$title->getPrefixedDBkey()}" );
187 } elseif ( $revision->getId() != $latest || $revision->getPageId() !== $page->getId() ) {
188 $lbFactory->commitMasterChanges( __METHOD__ );
193 $stats->increment(
'refreshlinks.rev_not_current' );
194 $this->
setLastError(
"Revision {$revision->getId()} is not current" );
198 $parserOutput =
false;
199 $parserOptions = $page->makeParserOptions(
'canonical' );
203 if ( isset( $this->params[
'rootJobTimestamp'] ) ) {
204 $opportunistic = !empty( $this->params[
'isOpportunistic'] );
206 $skewedTimestamp = $this->params[
'rootJobTimestamp'];
207 if ( $opportunistic ) {
213 wfTimestamp( TS_UNIX, $skewedTimestamp ) + self::CLOCK_FUDGE
217 if ( $page->getLinksTimestamp() > $skewedTimestamp ) {
218 $lbFactory->commitMasterChanges( __METHOD__ );
220 $stats->increment(
'refreshlinks.update_skipped' );
224 if ( $page->getTouched() >= $this->params[
'rootJobTimestamp'] || $opportunistic ) {
227 $parserOutput =
$services->getParserCache()->getDirty( $page, $parserOptions );
229 || $parserOutput->getCacheRevisionId() != $revision->getId()
230 || $parserOutput->getCacheTime() < $skewedTimestamp
232 $parserOutput =
false;
238 if ( $parserOutput ) {
239 $stats->increment(
'refreshlinks.parser_cached' );
241 $start = microtime(
true );
243 $checkCache = $page->shouldCheckParserCache( $parserOptions, $revision->getId() );
246 $renderedRevision = $renderer->getRenderedRevision(
252 'use-master' =>
true,
254 'audience' => RevisionRecord::RAW
257 $parserOutput = $renderedRevision->getRevisionParserOutput(
259 [
'generate-html' => $checkCache ]
265 $elapsed = microtime(
true ) - $start;
267 $parseThreshold = $this->params[
'parseThreshold'] ?? self::PARSE_THRESHOLD_SEC;
269 if ( $checkCache && $elapsed >= $parseThreshold && $parserOutput->isCacheable() ) {
272 $parserOutput, $page, $parserOptions, $ctime, $revision->getId()
275 $stats->increment(
'refreshlinks.parser_uncached' );
279 'recursive' => !empty( $this->params[
'useRecursiveLinksUpdate'] ),
281 'causeAction' => $this->params[
'causeAction'],
282 'causeAgent' => $this->params[
'causeAgent'],
284 'transactionTicket' => $ticket,
286 if ( !empty( $this->params[
'triggeringUser'] ) ) {
287 $userInfo = $this->params[
'triggeringUser'];
288 if ( $userInfo[
'userId'] ) {
296 $lbFactory->commitMasterChanges( __METHOD__ );
298 $page->doSecondaryDataUpdates(
$options );
304 $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket );
310 $info = parent::getDeduplicationInfo();
311 unset( $info[
'causeAction'] );
312 unset( $info[
'causeAgent'] );
313 if ( is_array( $info[
'params'] ) ) {
316 if ( isset( $info[
'params'][
'pages'] ) ) {
317 unset( $info[
'namespace'] );
318 unset( $info[
'title'] );
326 if ( !empty( $this->params[
'recursive'] ) ) {
328 } elseif ( isset( $this->params[
'pages'] ) ) {
329 return count( $this->params[
'pages'] );