public function lock($key, $timeout = 6, $expiry = 6) { return $this->writeStore->lock($key, $timeout, $expiry); }
protected function getServerStates(array $serverIndexes, $domain) { $writerIndex = $this->parent->getWriterIndex(); if (count($serverIndexes) == 1 && reset($serverIndexes) == $writerIndex) { # Single server only, just return zero without caching return ['lagTimes' => [$writerIndex => 0], 'weightScales' => [$writerIndex => 1.0]]; } $key = $this->getCacheKey($serverIndexes); # 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) { $this->replLogger->debug(__METHOD__ . ": got lag times ({$key}) from local cache"); return $value; // 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); $this->replLogger->debug(__METHOD__ . ": got lag times ({$key}) from main cache"); return $value; // 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 = []; $weightScales = []; $movAveRatio = $this->movingAveRatio; foreach ($serverIndexes as $i) { if ($i == $this->parent->getWriterIndex()) { $lagTimes[$i] = 0; // master always has no lag $weightScales[$i] = 1.0; // nominal weight continue; } $conn = $this->parent->getAnyOpenConnection($i); if ($conn) { $close = false; // already open } else { $conn = $this->parent->openConnection($i, $domain); $close = true; // new connection } $lastWeight = isset($staleValue['weightScales'][$i]) ? $staleValue['weightScales'][$i] : 1.0; $coefficient = $this->getWeightScale($i, $conn ?: null); $newWeight = $movAveRatio * $coefficient + (1 - $movAveRatio) * $lastWeight; // Scale from 10% to 100% of nominal weight $weightScales[$i] = max($newWeight, 0.1); if (!$conn) { $lagTimes[$i] = false; $host = $this->parent->getServerName($i); $this->replLogger->error(__METHOD__ . ": host {$host} is unreachable"); continue; } if ($conn->getLBInfo('is static')) { $lagTimes[$i] = 0; } else { $lagTimes[$i] = $conn->getLag(); if ($lagTimes[$i] === false) { $host = $this->parent->getServerName($i); $this->replLogger->error(__METHOD__ . ": host {$host} is not replicating?"); } } if ($close) { # 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 = ['lagTimes' => $lagTimes, 'weightScales' => $weightScales, 'timestamp' => microtime(true)]; $this->mainCache->set($key, $value, $staleTTL); $this->srvCache->set($key, $value, $staleTTL); $this->replLogger->info(__METHOD__ . ": re-calculated lag times ({$key})"); return $value; }
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']; }
public function lock($key, $timeout = 6, $expiry = 6, $rclass = '') { return $this->backend->lock($key, $timeout, $expiry, $rclass); }