MediaWiki  REL1_20
PoolCounter.php
Go to the documentation of this file.
00001 <?php
00042 abstract class PoolCounter {
00043 
00044         /* Return codes */
00045         const LOCKED   = 1; /* Lock acquired */
00046         const RELEASED = 2; /* Lock released */
00047         const DONE     = 3; /* Another worker did the work for you */
00048 
00049         const ERROR      = -1; /* Indeterminate error */
00050         const NOT_LOCKED = -2; /* Called release() with no lock held */
00051         const QUEUE_FULL = -3; /* There are already maxqueue workers on this lock */
00052         const TIMEOUT    = -4; /* Timeout exceeded */
00053         const LOCK_HELD  = -5; /* Cannot acquire another lock while you have one lock held */
00054 
00060         abstract function acquireForMe();
00061 
00068         abstract function acquireForAnyone();
00069 
00077         abstract function release();
00078 
00087         protected $key, $workers, $maxqueue, $timeout;
00088 
00097         public static function factory( $type, $key ) {
00098                 global $wgPoolCounterConf;
00099                 if ( !isset( $wgPoolCounterConf[$type] ) ) {
00100                         return new PoolCounter_Stub;
00101                 }
00102                 $conf = $wgPoolCounterConf[$type];
00103                 $class = $conf['class'];
00104 
00105                 return new $class( $conf, $type, $key );
00106         }
00107 
00108         protected function __construct( $conf, $type, $key ) {
00109                 $this->key = $key;
00110                 $this->workers  = $conf['workers'];
00111                 $this->maxqueue = $conf['maxqueue'];
00112                 $this->timeout  = $conf['timeout'];
00113         }
00114 }
00115 
00116 class PoolCounter_Stub extends PoolCounter {
00117 
00121         function acquireForMe() {
00122                 return Status::newGood( PoolCounter::LOCKED );
00123         }
00124 
00128         function acquireForAnyone() {
00129                 return Status::newGood( PoolCounter::LOCKED );
00130         }
00131 
00135         function release() {
00136                 return Status::newGood( PoolCounter::RELEASED );
00137         }
00138 
00139         public function __construct() {
00140                 /* No parameters needed */
00141         }
00142 }
00143 
00147 abstract class PoolCounterWork {
00148         protected $cacheable = false; //Does this override getCachedWork() ?
00149 
00153         abstract function doWork();
00154 
00159         function getCachedWork() {
00160                 return false;
00161         }
00162 
00168         function fallback() {
00169                 return false;
00170         }
00171 
00176         function error( $status ) {
00177                 return false;
00178         }
00179 
00185         function logError( $status ) {
00186                 wfDebugLog( 'poolcounter', $status->getWikiText() );
00187         }
00188 
00194         function execute( $skipcache = false ) {
00195                 if ( $this->cacheable && !$skipcache ) {
00196                         $status = $this->poolCounter->acquireForAnyone();
00197                 } else {
00198                         $status = $this->poolCounter->acquireForMe();
00199                 }
00200 
00201                 if ( !$status->isOK() ) {
00202                         // Respond gracefully to complete server breakage: just log it and do the work
00203                         $this->logError( $status );
00204                         return $this->doWork();
00205                 }
00206 
00207                 switch ( $status->value ) {
00208                         case PoolCounter::LOCKED:
00209                                 $result = $this->doWork();
00210                                 $this->poolCounter->release();
00211                                 return $result;
00212 
00213                         case PoolCounter::DONE:
00214                                 $result = $this->getCachedWork();
00215                                 if ( $result === false ) {
00216                                         /* That someone else work didn't serve us.
00217                                          * Acquire the lock for me
00218                                          */
00219                                         return $this->execute( true );
00220                                 }
00221                                 return $result;
00222 
00223                         case PoolCounter::QUEUE_FULL:
00224                         case PoolCounter::TIMEOUT:
00225                                 $result = $this->fallback();
00226 
00227                                 if ( $result !== false ) {
00228                                         return $result;
00229                                 }
00230                                 /* no break */
00231 
00232                         /* These two cases should never be hit... */
00233                         case PoolCounter::ERROR:
00234                         default:
00235                                 $errors = array( PoolCounter::QUEUE_FULL => 'pool-queuefull', PoolCounter::TIMEOUT => 'pool-timeout' );
00236 
00237                                 $status = Status::newFatal( isset( $errors[$status->value] ) ? $errors[$status->value] : 'pool-errorunknown' );
00238                                 $this->logError( $status );
00239                                 return $this->error( $status );
00240                 }
00241         }
00242 
00243         function __construct( $type, $key ) {
00244                 $this->poolCounter = PoolCounter::factory( $type, $key );
00245         }
00246 }