/**
  * @covers WANObjectCache::set()
  */
 public function testWritePending()
 {
     $value = 1;
     $key = wfRandomString();
     $opts = ['pending' => true];
     $this->cache->set($key, $value, 30, $opts);
     $this->assertEquals(false, $this->cache->get($key), "Pending value not written.");
 }
示例#2
0
 /**
  * Reserve a row with a single UPDATE without holding row locks over RTTs...
  *
  * @param string $uuid 32 char hex string
  * @param int $rand Random unsigned integer (31 bits)
  * @param bool $gte Search for job_random >= $random (otherwise job_random <= $random)
  * @return stdClass|bool Row|false
  */
 protected function claimRandom($uuid, $rand, $gte)
 {
     $dbw = $this->getMasterDB();
     // Check cache to see if the queue has <= OFFSET items
     $tinyQueue = $this->cache->get($this->getCacheKey('small'));
     $row = false;
     // the row acquired
     $invertedDirection = false;
     // whether one job_random direction was already scanned
     // This uses a replication safe method for acquiring jobs. One could use UPDATE+LIMIT
     // instead, but that either uses ORDER BY (in which case it deadlocks in MySQL) or is
     // not replication safe. Due to http://bugs.mysql.com/bug.php?id=6980, subqueries cannot
     // be used here with MySQL.
     do {
         if ($tinyQueue) {
             // queue has <= MAX_OFFSET rows
             // For small queues, using OFFSET will overshoot and return no rows more often.
             // Instead, this uses job_random to pick a row (possibly checking both directions).
             $ineq = $gte ? '>=' : '<=';
             $dir = $gte ? 'ASC' : 'DESC';
             $row = $dbw->selectRow('job', self::selectFields(), array('job_cmd' => $this->type, 'job_token' => '', "job_random {$ineq} {$dbw->addQuotes($rand)}"), __METHOD__, array('ORDER BY' => "job_random {$dir}"));
             if (!$row && !$invertedDirection) {
                 $gte = !$gte;
                 $invertedDirection = true;
                 continue;
                 // try the other direction
             }
         } else {
             // table *may* have >= MAX_OFFSET rows
             // Bug 42614: "ORDER BY job_random" with a job_random inequality causes high CPU
             // in MySQL if there are many rows for some reason. This uses a small OFFSET
             // instead of job_random for reducing excess claim retries.
             $row = $dbw->selectRow('job', self::selectFields(), array('job_cmd' => $this->type, 'job_token' => ''), __METHOD__, array('OFFSET' => mt_rand(0, self::MAX_OFFSET)));
             if (!$row) {
                 $tinyQueue = true;
                 // we know the queue must have <= MAX_OFFSET rows
                 $this->cache->set($this->getCacheKey('small'), 1, 30);
                 continue;
                 // use job_random
             }
         }
         if ($row) {
             // claim the job
             $dbw->update('job', array('job_token' => $uuid, 'job_token_timestamp' => $dbw->timestamp(), 'job_attempts = job_attempts+1'), array('job_cmd' => $this->type, 'job_id' => $row->job_id, 'job_token' => ''), __METHOD__);
             // This might get raced out by another runner when claiming the previously
             // selected row. The use of job_random should minimize this problem, however.
             if (!$dbw->affectedRows()) {
                 $row = false;
                 // raced out
             }
         } else {
             break;
             // nothing to do
         }
     } while (!$row);
     return $row;
 }
示例#3
0
 /**
  * @covers WANObjectCache::delete()
  */
 public function testDelete()
 {
     $key = wfRandomString();
     $value = wfRandomString();
     $this->cache->set($key, $value);
     $curTTL = null;
     $v = $this->cache->get($key, $curTTL);
     $this->assertEquals($value, $v, "Key was created with value");
     $this->assertGreaterThan(0, $curTTL, "Existing key has current TTL > 0");
     $this->cache->delete($key);
     $curTTL = null;
     $v = $this->cache->get($key, $curTTL);
     $this->assertFalse($v, "Deleted key has false value");
     $this->assertLessThan(0, $curTTL, "Deleted key has current TTL < 0");
     $this->cache->set($key, $value . 'more');
     $this->assertFalse($v, "Deleted key is tombstoned and has false value");
     $this->assertLessThan(0, $curTTL, "Deleted key is tombstoned and has current TTL < 0");
 }
