diff options
Diffstat (limited to 'includes/poolcounter')
-rw-r--r-- | includes/poolcounter/PoolCounter.php | 57 | ||||
-rw-r--r-- | includes/poolcounter/PoolCounterRedis.php | 17 | ||||
-rw-r--r-- | includes/poolcounter/PoolWorkArticleView.php | 7 |
3 files changed, 74 insertions, 7 deletions
diff --git a/includes/poolcounter/PoolCounter.php b/includes/poolcounter/PoolCounter.php index e77ffd7c..5692d731 100644 --- a/includes/poolcounter/PoolCounter.php +++ b/includes/poolcounter/PoolCounter.php @@ -34,7 +34,10 @@ * minutes and hundreds of read hits. * * The PoolCounter provides semaphore semantics for restricting the number - * of workers that may be concurrently performing such single task. + * of workers that may be concurrently performing such single task. Only one + * key can be locked by any PoolCounter instance of a process, except for keys + * that start with "nowait:". However, only 0 timeouts (non-blocking requests) + * can be used with "nowait:" keys. * * By default PoolCounter_Stub is used, which provides no locking. You * can get a useful one in the PoolCounter extension. @@ -68,6 +71,15 @@ abstract class PoolCounter { protected $timeout; /** + * @var boolean Whether the key is a "might wait" key + */ + private $isMightWaitKey; + /** + * @var boolean Whether this process holds a "might wait" lock key + */ + private static $acquiredMightWaitKey = 0; + + /** * @param array $conf * @param string $type * @param string $key @@ -84,6 +96,7 @@ abstract class PoolCounter { $key = $this->hashKeyIntoSlots( $key, $this->slots ); } $this->key = $key; + $this->isMightWaitKey = !preg_match( '/^nowait:/', $this->key ); } /** @@ -137,6 +150,48 @@ abstract class PoolCounter { abstract public function release(); /** + * Checks that the lock request is sane. + * @return Status - good for sane requests fatal for insane + * @since 1.25 + */ + final protected function precheckAcquire() { + if ( $this->isMightWaitKey ) { + if ( self::$acquiredMightWaitKey ) { + /* + * The poolcounter itself is quite happy to allow you to wait + * on another lock while you have a lock you waited on already + * but we think that it is unlikely to be a good idea. So we + * made it an error. If you are _really_ _really_ sure it is a + * good idea then feel free to implement an unsafe flag or + * something. + */ + return Status::newFatal( 'poolcounter-usage-error', + 'You may only aquire a single non-nowait lock.' ); + } + } elseif ( $this->timeout !== 0 ) { + return Status::newFatal( 'poolcounter-usage-error', + 'Locks starting in nowait: must have 0 timeout.' ); + } + return Status::newGood(); + } + + /** + * Update any lock tracking information when the lock is acquired + * @since 1.25 + */ + final protected function onAcquire() { + self::$acquiredMightWaitKey |= $this->isMightWaitKey; + } + + /** + * Update any lock tracking information when the lock is released + * @since 1.25 + */ + final protected function onRelease() { + self::$acquiredMightWaitKey &= !$this->isMightWaitKey; + } + + /** * Given a key (any string) and the number of lots, returns a slot number (an integer from the [0..($slots-1)] range). * This is used for a global limit on the number of instances of a given type that can acquire a lock. * The hashing is deterministic so that PoolCounter::$workers is always an upper limit of how many instances with diff --git a/includes/poolcounter/PoolCounterRedis.php b/includes/poolcounter/PoolCounterRedis.php index d609f614..98797a30 100644 --- a/includes/poolcounter/PoolCounterRedis.php +++ b/includes/poolcounter/PoolCounterRedis.php @@ -121,19 +121,26 @@ class PoolCounterRedis extends PoolCounter { } function acquireForMe() { - $section = new ProfileSection( __METHOD__ ); + + $status = $this->precheckAcquire(); + if ( !$status->isGood() ) { + return $status; + } return $this->waitForSlotOrNotif( self::AWAKE_ONE ); } function acquireForAnyone() { - $section = new ProfileSection( __METHOD__ ); + + $status = $this->precheckAcquire(); + if ( !$status->isGood() ) { + return $status; + } return $this->waitForSlotOrNotif( self::AWAKE_ALL ); } function release() { - $section = new ProfileSection( __METHOD__ ); if ( $this->slot === null ) { return Status::newGood( PoolCounter::NOT_LOCKED ); // not locked @@ -207,6 +214,8 @@ LUA; $this->onRelease = null; unset( self::$active[$this->session] ); + $this->onRelease(); + return Status::newGood( PoolCounter::RELEASED ); } @@ -266,6 +275,8 @@ LUA; self::$active[$this->session] = $this; } + $this->onAcquire(); + return Status::newGood( $slot === 'w' ? PoolCounter::DONE : PoolCounter::LOCKED ); } diff --git a/includes/poolcounter/PoolWorkArticleView.php b/includes/poolcounter/PoolWorkArticleView.php index 5e7e3912..a702d2e8 100644 --- a/includes/poolcounter/PoolWorkArticleView.php +++ b/includes/poolcounter/PoolWorkArticleView.php @@ -67,7 +67,8 @@ class PoolWorkArticleView extends PoolCounterWork { $this->parserOptions = $parserOptions; $this->content = $content; $this->cacheKey = ParserCache::singleton()->getKey( $page, $parserOptions ); - parent::__construct( 'ArticleView', $this->cacheKey . ':revid:' . $revid ); + $keyPrefix = $this->cacheKey ?: wfMemcKey( 'articleview', 'missingcachekey' ); + parent::__construct( 'ArticleView', $keyPrefix . ':revid:' . $revid ); } /** @@ -153,12 +154,12 @@ class PoolWorkArticleView extends PoolCounterWork { // Make sure file cache is not used on uncacheable content. // Output that has magic words in it can still use the parser cache // (if enabled), though it will generally expire sooner. - if ( !$this->parserOutput->isCacheable() || $this->parserOutput->containsOldMagic() ) { + if ( !$this->parserOutput->isCacheable() ) { $wgUseFileCache = false; } if ( $isCurrent ) { - $this->page->doCascadeProtectionUpdates( $this->parserOutput ); + $this->page->triggerOpportunisticLinksUpdate( $this->parserOutput ); } return true; |