/docs/js
/images/[0-9a-f]
/images/archive
+/images/cache
/images/deleted
/images/lockdir
/images/temp
}
$msg = $job->toString() . " STARTING";
- $this->logger->info( $msg );
+ $this->logger->debug( $msg );
$this->debugCallback( $msg );
// Run the job...
class RefreshLinksJob extends Job {
const PARSE_THRESHOLD_SEC = 1.0;
+ const CLOCK_FUDGE = 10;
+
function __construct( $title, $params = '' ) {
parent::__construct( 'refreshLinks', $title, $params );
// A separate type is used just for cascade-protected backlinks
$parserOutput = false;
$parserOptions = $page->makeParserOptions( 'canonical' );
- // If page_touched changed after this root job (with a good slave lag skew factor),
- // then it is likely that any views of the pages already resulted in re-parses which
- // are now in cache. This can be reused to avoid expensive parsing in some cases.
+ // If page_touched changed after this root job, then it is likely that
+ // any views of the pages already resulted in re-parses which are now in
+ // cache. The cache can be reused to avoid expensive parsing in some cases.
if ( isset( $this->params['rootJobTimestamp'] ) ) {
- $skewedTimestamp = wfTimestamp( TS_UNIX, $this->params['rootJobTimestamp'] ) + 5;
- if ( $page->getLinksTimestamp() > wfTimestamp( TS_MW, $skewedTimestamp ) ) {
+ $opportunistic = !empty( $this->params['isOpportunistic'] );
+
+ $skewedTimestamp = $this->params['rootJobTimestamp'];
+ if ( $opportunistic ) {
+ // Neither clock skew nor DB snapshot/slave lag matter much for such
+ // updates; focus on reusing the (often recently updated) cache
+ } else {
+ // For transclusion updates, the template changes must be reflected
+ $skewedTimestamp = wfTimestamp( TS_MW,
+ wfTimestamp( TS_UNIX, $skewedTimestamp ) + self::CLOCK_FUDGE
+ );
+ }
+
+ if ( $page->getLinksTimestamp() > $skewedTimestamp ) {
// Something already updated the backlinks since this job was made
return true;
}
- if ( $page->getTouched() > wfTimestamp( TS_MW, $skewedTimestamp ) ) {
+
+ if ( $page->getTouched() >= $skewedTimestamp || $opportunistic ) {
+ // Something bumped page_touched since this job was made
+ // or the cache is otherwise suspected to be up-to-date
$parserOutput = ParserCache::singleton()->getDirty( $page, $parserOptions );
- if ( $parserOutput && $parserOutput->getCacheTime() <= $skewedTimestamp ) {
+ if ( $parserOutput && $parserOutput->getCacheTime() < $skewedTimestamp ) {
$parserOutput = false; // too stale
}
}
}
+
// Fetch the current revision and parse it if necessary...
if ( $parserOutput == false ) {
$start = microtime( true );
/**
* Fetch the value of a timestamp "check" key
*
+ * Note that "check" keys won't collide with other regular keys
+ *
* @param string $key
* @return float|bool TS_UNIX timestamp of the key; false if not present
*/
* avoid race conditions where dependent keys get updated with a
* stale value (e.g. from a DB slave).
*
+ * Note that "check" keys won't collide with other regular keys
+ *
* @see WANObjectCache::get()
*
* @param string $key Cache key
}
function pageCount( $image ) {
- $tree = $this->getMetaTree( $image );
- if ( !$tree ) {
- return false;
+ global $wgMemc;
+
+ $key = wfMemcKey( 'file-djvu', 'pageCount', $image->getSha1() );
+
+ $count = $wgMemc->get( $key );
+ if ( $count === false ) {
+ $tree = $this->getMetaTree( $image );
+ if ( !$tree ) {
+ return false;
+ }
+ $count = count( $tree->xpath( '//OBJECT' ) );
+ $wgMemc->set( $key, $count );
}
- return count( $tree->xpath( '//OBJECT' ) );
+ return $count;
}
function getPageDimensions( $image, $page ) {
- $tree = $this->getMetaTree( $image );
- if ( !$tree ) {
- return false;
- }
+ global $wgMemc;
- $o = $tree->BODY[0]->OBJECT[$page - 1];
- if ( $o ) {
- return array(
- 'width' => intval( $o['width'] ),
- 'height' => intval( $o['height'] )
- );
- } else {
- return false;
+ $key = wfMemcKey( 'file-djvu', 'dimensions', $image->getSha1() );
+
+ $dimsByPage = $wgMemc->get( $key );
+ if ( !is_array( $dimsByPage ) ) {
+ $tree = $this->getMetaTree( $image );
+ if ( !$tree ) {
+ return false;
+ }
+
+ $dimsByPage = array();
+ $count = count( $tree->xpath( '//OBJECT' ) );
+ for ( $i = 0; $i < $count; ++$i ) {
+ $o = $tree->BODY[0]->OBJECT[$i];
+ if ( $o ) {
+ $dimsByPage[$i] = array(
+ 'width' => (int)$o['width'],
+ 'height' => (int)$o['height']
+ );
+ } else {
+ $dimsByPage[$i] = false;
+ }
+ }
+
+ $wgMemc->set( $key, $dimsByPage );
}
+
+ $index = $page - 1; // MW starts pages at 1
+
+ return isset( $dimsByPage[$index] ) ? $dimsByPage[$index] : false;
}
/**
// Check if the last link refresh was before page_touched
if ( $this->getLinksTimestamp() < $this->getTouched() ) {
+ $params['isOpportunistic'] = true;
+ $params['rootJobTimestamp'] = $parserOutput->getCacheTime();
+
JobQueueGroup::singleton()->push( EnqueueJob::newFromLocalJobs(
new JobSpecification( 'refreshLinks', $params,
array( 'removeDuplicates' => true ), $this->mTitle )
if ( $this->tryRespondLastModified( $context, $ts ) ) {
return false; // output handled (buffers cleared)
}
+ // Send content type and cache headers
+ $this->sendResponseHeaders( $context, $ts, false );
+ $response = $fileCache->fetchText();
// Capture any PHP warnings from the output buffer and append them to the
// response in a comment if we're in debug mode.
if ( $context->getDebug() && strlen( $warnings = ob_get_contents() ) ) {
- $response = "/*\n$warnings\n*/\n" . $response;
+ $response = self::makeComment( $warnings ) . $response;
}
- // Send content type and cache headers
- $this->sendResponseHeaders( $context, $ts, false );
- $response = $fileCache->fetchText();
// Remove the output buffer and output the response
ob_end_clean();
echo $response . "\n/* Cached {$ts} */";
'displaytitle' => array( '1', 'MOSTRATITOLO', 'DISPLAYTITLE' ),
'language' => array( '0', '#LINGUA', '#LANGUAGE:' ),
'numberofadmins' => array( '1', 'NUMEROADMIN', 'NUMBEROFADMINS' ),
- 'special' => array( '0', 'speciale', 'special' ),
'tag' => array( '0', 'etichetta', 'tag' ),
'pagesincategory' => array( '1', 'PAGINEINCAT', 'PAGESINCATEGORY', 'PAGESINCAT' ),
'pagesize' => array( '1', 'DIMENSIONEPAGINA', 'PESOPAGINA', 'PAGESIZE' ),
'fullurl' => array( '0', 'URLEPLOTË', 'FULLURL:' ),
'language' => array( '0', '#GJUHA:', '#LANGUAGE:' ),
'numberofadmins' => array( '1', 'NUMRIIADMINISTRUESVE', 'NUMBEROFADMINS' ),
- 'special' => array( '0', 'speciale', 'special' ),
'hiddencat' => array( '1', '__KATEGORIEFSHEHUR__', '__HIDDENCAT__' ),
'pagesize' => array( '1', 'MADHËSIAEFAQES', 'PAGESIZE' ),
);