X-Git-Url: http://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fobjectcache%2FSqlBagOStuff.php;h=2ef94c429727c0bdca00968c04017bdcb1266e49;hb=ff320d9e49f1e67c2b3dad42503b9f16ce089250;hp=088f94e37c930b0e5aae44247f9cf1b225df457e;hpb=3353ced6cd3148de1549568ee9633a913fd5faab;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/objectcache/SqlBagOStuff.php b/includes/objectcache/SqlBagOStuff.php index 088f94e37c..2ef94c4297 100644 --- a/includes/objectcache/SqlBagOStuff.php +++ b/includes/objectcache/SqlBagOStuff.php @@ -46,7 +46,9 @@ class SqlBagOStuff extends BagOStuff { /** @var int */ protected $lastExpireAll = 0; /** @var int */ - protected $purgePeriod = 100; + protected $purgePeriod = 10; + /** @var int */ + protected $purgeLimit = 100; /** @var int */ protected $shards = 1; /** @var string */ @@ -77,12 +79,13 @@ class SqlBagOStuff extends BagOStuff { * when a cluster is replicated to another site (with different host names) * but each server has a corresponding replica in the other cluster. * - * - purgePeriod: The average number of object cache requests in between + * - purgePeriod: The average number of object cache writes in between * garbage collection operations, where expired entries * are removed from the database. Or in other words, the * reciprocal of the probability of purging on any given - * request. If this is set to zero, purging will never be - * done. + * write. If this is set to zero, purging will never be done. + * + * - purgeLimit: Maximum number of rows to purge at once. * * - tableName: The table name to use, default is "objectcache". * @@ -135,6 +138,9 @@ class SqlBagOStuff extends BagOStuff { if ( isset( $params['purgePeriod'] ) ) { $this->purgePeriod = intval( $params['purgePeriod'] ); } + if ( isset( $params['purgeLimit'] ) ) { + $this->purgeLimit = intval( $params['purgeLimit'] ); + } if ( isset( $params['tableName'] ) ) { $this->tableName = $params['tableName']; } @@ -250,7 +256,7 @@ class SqlBagOStuff extends BagOStuff { return false; } - public function getMulti( array $keys, $flags = 0 ) { + protected function doGetMulti( array $keys, $flags = 0 ) { $values = []; $blobs = $this->fetchBlobMulti( $keys ); @@ -261,7 +267,7 @@ class SqlBagOStuff extends BagOStuff { return $values; } - public function fetchBlobMulti( array $keys, $flags = 0 ) { + protected function fetchBlobMulti( array $keys, $flags = 0 ) { $values = []; // array of (key => value) $keysByTable = []; @@ -270,8 +276,6 @@ class SqlBagOStuff extends BagOStuff { $keysByTable[$serverIndex][$tableName][] = $key; } - $this->garbageCollect(); // expire old entries if any - $dataRows = []; foreach ( $keysByTable as $serverIndex => $serverKeys ) { try { @@ -338,6 +342,7 @@ class SqlBagOStuff extends BagOStuff { $result = true; $exptime = (int)$expiry; + /** @noinspection PhpUnusedLocalVariableInspection */ $silenceScope = $this->silenceTransactionProfiler(); foreach ( $keysByTable as $serverIndex => $serverKeys ) { $db = null; @@ -391,8 +396,8 @@ class SqlBagOStuff extends BagOStuff { return $result; } - public function set( $key, $value, $exptime = 0, $flags = 0 ) { - $ok = $this->setMulti( [ $key => $value ], $exptime ); + protected function doSet( $key, $value, $exptime = 0, $flags = 0 ) { + $ok = $this->insertMulti( [ $key => $value ], $exptime, $flags, true ); return $ok; } @@ -406,6 +411,7 @@ class SqlBagOStuff extends BagOStuff { protected function cas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) { list( $serverIndex, $tableName ) = $this->getTableByKey( $key ); $db = null; + /** @noinspection PhpUnusedLocalVariableInspection */ $silenceScope = $this->silenceTransactionProfiler(); try { $db = $this->getDB( $serverIndex ); @@ -446,6 +452,10 @@ class SqlBagOStuff extends BagOStuff { } public function deleteMulti( array $keys, $flags = 0 ) { + return $this->purgeMulti( $keys, $flags ); + } + + public function purgeMulti( array $keys, $flags = 0 ) { $keysByTable = []; foreach ( $keys as $key ) { list( $serverIndex, $tableName ) = $this->getTableByKey( $key ); @@ -453,6 +463,7 @@ class SqlBagOStuff extends BagOStuff { } $result = true; + /** @noinspection PhpUnusedLocalVariableInspection */ $silenceScope = $this->silenceTransactionProfiler(); foreach ( $keysByTable as $serverIndex => $serverKeys ) { $db = null; @@ -482,8 +493,8 @@ class SqlBagOStuff extends BagOStuff { return $result; } - public function delete( $key, $flags = 0 ) { - $ok = $this->deleteMulti( [ $key ], $flags ); + protected function doDelete( $key, $flags = 0 ) { + $ok = $this->purgeMulti( [ $key ], $flags ); return $ok; } @@ -491,6 +502,7 @@ class SqlBagOStuff extends BagOStuff { public function incr( $key, $step = 1 ) { list( $serverIndex, $tableName ) = $this->getTableByKey( $key ); $db = null; + /** @noinspection PhpUnusedLocalVariableInspection */ $silenceScope = $this->silenceTransactionProfiler(); try { $db = $this->getDB( $serverIndex ); @@ -522,7 +534,7 @@ class SqlBagOStuff extends BagOStuff { 'exptime' => $row->exptime ], __METHOD__, - 'IGNORE' + [ 'IGNORE' ] ); if ( $db->affectedRows() == 0 ) { @@ -549,6 +561,7 @@ class SqlBagOStuff extends BagOStuff { public function changeTTL( $key, $exptime = 0, $flags = 0 ) { list( $serverIndex, $tableName ) = $this->getTableByKey( $key ); $db = null; + /** @noinspection PhpUnusedLocalVariableInspection */ $silenceScope = $this->silenceTransactionProfiler(); try { $db = $this->getDB( $serverIndex ); @@ -608,7 +621,7 @@ class SqlBagOStuff extends BagOStuff { // Disabled return; } - // Only purge on one in every $this->purgePeriod requests. + // Only purge on one in every $this->purgePeriod writes if ( $this->purgePeriod !== 1 && mt_rand( 0, $this->purgePeriod - 1 ) ) { return; } @@ -616,7 +629,11 @@ class SqlBagOStuff extends BagOStuff { // Avoid repeating the delete within a few seconds if ( $now > ( $this->lastExpireAll + 1 ) ) { $this->lastExpireAll = $now; - $this->expireAll(); + $this->deleteObjectsExpiringBefore( + wfTimestamp( TS_MW, $now ), + false, + $this->purgeLimit + ); } } @@ -624,14 +641,15 @@ class SqlBagOStuff extends BagOStuff { $this->deleteObjectsExpiringBefore( wfTimestampNow() ); } - /** - * Delete objects from the database which expire before a certain date. - * @param string $timestamp - * @param bool|callable $progressCallback - * @return bool - */ - public function deleteObjectsExpiringBefore( $timestamp, $progressCallback = false ) { + public function deleteObjectsExpiringBefore( + $timestamp, + $progressCallback = false, + $limit = INF + ) { + /** @noinspection PhpUnusedLocalVariableInspection */ $silenceScope = $this->silenceTransactionProfiler(); + + $count = 0; for ( $serverIndex = 0; $serverIndex < $this->numServers; $serverIndex++ ) { $db = null; try { @@ -651,7 +669,8 @@ class SqlBagOStuff extends BagOStuff { [ 'keyname', 'exptime' ], $conds, __METHOD__, - [ 'LIMIT' => 100, 'ORDER BY' => 'exptime' ] ); + [ 'LIMIT' => 100, 'ORDER BY' => 'exptime' ] + ); if ( $rows === false || !$rows->numRows() ) { break; } @@ -674,9 +693,14 @@ class SqlBagOStuff extends BagOStuff { 'exptime < ' . $db->addQuotes( $dbTimestamp ), 'keyname' => $keys ], - __METHOD__ ); + __METHOD__ + ); + $count += $db->affectedRows(); + if ( $count >= $limit ) { + return true; + } - if ( $progressCallback ) { + if ( is_callable( $progressCallback ) ) { if ( intval( $totalSeconds ) === 0 ) { $percent = 0; } else { @@ -700,6 +724,7 @@ class SqlBagOStuff extends BagOStuff { return false; } } + return true; } @@ -709,6 +734,7 @@ class SqlBagOStuff extends BagOStuff { * @return bool */ public function deleteAll() { + /** @noinspection PhpUnusedLocalVariableInspection */ $silenceScope = $this->silenceTransactionProfiler(); for ( $serverIndex = 0; $serverIndex < $this->numServers; $serverIndex++ ) { $db = null; @@ -725,15 +751,77 @@ class SqlBagOStuff extends BagOStuff { return true; } + public function lock( $key, $timeout = 6, $expiry = 6, $rclass = '' ) { + // Avoid deadlocks and allow lock reentry if specified + if ( isset( $this->locks[$key] ) ) { + if ( $rclass != '' && $this->locks[$key]['class'] === $rclass ) { + ++$this->locks[$key]['depth']; + return true; + } else { + return false; + } + } + + list( $serverIndex ) = $this->getTableByKey( $key ); + try { + $db = $this->getDB( $serverIndex ); + $ok = $db->lock( $key, __METHOD__, $timeout ); + if ( $ok ) { + $this->locks[$key] = [ 'class' => $rclass, 'depth' => 1 ]; + } + + $this->logger->warning( + __METHOD__ . " failed due to timeout for {key}.", + [ 'key' => $key, 'timeout' => $timeout ] + ); + + return $ok; + } catch ( DBError $e ) { + $this->handleWriteError( $e, $db, $serverIndex ); + $ok = false; + } + + return $ok; + } + + public function unlock( $key ) { + if ( !isset( $this->locks[$key] ) ) { + return false; + } + + if ( --$this->locks[$key]['depth'] <= 0 ) { + unset( $this->locks[$key] ); + + list( $serverIndex ) = $this->getTableByKey( $key ); + try { + $db = $this->getDB( $serverIndex ); + $ok = $db->unlock( $key, __METHOD__ ); + if ( !$ok ) { + $this->logger->warning( + __METHOD__ . ' failed to release lock for {key}.', + [ 'key' => $key ] + ); + } + } catch ( DBError $e ) { + $this->handleWriteError( $e, $db, $serverIndex ); + $ok = false; + } + } else { + $ok = false; + } + + return $ok; + } + /** * Serialize an object and, if possible, compress the representation. * On typical message and page data, this can provide a 3X decrease * in storage requirements. * - * @param mixed &$data + * @param mixed $data * @return string */ - protected function serialize( &$data ) { + protected function serialize( $data ) { $serial = serialize( $data ); if ( function_exists( 'gzdeflate' ) ) { @@ -786,8 +874,8 @@ class SqlBagOStuff extends BagOStuff { * @param int $serverIndex * @throws Exception */ - protected function handleWriteError( DBError $exception, IDatabase $db = null, $serverIndex ) { - if ( !$db ) { + protected function handleWriteError( DBError $exception, $db, $serverIndex ) { + if ( !( $db instanceof IDatabase ) ) { $this->markServerDown( $exception, $serverIndex ); }