/**
  * @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.");
 }
Example #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;
 }
Example #3
0
 /**
  * Set the cached stat info for a file path.
  * Negatives (404s) are not cached. By not caching negatives, we can skip cache
  * salting for the case when a file is created at a path were there was none before.
  *
  * @param string $path Storage path
  * @param array $val Stat information to cache
  */
 protected final function setFileCache($path, array $val)
 {
     $path = FileBackend::normalizeStoragePath($path);
     if ($path === null) {
         return;
         // invalid storage path
     }
     $mtime = ConvertibleTimestamp::convert(TS_UNIX, $val['mtime']);
     $ttl = $this->memCache->adaptiveTTL($mtime, 7 * 86400, 300, 0.1);
     $key = $this->fileCacheKey($path);
     // Set the cache unless it is currently salted.
     $this->memCache->set($key, $val, $ttl);
 }
 /**
  * Set the cached stat info for a file path.
  * Negatives (404s) are not cached. By not caching negatives, we can skip cache
  * salting for the case when a file is created at a path were there was none before.
  *
  * @param string $path Storage path
  * @param array $val Stat information to cache
  */
 protected final function setFileCache($path, array $val)
 {
     $path = FileBackend::normalizeStoragePath($path);
     if ($path === null) {
         return;
         // invalid storage path
     }
     $age = time() - wfTimestamp(TS_UNIX, $val['mtime']);
     $ttl = min(7 * 86400, max(300, floor(0.1 * $age)));
     $key = $this->fileCacheKey($path);
     // Set the cache unless it is currently salted.
     $this->memCache->set($key, $val, $ttl);
 }
Example #5
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");
 }
Example #6
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;
 }
Example #7
0
 /**
  * Set the md5 used to validate the local disk cache
  *
  * @param string $code
  * @param string $hash
  */
 protected function setValidationHash($code, $hash)
 {
     $this->wanCache->set(wfMemcKey('messages', $code, 'hash'), $hash, WANObjectCache::TTL_NONE);
 }