const ERROR_UNCACHEABLE = 'uncacheable';
public function execute() {
- $cache = ObjectCache::getLocalClusterInstance();
-
$user = $this->getUser();
$params = $this->extractRequestParams();
// The user will abort the AJAX request by pressing "save", so ignore that
ignore_user_abort( true );
+ // Use the master DB for fast blocking locks
+ $dbw = wfGetDB( DB_MASTER );
+
// Get a key based on the source text, format, and user preferences
$key = self::getStashKey( $title, $content, $user );
// De-duplicate requests on the same key
if ( $user->pingLimiter( 'stashedit' ) ) {
$status = 'ratelimited';
- } elseif ( $cache->lock( $key, 0, 30 ) ) {
- /** @noinspection PhpUnusedLocalVariableInspection */
- $unlocker = new ScopedCallback( function() use ( $cache, $key ) {
- $cache->unlock( $key );
- } );
+ } elseif ( $dbw->lock( $key, __METHOD__, 1 ) ) {
$status = self::parseAndStash( $page, $content, $user );
+ $dbw->unlock( $key, __METHOD__ );
} else {
$status = 'busy';
}
if ( $editInfo && $editInfo->output ) {
$key = self::getStashKey( $page->getTitle(), $content, $user );
+ // Let extensions add ParserOutput metadata or warm other caches
+ Hooks::run( 'ParserOutputStashForEdit', array( $page, $content, $editInfo->output ) );
+
list( $stashInfo, $ttl ) = self::buildStashValue(
$editInfo->pstContent, $editInfo->output, $editInfo->timestamp
);
if ( $stashInfo ) {
$ok = $cache->set( $key, $stashInfo, $ttl );
if ( $ok ) {
+
$logger->debug( "Cached parser output for key '$key'." );
return self::ERROR_NONE;
} else {
public static function checkCache( Title $title, Content $content, User $user ) {
$cache = ObjectCache::getLocalClusterInstance();
$logger = LoggerFactory::getInstance( 'StashEdit' );
+ $stats = RequestContext::getMain()->getStats();
$key = self::getStashKey( $title, $content, $user );
$editInfo = $cache->get( $key );
if ( !is_object( $editInfo ) ) {
$start = microtime( true );
// We ignore user aborts and keep parsing. Block on any prior parsing
- // so as to use it's results and make use of the time spent parsing.
- if ( $cache->lock( $key, 30, 30 ) ) {
+ // so as to use its results and make use of the time spent parsing.
+ // Skip this logic if there no master connection in case this method
+ // is called on an HTTP GET request for some reason.
+ $lb = wfGetLB();
+ $dbw = $lb->getAnyOpenConnection( $lb->getWriterIndex() );
+ if ( $dbw && $dbw->lock( $key, __METHOD__, 30 ) ) {
$editInfo = $cache->get( $key );
- $cache->unlock( $key );
- }
- $sec = microtime( true ) - $start;
- if ( $sec > .01 ) {
- $logger->warning( "Waited $sec seconds on '$key'." );
+ $dbw->unlock( $key, __METHOD__ );
}
+
+ $timeMs = 1000 * max( 0, microtime( true ) - $start );
+ $stats->timing( 'editstash.lock-wait-time', $timeMs );
}
if ( !is_object( $editInfo ) || !$editInfo->output ) {
+ $stats->increment( 'editstash.cache-misses' );
$logger->debug( "No cache value for key '$key'." );
return false;
}
$time = wfTimestamp( TS_UNIX, $editInfo->output->getTimestamp() );
if ( ( time() - $time ) <= 3 ) {
+ $stats->increment( 'editstash.cache-hits' );
$logger->debug( "Timestamp-based cache hit for key '$key'." );
return $editInfo; // assume nothing changed
}
}
if ( $changed || $res->numRows() != $templateUses ) {
+ $stats->increment( 'editstash.cache-misses' );
$logger->info( "Stale cache for key '$key'; template changed." );
return false;
}
}
if ( $changed || $res->numRows() != count( $files ) ) {
+ $stats->increment( 'editstash.cache-misses' );
$logger->info( "Stale cache for key '$key'; file changed." );
return false;
}
}
+ $stats->increment( 'editstash.cache-hits' );
$logger->debug( "Cache hit for key '$key'." );
return $editInfo;
return true;
}
+ function isWriteMode() {
+ return true;
+ }
+
function isInternal() {
return true;
}