public function getLagTimes($serverIndexes, $wiki) { if (count($serverIndexes) == 1 && reset($serverIndexes) == 0) { // Single server only, just return zero without caching return array(0 => 0); } $expiry = 5; $requestRate = 10; $cache = $this->cache; $masterName = $this->parent->getServerName(0); $memcKey = wfMemcKey('lag_times', $masterName); $times = $cache->get($memcKey); if (is_array($times)) { # Randomly recache with probability rising over $expiry $elapsed = time() - $times['timestamp']; $chance = max(0, ($expiry - $elapsed) * $requestRate); if (mt_rand(0, $chance) != 0) { unset($times['timestamp']); // hide from caller return $times; } wfIncrStats('lag_cache.miss.expired'); } else { wfIncrStats('lag_cache.miss.absent'); } # Cache key missing or expired if ($cache->lock($memcKey, 0, 10)) { # Let this process alone update the cache value $unlocker = new ScopedCallback(function () use($cache, $memcKey) { $cache->unlock($memcKey); }); } elseif (is_array($times)) { # Could not acquire lock but an old cache exists, so use it unset($times['timestamp']); // hide from caller return $times; } $times = array(); foreach ($serverIndexes as $i) { if ($i == 0) { # Master $times[$i] = 0; } elseif (false !== ($conn = $this->parent->getAnyOpenConnection($i))) { $times[$i] = $conn->getLag(); } elseif (false !== ($conn = $this->parent->openConnection($i, $wiki))) { $times[$i] = $conn->getLag(); // Close the connection to avoid sleeper connections piling up. // Note that the caller will pick one of these DBs and reconnect, // which is slightly inefficient, but this only matters for the lag // time cache miss cache, which is far less common that cache hits. $this->parent->closeConnection($conn); } } # Add a timestamp key so we know when it was cached $times['timestamp'] = time(); $cache->set($memcKey, $times, $expiry + 10); unset($times['timestamp']); // hide from caller return $times; }
public function getLagTimes($serverIndexes, $wiki) { if (count($serverIndexes) == 1 && reset($serverIndexes) == 0) { # Single server only, just return zero without caching return array(0 => 0); } $key = $this->getLagTimeCacheKey(); # Randomize TTLs to reduce stampedes (4.0 - 5.0 sec) $ttl = mt_rand(4000000.0, 5000000.0) / 1000000.0; # Keep keys around longer as fallbacks $staleTTL = 60; # (a) Check the local APC cache $value = $this->srvCache->get($key); if ($value && $value['timestamp'] > microtime(true) - $ttl) { wfDebugLog('replication', __METHOD__ . ": got lag times ({$key}) from local cache"); return $value['lagTimes']; // cache hit } $staleValue = $value ?: false; # (b) Check the shared cache and backfill APC $value = $this->mainCache->get($key); if ($value && $value['timestamp'] > microtime(true) - $ttl) { $this->srvCache->set($key, $value, $staleTTL); wfDebugLog('replication', __METHOD__ . ": got lag times ({$key}) from main cache"); return $value['lagTimes']; // cache hit } $staleValue = $value ?: $staleValue; # (c) Cache key missing or expired; regenerate and backfill if ($this->mainCache->lock($key, 0, 10)) { # Let this process alone update the cache value $cache = $this->mainCache; /** @noinspection PhpUnusedLocalVariableInspection */ $unlocker = new ScopedCallback(function () use($cache, $key) { $cache->unlock($key); }); } elseif ($staleValue) { # Could not acquire lock but an old cache exists, so use it return $staleValue['lagTimes']; } $lagTimes = array(); foreach ($serverIndexes as $i) { if ($i == 0) { # Master $lagTimes[$i] = 0; } elseif (false !== ($conn = $this->parent->getAnyOpenConnection($i))) { $lagTimes[$i] = $conn->getLag(); } elseif (false !== ($conn = $this->parent->openConnection($i, $wiki))) { $lagTimes[$i] = $conn->getLag(); # Close the connection to avoid sleeper connections piling up. # Note that the caller will pick one of these DBs and reconnect, # which is slightly inefficient, but this only matters for the lag # time cache miss cache, which is far less common that cache hits. $this->parent->closeConnection($conn); } } # Add a timestamp key so we know when it was cached $value = array('lagTimes' => $lagTimes, 'timestamp' => microtime(true)); $this->mainCache->set($key, $value, $staleTTL); $this->srvCache->set($key, $value, $staleTTL); wfDebugLog('replication', __METHOD__ . ": re-calculated lag times ({$key})"); return $value['lagTimes']; }