summaryrefslogtreecommitdiff
path: root/includes/PoolCounter.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/PoolCounter.php')
-rw-r--r--includes/PoolCounter.php115
1 files changed, 73 insertions, 42 deletions
diff --git a/includes/PoolCounter.php b/includes/PoolCounter.php
index 3851767f..182520e1 100644
--- a/includes/PoolCounter.php
+++ b/includes/PoolCounter.php
@@ -4,9 +4,14 @@
* When you have many workers (threads/servers) giving service, and a
* cached item expensive to produce expires, you may get several workers
* doing the job at the same time.
+ *
* Given enough requests and the item expiring fast (non-cacheable,
* lots of edits...) that single work can end up unfairly using most (all)
- * of the cpu of the pool. This is also known as 'Michael Jackson effect'.
+ * of the cpu of the pool. This is also known as 'Michael Jackson effect'
+ * since this effect triggered on the english wikipedia on the day Michael
+ * Jackson died, the biographical article got hit with several edits per
+ * 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.
*
@@ -16,15 +21,15 @@
abstract class PoolCounter {
/* Return codes */
- const LOCKED = 1; /* Lock acquired */
+ const LOCKED = 1; /* Lock acquired */
const RELEASED = 2; /* Lock released */
- const DONE = 3; /* Another one did the work for you */
+ const DONE = 3; /* Another worker did the work for you */
- const ERROR = -1; /* Indeterminate error */
- const NOT_LOCKED = -2; /* Called release() with no lock held */
- const QUEUE_FULL = -3; /* There are already maxqueue workers on this lock */
- const TIMEOUT = -4; /* Timeout exceeded */
- const LOCK_HELD = -5; /* Cannot acquire another lock while you have one lock held */
+ const ERROR = -1; /* Indeterminate error */
+ const NOT_LOCKED = -2; /* Called release() with no lock held */
+ const QUEUE_FULL = -3; /* There are already maxqueue workers on this lock */
+ const TIMEOUT = -4; /* Timeout exceeded */
+ const LOCK_HELD = -5; /* Cannot acquire another lock while you have one lock held */
/**
* I want to do this task and I need to do it myself.
@@ -62,6 +67,11 @@ abstract class PoolCounter {
/**
* Create a Pool counter. This should only be called from the PoolWorks.
+ *
+ * @param $type
+ * @param $key
+ *
+ * @return PoolCounter
*/
public static function factory( $type, $key ) {
global $wgPoolCounterConf;
@@ -83,18 +93,28 @@ abstract class PoolCounter {
}
class PoolCounter_Stub extends PoolCounter {
+
+ /**
+ * @return Status
+ */
function acquireForMe() {
return Status::newGood( PoolCounter::LOCKED );
}
+ /**
+ * @return Status
+ */
function acquireForAnyone() {
return Status::newGood( PoolCounter::LOCKED );
}
+ /**
+ * @return Status
+ */
function release() {
return Status::newGood( PoolCounter::RELEASED );
}
-
+
public function __construct() {
/* No parameters needed */
}
@@ -134,6 +154,13 @@ abstract class PoolCounterWork {
function error( $status ) {
return false;
}
+
+ /**
+ * Log an error
+ */
+ function logError( $status ) {
+ wfDebugLog( 'poolcounter', $status->getWikiText() );
+ }
/**
* Get the result of the work (whatever it is), or false.
@@ -144,43 +171,47 @@ abstract class PoolCounterWork {
} else {
$status = $this->poolCounter->acquireForMe();
}
-
- if ( $status->isOK() ) {
- switch ( $status->value ) {
- case PoolCounter::LOCKED:
- $result = $this->doWork();
- $this->poolCounter->release();
- return $result;
+
+ if ( !$status->isOK() ) {
+ // Respond gracefully to complete server breakage: just log it and do the work
+ $this->logError( $status );
+ return $this->doWork();
+ }
+
+ switch ( $status->value ) {
+ case PoolCounter::LOCKED:
+ $result = $this->doWork();
+ $this->poolCounter->release();
+ return $result;
+
+ case PoolCounter::DONE:
+ $result = $this->getCachedWork();
+ if ( $result === false ) {
+ /* That someone else work didn't serve us.
+ * Acquire the lock for me
+ */
+ return $this->execute( true );
+ }
+ return $result;
+
+ case PoolCounter::QUEUE_FULL:
+ case PoolCounter::TIMEOUT:
+ $result = $this->fallback();
- case PoolCounter::DONE:
- $result = $this->getCachedWork();
- if ( $result === false ) {
- /* That someone else work didn't serve us.
- * Acquire the lock for me
- */
- return $this->execute( true );
- }
+ if ( $result !== false ) {
return $result;
-
- case PoolCounter::QUEUE_FULL:
- case PoolCounter::TIMEOUT:
- $result = $this->fallback();
-
- if ( $result !== false ) {
- return $result;
- }
- /* no break */
+ }
+ /* no break */
+
+ /* These two cases should never be hit... */
+ case PoolCounter::ERROR:
+ default:
+ $errors = array( PoolCounter::QUEUE_FULL => 'pool-queuefull', PoolCounter::TIMEOUT => 'pool-timeout' );
- /* These two cases should never be hit... */
- case PoolCounter::ERROR:
- default:
- $errors = array( PoolCounter::QUEUE_FULL => 'pool-queuefull', PoolCounter::TIMEOUT => 'pool-timeout' );
-
- $status = Status::newFatal( isset($errors[$status->value]) ? $errors[$status->value] : 'pool-errorunknown' );
- /* continue to the error */
- }
+ $status = Status::newFatal( isset($errors[$status->value]) ? $errors[$status->value] : 'pool-errorunknown' );
+ $this->logError( $status );
+ return $this->error( $status );
}
- return $this->error( $status );
}
function __construct( $type, $key ) {