示例#4
0
 /**
  * Get a message from the MediaWiki namespace, with caching. The key must
  * first be converted to two-part lang/msg form if necessary.
  *
  * Unlike self::get(), this function doesn't resolve fallback chains, and
  * some callers require this behavior. LanguageConverter::parseCachedTable()
  * and self::get() are some examples in core.
  *
  * @param string $title Message cache key with initial uppercase letter.
  * @param string $code Code denoting the language to try.
  * @return string|bool The message, or false if it does not exist or on error
  */
 function getMsgFromNamespace($title, $code)
 {
     $this->load($code);
     if (isset($this->mCache[$code][$title])) {
         $entry = $this->mCache[$code][$title];
         if (substr($entry, 0, 1) === ' ') {
             // The message exists, so make sure a string
             // is returned.
             return (string) substr($entry, 1);
         } elseif ($entry === '!NONEXISTENT') {
             return false;
         } elseif ($entry === '!TOO BIG') {
             // Fall through and try invididual message cache below
         }
     } else {
         // XXX: This is not cached in process cache, should it?
         $message = false;
         Hooks::run('MessagesPreLoad', array($title, &$message));
         if ($message !== false) {
             return $message;
         }
         return false;
     }
     # Try the individual message cache
     $titleKey = wfMemcKey('messages', 'individual', $title);
     $entry = $this->wanCache->get($titleKey);
     if ($entry) {
         if (substr($entry, 0, 1) === ' ') {
             $this->mCache[$code][$title] = $entry;
             // The message exists, so make sure a string
             // is returned.
             return (string) substr($entry, 1);
         } elseif ($entry === '!NONEXISTENT') {
             $this->mCache[$code][$title] = '!NONEXISTENT';
             return false;
         } else {
             # Corrupt/obsolete entry, delete it
             $this->wanCache->delete($titleKey);
         }
     }
     # Try loading it from the database
     $revision = Revision::newFromTitle(Title::makeTitle(NS_MEDIAWIKI, $title));
     if ($revision) {
         $content = $revision->getContent();
         if (!$content) {
             // A possibly temporary loading failure.
             wfDebugLog('MessageCache', __METHOD__ . ": failed to load message page text for {$title} ({$code})");
             $message = null;
             // no negative caching
         } else {
             // XXX: Is this the right way to turn a Content object into a message?
             // NOTE: $content is typically either WikitextContent, JavaScriptContent or
             //       CssContent. MessageContent is *not* used for storing messages, it's
             //       only used for wrapping them when needed.
             $message = $content->getWikitextForTransclusion();
             if ($message === false || $message === null) {
                 wfDebugLog('MessageCache', __METHOD__ . ": message content doesn't provide wikitext " . "(content model: " . $content->getModel() . ")");
                 $message = false;
                 // negative caching
             } else {
                 $this->mCache[$code][$title] = ' ' . $message;
                 $this->wanCache->set($titleKey, ' ' . $message, $this->mExpiry);
             }
         }
     } else {
         $message = false;
         // negative caching
     }
     if ($message === false) {
         // negative caching
         $this->mCache[$code][$title] = '!NONEXISTENT';
         $this->wanCache->set($titleKey, '!NONEXISTENT', $this->mExpiry);
     }
     return $message;
 }
示例#5
0
 public function testMcRouterSupport()
 {
     $localBag = $this->getMock('EmptyBagOStuff', ['set', 'delete']);
     $localBag->expects($this->never())->method('set');
     $localBag->expects($this->never())->method('delete');
     $wanCache = new WANObjectCache(['cache' => $localBag, 'pool' => 'testcache-hash', 'relayer' => new EventRelayerNull([])]);
     $valFunc = function () {
         return 1;
     };
     // None of these should use broadcasting commands (e.g. SET, DELETE)
     $wanCache->get('x');
     $wanCache->get('x', $ctl, ['check1']);
     $wanCache->getMulti(['x', 'y']);
     $wanCache->getMulti(['x', 'y'], $ctls, ['check2']);
     $wanCache->getWithSetCallback('p', 30, $valFunc);
     $wanCache->getCheckKeyTime('zzz');
 }
示例#6
0
 /**
  * Get the md5 used to validate the local disk cache
  *
  * @param string $code
  * @return array (hash or false, bool expiry status)
  */
 protected function getValidationHash($code)
 {
     $curTTL = null;
     $value = $this->wanCache->get(wfMemcKey('messages', $code, 'hash'), $curTTL, array(wfMemcKey('messages', $code)));
     $expired = $curTTL === null || $curTTL < 0;
     return array($value, $expired);
 }