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' ];
66 $job->command =
'refreshLinksPrioritized';
78 $job->command =
'refreshLinksDynamic';
87 if ( !empty( $this->params[
'recursive'] ) ) {
91 if ( !isset( $this->params[
'range'] ) ) {
92 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
93 if ( !$lbFactory->waitForReplication( [
94 'domain' => $lbFactory->getLocalDomainID(),
95 'timeout' => self::LAG_WAIT_TIMEOUT
97 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
98 $stats->increment(
'refreshlinks.lag_wait_failed' );
103 $extraParams[
'triggeredRecursive'] =
true;
105 $extraParams[
'causeAction'] = $this->params[
'causeAction'];
106 $extraParams[
'causeAgent'] = $this->params[
'causeAgent'];
113 [
'params' => $extraParams ]
117 } elseif ( isset( $this->params[
'pages'] ) ) {
118 foreach ( $this->params[
'pages']
as $nsAndKey ) {
119 list( $ns, $dbKey ) = $nsAndKey;
135 $services = MediaWikiServices::getInstance();
136 $stats =
$services->getStatsdDataFactory();
137 $lbFactory =
$services->getDBLoadBalancerFactory();
138 $revisionStore =
$services->getRevisionStore();
139 $renderer =
$services->getRevisionRenderer();
140 $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
143 $page->loadPageData( WikiPage::READ_LATEST );
146 $dbw = $lbFactory->getMainLB()->getConnection(
DB_MASTER );
149 if ( $scopedLock ===
null ) {
151 $this->
setLastError(
'LinksUpdate already running for this page, try again later.' );
159 if ( !empty( $this->params[
'triggeringRevisionId'] ) ) {
162 $revision = $revisionStore->getRevisionById(
163 (
int)$this->params[
'triggeringRevisionId'],
164 Revision::READ_LATEST
168 $revision = $revisionStore->getRevisionByTitle(
$title, 0, Revision::READ_LATEST );
172 $stats->increment(
'refreshlinks.rev_not_found' );
173 $this->
setLastError(
"Revision not found for {$title->getPrefixedDBkey()}" );
175 } elseif ( $revision->getId() != $latest || $revision->getPageId() !== $page->getId() ) {
180 $stats->increment(
'refreshlinks.rev_not_current' );
181 $this->
setLastError(
"Revision {$revision->getId()} is not current" );
185 $parserOutput =
false;
186 $parserOptions = $page->makeParserOptions(
'canonical' );
190 if ( isset( $this->params[
'rootJobTimestamp'] ) ) {
191 $opportunistic = !empty( $this->params[
'isOpportunistic'] );
193 $skewedTimestamp = $this->params[
'rootJobTimestamp'];
194 if ( $opportunistic ) {
200 wfTimestamp( TS_UNIX, $skewedTimestamp ) + self::CLOCK_FUDGE
204 if ( $page->getLinksTimestamp() > $skewedTimestamp ) {
206 $stats->increment(
'refreshlinks.update_skipped' );
210 if ( $page->getTouched() >= $this->params[
'rootJobTimestamp'] || $opportunistic ) {
213 $parserOutput =
$services->getParserCache()->getDirty( $page, $parserOptions );
215 || $parserOutput->getCacheRevisionId() != $revision->getId()
216 || $parserOutput->getCacheTime() < $skewedTimestamp
218 $parserOutput =
false;
224 if ( $parserOutput ) {
225 $stats->increment(
'refreshlinks.parser_cached' );
227 $start = microtime(
true );
229 $checkCache = $page->shouldCheckParserCache( $parserOptions, $revision->getId() );
232 $renderedRevision = $renderer->getRenderedRevision(
238 'use-master' =>
true,
240 'audience' => RevisionRecord::RAW
243 $parserOutput = $renderedRevision->getRevisionParserOutput(
245 [
'generate-html' => $checkCache ]
251 $elapsed = microtime(
true ) - $start;
253 $parseThreshold = $this->params[
'parseThreshold'] ?? self::PARSE_THRESHOLD_SEC;
255 if ( $checkCache && $elapsed >= $parseThreshold && $parserOutput->isCacheable() ) {
258 $parserOutput, $page, $parserOptions, $ctime, $revision->getId()
261 $stats->increment(
'refreshlinks.parser_uncached' );
265 'recursive' => !empty( $this->params[
'useRecursiveLinksUpdate'] ),
267 'causeAction' => $this->params[
'causeAction'],
268 'causeAgent' => $this->params[
'causeAgent'],
270 'transactionTicket' => $ticket,
272 if ( !empty( $this->params[
'triggeringUser'] ) ) {
273 $userInfo = $this->params[
'triggeringUser'];
274 if ( $userInfo[
'userId'] ) {
281 $page->doSecondaryDataUpdates(
$options );
287 $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket );
293 $info = parent::getDeduplicationInfo();
294 unset( $info[
'causeAction'] );
295 unset( $info[
'causeAgent'] );
296 if ( is_array( $info[
'params'] ) ) {
299 if ( isset( $info[
'params'][
'pages'] ) ) {
300 unset( $info[
'namespace'] );
301 unset( $info[
'title'] );
309 if ( !empty( $this->params[
'recursive'] ) ) {
311 } elseif ( isset( $this->params[
'pages'] ) ) {
312 return count( $this->params[
'pages'] );