[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/poolcounter/ -> PoolCounter.php (source)

   1  <?php
   2  /**
   3   * Provides of semaphore semantics for restricting the number
   4   * of workers that may be concurrently performing the same task.
   5   *
   6   * This program is free software; you can redistribute it and/or modify
   7   * it under the terms of the GNU General Public License as published by
   8   * the Free Software Foundation; either version 2 of the License, or
   9   * (at your option) any later version.
  10   *
  11   * This program is distributed in the hope that it will be useful,
  12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14   * GNU General Public License for more details.
  15   *
  16   * You should have received a copy of the GNU General Public License along
  17   * with this program; if not, write to the Free Software Foundation, Inc.,
  18   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19   * http://www.gnu.org/copyleft/gpl.html
  20   *
  21   * @file
  22   */
  23  
  24  /**
  25   * When you have many workers (threads/servers) giving service, and a
  26   * cached item expensive to produce expires, you may get several workers
  27   * doing the job at the same time.
  28   *
  29   * Given enough requests and the item expiring fast (non-cacheable,
  30   * lots of edits...) that single work can end up unfairly using most (all)
  31   * of the cpu of the pool. This is also known as 'Michael Jackson effect'
  32   * since this effect triggered on the english wikipedia on the day Michael
  33   * Jackson died, the biographical article got hit with several edits per
  34   * minutes and hundreds of read hits.
  35   *
  36   * The PoolCounter provides semaphore semantics for restricting the number
  37   * of workers that may be concurrently performing such single task.
  38   *
  39   * By default PoolCounter_Stub is used, which provides no locking. You
  40   * can get a useful one in the PoolCounter extension.
  41   */
  42  abstract class PoolCounter {
  43      /* Return codes */
  44      const LOCKED = 1; /* Lock acquired */
  45      const RELEASED = 2; /* Lock released */
  46      const DONE = 3; /* Another worker did the work for you */
  47  
  48      const ERROR = -1; /* Indeterminate error */
  49      const NOT_LOCKED = -2; /* Called release() with no lock held */
  50      const QUEUE_FULL = -3; /* There are already maxqueue workers on this lock */
  51      const TIMEOUT = -4; /* Timeout exceeded */
  52      const LOCK_HELD = -5; /* Cannot acquire another lock while you have one lock held */
  53  
  54      /** @var string All workers with the same key share the lock */
  55      protected $key;
  56      /** @var int Maximum number of workers working on tasks with the same key simultaneously */
  57      protected $workers;
  58      /**
  59       * Maximum number of workers working on this task type, regardless of key.
  60       * 0 means unlimited. Max allowed value is 65536.
  61       * The way the slot limit is enforced is overzealous - this option should be used with caution.
  62       * @var int
  63       */
  64      protected $slots = 0;
  65      /** @var int If this number of workers are already working/waiting, fail instead of wait */
  66      protected $maxqueue;
  67      /** @var float Maximum time in seconds to wait for the lock */
  68      protected $timeout;
  69  
  70      /**
  71       * @param array $conf
  72       * @param string $type
  73       * @param string $key
  74       */
  75  	protected function __construct( $conf, $type, $key ) {
  76          $this->workers = $conf['workers'];
  77          $this->maxqueue = $conf['maxqueue'];
  78          $this->timeout = $conf['timeout'];
  79          if ( isset( $conf['slots'] ) ) {
  80              $this->slots = $conf['slots'];
  81          }
  82  
  83          if ( $this->slots ) {
  84              $key = $this->hashKeyIntoSlots( $key, $this->slots );
  85          }
  86          $this->key = $key;
  87      }
  88  
  89      /**
  90       * Create a Pool counter. This should only be called from the PoolWorks.
  91       *
  92       * @param string $type
  93       * @param string $key
  94       *
  95       * @return PoolCounter
  96       */
  97  	public static function factory( $type, $key ) {
  98          global $wgPoolCounterConf;
  99          if ( !isset( $wgPoolCounterConf[$type] ) ) {
 100              return new PoolCounter_Stub;
 101          }
 102          $conf = $wgPoolCounterConf[$type];
 103          $class = $conf['class'];
 104  
 105          return new $class( $conf, $type, $key );
 106      }
 107  
 108      /**
 109       * @return string
 110       */
 111  	public function getKey() {
 112          return $this->key;
 113      }
 114  
 115      /**
 116       * I want to do this task and I need to do it myself.
 117       *
 118       * @return Status Value is one of Locked/Error
 119       */
 120      abstract public function acquireForMe();
 121  
 122      /**
 123       * I want to do this task, but if anyone else does it
 124       * instead, it's also fine for me. I will read its cached data.
 125       *
 126       * @return Status Value is one of Locked/Done/Error
 127       */
 128      abstract public function acquireForAnyone();
 129  
 130      /**
 131       * I have successfully finished my task.
 132       * Lets another one grab the lock, and returns the workers
 133       * waiting on acquireForAnyone()
 134       *
 135       * @return Status Value is one of Released/NotLocked/Error
 136       */
 137      abstract public function release();
 138  
 139      /**
 140       * Given a key (any string) and the number of lots, returns a slot number (an integer from the [0..($slots-1)] range).
 141       * This is used for a global limit on the number of instances  of a given type that can acquire a lock.
 142       * The hashing is deterministic so that PoolCounter::$workers is always an upper limit of how many instances with
 143       * the same key can acquire a lock.
 144       *
 145       * @param string $key PoolCounter instance key (any string)
 146       * @param int $slots The number of slots (max allowed value is 65536)
 147       * @return int
 148       */
 149  	protected function hashKeyIntoSlots( $key, $slots ) {
 150          return hexdec( substr( sha1( $key ), 0, 4 ) ) % $slots;
 151      }
 152  }
 153  
 154  // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
 155  class PoolCounter_Stub extends PoolCounter {
 156      // @codingStandardsIgnoreEnd
 157  
 158  	public function __construct() {
 159          /* No parameters needed */
 160      }
 161  
 162  	public function acquireForMe() {
 163          return Status::newGood( PoolCounter::LOCKED );
 164      }
 165  
 166  	public function acquireForAnyone() {
 167          return Status::newGood( PoolCounter::LOCKED );
 168      }
 169  
 170  	public function release() {
 171          return Status::newGood( PoolCounter::RELEASED );
 172      }
 173  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1