From f6d65e533c62f6deb21342d4901ece24497b433e Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Thu, 4 Jun 2015 07:31:04 +0200 Subject: Update to MediaWiki 1.25.1 --- includes/objectcache/APCBagOStuff.php | 108 ------ includes/objectcache/BagOStuff.php | 406 --------------------- includes/objectcache/EmptyBagOStuff.php | 80 ---- includes/objectcache/HashBagOStuff.php | 113 ------ includes/objectcache/MemcachedBagOStuff.php | 19 +- includes/objectcache/MemcachedClient.php | 20 +- includes/objectcache/MemcachedPeclBagOStuff.php | 25 +- includes/objectcache/MemcachedPhpBagOStuff.php | 18 +- includes/objectcache/MultiWriteBagOStuff.php | 30 +- includes/objectcache/ObjectCache.php | 11 +- includes/objectcache/ObjectCacheSessionHandler.php | 38 +- includes/objectcache/RedisBagOStuff.php | 24 +- includes/objectcache/SqlBagOStuff.php | 75 ++-- includes/objectcache/WinCacheBagOStuff.php | 92 ----- includes/objectcache/XCacheBagOStuff.php | 117 ------ 15 files changed, 140 insertions(+), 1036 deletions(-) delete mode 100644 includes/objectcache/APCBagOStuff.php delete mode 100644 includes/objectcache/BagOStuff.php delete mode 100644 includes/objectcache/EmptyBagOStuff.php delete mode 100644 includes/objectcache/HashBagOStuff.php delete mode 100644 includes/objectcache/WinCacheBagOStuff.php delete mode 100644 includes/objectcache/XCacheBagOStuff.php (limited to 'includes/objectcache') diff --git a/includes/objectcache/APCBagOStuff.php b/includes/objectcache/APCBagOStuff.php deleted file mode 100644 index 4cbb32df..00000000 --- a/includes/objectcache/APCBagOStuff.php +++ /dev/null @@ -1,108 +0,0 @@ -isInteger( $val ) ) { - $val = intval( $val ); - } else { - $val = unserialize( $val ); - } - } - - return $val; - } - - /** - * @param string $key - * @param mixed $value - * @param int $exptime - * @return bool - */ - public function set( $key, $value, $exptime = 0 ) { - if ( !$this->isInteger( $value ) ) { - $value = serialize( $value ); - } - - apc_store( $key, $value, $exptime ); - - return true; - } - - /** - * @param mixed $casToken - * @param string $key - * @param mixed $value - * @param int $exptime - * @return bool - */ - public function cas( $casToken, $key, $value, $exptime = 0 ) { - // APC's CAS functions only work on integers - throw new MWException( "CAS is not implemented in " . __CLASS__ ); - } - - /** - * @param string $key - * @param int $time - * @return bool - */ - public function delete( $key, $time = 0 ) { - apc_delete( $key ); - - return true; - } - - /** - * @param string $key - * @param Closure $callback Callback method to be executed - * @param int $exptime Either an interval in seconds or a unix timestamp for expiry - * @param int $attempts The amount of times to attempt a merge in case of failure - * @return bool Success - */ - public function merge( $key, Closure $callback, $exptime = 0, $attempts = 10 ) { - return $this->mergeViaLock( $key, $callback, $exptime, $attempts ); - } - - public function incr( $key, $value = 1 ) { - return apc_inc( $key, $value ); - } - - public function decr( $key, $value = 1 ) { - return apc_dec( $key, $value ); - } -} diff --git a/includes/objectcache/BagOStuff.php b/includes/objectcache/BagOStuff.php deleted file mode 100644 index 1978c3ea..00000000 --- a/includes/objectcache/BagOStuff.php +++ /dev/null @@ -1,406 +0,0 @@ - - * https://www.mediawiki.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - * @ingroup Cache - */ - -/** - * @defgroup Cache Cache - */ - -/** - * interface is intended to be more or less compatible with - * the PHP memcached client. - * - * backends for local hash array and SQL table included: - * - * $bag = new HashBagOStuff(); - * $bag = new SqlBagOStuff(); # connect to db first - * - * - * @ingroup Cache - */ -abstract class BagOStuff { - private $debugMode = false; - - protected $lastError = self::ERR_NONE; - - /** Possible values for getLastError() */ - const ERR_NONE = 0; // no error - const ERR_NO_RESPONSE = 1; // no response - const ERR_UNREACHABLE = 2; // can't connect - const ERR_UNEXPECTED = 3; // response gave some error - - /** - * @param bool $bool - */ - public function setDebug( $bool ) { - $this->debugMode = $bool; - } - - /* *** THE GUTS OF THE OPERATION *** */ - /* Override these with functional things in subclasses */ - - /** - * Get an item with the given key. Returns false if it does not exist. - * @param string $key - * @param mixed $casToken [optional] - * @return mixed Returns false on failure - */ - abstract public function get( $key, &$casToken = null ); - - /** - * Set an item. - * @param string $key - * @param mixed $value - * @param int $exptime Either an interval in seconds or a unix timestamp for expiry - * @return bool Success - */ - abstract public function set( $key, $value, $exptime = 0 ); - - /** - * Check and set an item. - * @param mixed $casToken - * @param string $key - * @param mixed $value - * @param int $exptime Either an interval in seconds or a unix timestamp for expiry - * @return bool Success - */ - abstract public function cas( $casToken, $key, $value, $exptime = 0 ); - - /** - * Delete an item. - * @param string $key - * @param int $time Amount of time to delay the operation (mostly memcached-specific) - * @return bool True if the item was deleted or not found, false on failure - */ - abstract public function delete( $key, $time = 0 ); - - /** - * Merge changes into the existing cache value (possibly creating a new one). - * The callback function returns the new value given the current value (possibly false), - * and takes the arguments: (this BagOStuff object, cache key, current value). - * - * @param string $key - * @param Closure $callback Callback method to be executed - * @param int $exptime Either an interval in seconds or a unix timestamp for expiry - * @param int $attempts The amount of times to attempt a merge in case of failure - * @return bool Success - */ - public function merge( $key, Closure $callback, $exptime = 0, $attempts = 10 ) { - return $this->mergeViaCas( $key, $callback, $exptime, $attempts ); - } - - /** - * @see BagOStuff::merge() - * - * @param string $key - * @param Closure $callback Callback method to be executed - * @param int $exptime Either an interval in seconds or a unix timestamp for expiry - * @param int $attempts The amount of times to attempt a merge in case of failure - * @return bool Success - */ - protected function mergeViaCas( $key, Closure $callback, $exptime = 0, $attempts = 10 ) { - do { - $casToken = null; // passed by reference - $currentValue = $this->get( $key, $casToken ); // get the old value - $value = $callback( $this, $key, $currentValue ); // derive the new value - - if ( $value === false ) { - $success = true; // do nothing - } elseif ( $currentValue === false ) { - // Try to create the key, failing if it gets created in the meantime - $success = $this->add( $key, $value, $exptime ); - } else { - // Try to update the key, failing if it gets changed in the meantime - $success = $this->cas( $casToken, $key, $value, $exptime ); - } - } while ( !$success && --$attempts ); - - return $success; - } - - /** - * @see BagOStuff::merge() - * - * @param string $key - * @param Closure $callback Callback method to be executed - * @param int $exptime Either an interval in seconds or a unix timestamp for expiry - * @param int $attempts The amount of times to attempt a merge in case of failure - * @return bool Success - */ - protected function mergeViaLock( $key, Closure $callback, $exptime = 0, $attempts = 10 ) { - if ( !$this->lock( $key, 6 ) ) { - return false; - } - - $currentValue = $this->get( $key ); // get the old value - $value = $callback( $this, $key, $currentValue ); // derive the new value - - if ( $value === false ) { - $success = true; // do nothing - } else { - $success = $this->set( $key, $value, $exptime ); // set the new value - } - - if ( !$this->unlock( $key ) ) { - // this should never happen - trigger_error( "Could not release lock for key '$key'." ); - } - - return $success; - } - - /** - * @param string $key - * @param int $timeout [optional] - * @return bool Success - */ - public function lock( $key, $timeout = 6 ) { - $this->clearLastError(); - $timestamp = microtime( true ); // starting UNIX timestamp - if ( $this->add( "{$key}:lock", 1, $timeout ) ) { - return true; - } elseif ( $this->getLastError() ) { - return false; - } - - $uRTT = ceil( 1e6 * ( microtime( true ) - $timestamp ) ); // estimate RTT (us) - $sleep = 2 * $uRTT; // rough time to do get()+set() - - $locked = false; // lock acquired - $attempts = 0; // failed attempts - do { - if ( ++$attempts >= 3 && $sleep <= 1e6 ) { - // Exponentially back off after failed attempts to avoid network spam. - // About 2*$uRTT*(2^n-1) us of "sleep" happen for the next n attempts. - $sleep *= 2; - } - usleep( $sleep ); // back off - $this->clearLastError(); - $locked = $this->add( "{$key}:lock", 1, $timeout ); - if ( $this->getLastError() ) { - return false; - } - } while ( !$locked ); - - return $locked; - } - - /** - * @param string $key - * @return bool Success - */ - public function unlock( $key ) { - return $this->delete( "{$key}:lock" ); - } - - /** - * Delete all objects expiring before a certain date. - * @param string $date The reference date in MW format - * @param callable|bool $progressCallback Optional, a function which will be called - * regularly during long-running operations with the percentage progress - * as the first parameter. - * - * @return bool Success, false if unimplemented - */ - public function deleteObjectsExpiringBefore( $date, $progressCallback = false ) { - // stub - return false; - } - - /* *** Emulated functions *** */ - - /** - * Get an associative array containing the item for each of the keys that have items. - * @param array $keys List of strings - * @return array - */ - public function getMulti( array $keys ) { - $res = array(); - foreach ( $keys as $key ) { - $val = $this->get( $key ); - if ( $val !== false ) { - $res[$key] = $val; - } - } - return $res; - } - - /** - * Batch insertion - * @param array $data $key => $value assoc array - * @param int $exptime Either an interval in seconds or a unix timestamp for expiry - * @return bool Success - * @since 1.24 - */ - public function setMulti( array $data, $exptime = 0 ) { - $res = true; - foreach ( $data as $key => $value ) { - if ( !$this->set( $key, $value, $exptime ) ) { - $res = false; - } - } - return $res; - } - - /** - * @param string $key - * @param mixed $value - * @param int $exptime - * @return bool Success - */ - public function add( $key, $value, $exptime = 0 ) { - if ( $this->get( $key ) === false ) { - return $this->set( $key, $value, $exptime ); - } - return false; // key already set - } - - /** - * Increase stored value of $key by $value while preserving its TTL - * @param string $key Key to increase - * @param int $value Value to add to $key (Default 1) - * @return int|bool New value or false on failure - */ - public function incr( $key, $value = 1 ) { - if ( !$this->lock( $key ) ) { - return false; - } - $n = $this->get( $key ); - if ( $this->isInteger( $n ) ) { // key exists? - $n += intval( $value ); - $this->set( $key, max( 0, $n ) ); // exptime? - } else { - $n = false; - } - $this->unlock( $key ); - - return $n; - } - - /** - * Decrease stored value of $key by $value while preserving its TTL - * @param string $key - * @param int $value - * @return int - */ - public function decr( $key, $value = 1 ) { - return $this->incr( $key, - $value ); - } - - /** - * Increase stored value of $key by $value while preserving its TTL - * - * This will create the key with value $init and TTL $ttl if not present - * - * @param string $key - * @param int $ttl - * @param int $value - * @param int $init - * @return bool - * @since 1.24 - */ - public function incrWithInit( $key, $ttl, $value = 1, $init = 1 ) { - return $this->incr( $key, $value ) || - $this->add( $key, (int)$init, $ttl ) || $this->incr( $key, $value ); - } - - /** - * Get the "last error" registered; clearLastError() should be called manually - * @return int ERR_* constant for the "last error" registry - * @since 1.23 - */ - public function getLastError() { - return $this->lastError; - } - - /** - * Clear the "last error" registry - * @since 1.23 - */ - public function clearLastError() { - $this->lastError = self::ERR_NONE; - } - - /** - * Set the "last error" registry - * @param int $err ERR_* constant - * @since 1.23 - */ - protected function setLastError( $err ) { - $this->lastError = $err; - } - - /** - * @param string $text - */ - public function debug( $text ) { - if ( $this->debugMode ) { - $class = get_class( $this ); - wfDebug( "$class debug: $text\n" ); - } - } - - /** - * Convert an optionally relative time to an absolute time - * @param int $exptime - * @return int - */ - protected function convertExpiry( $exptime ) { - if ( ( $exptime != 0 ) && ( $exptime < 86400 * 3650 /* 10 years */ ) ) { - return time() + $exptime; - } else { - return $exptime; - } - } - - /** - * Convert an optionally absolute expiry time to a relative time. If an - * absolute time is specified which is in the past, use a short expiry time. - * - * @param int $exptime - * @return int - */ - protected function convertToRelative( $exptime ) { - if ( $exptime >= 86400 * 3650 /* 10 years */ ) { - $exptime -= time(); - if ( $exptime <= 0 ) { - $exptime = 1; - } - return $exptime; - } else { - return $exptime; - } - } - - /** - * Check if a value is an integer - * - * @param mixed $value - * @return bool - */ - protected function isInteger( $value ) { - return ( is_int( $value ) || ctype_digit( $value ) ); - } -} diff --git a/includes/objectcache/EmptyBagOStuff.php b/includes/objectcache/EmptyBagOStuff.php deleted file mode 100644 index 9595b83c..00000000 --- a/includes/objectcache/EmptyBagOStuff.php +++ /dev/null @@ -1,80 +0,0 @@ -bag = array(); - } - - /** - * @param string $key - * @return bool - */ - protected function expire( $key ) { - $et = $this->bag[$key][1]; - - if ( ( $et == 0 ) || ( $et > time() ) ) { - return false; - } - - $this->delete( $key ); - - return true; - } - - /** - * @param string $key - * @param mixed $casToken [optional] - * @return bool|mixed - */ - function get( $key, &$casToken = null ) { - if ( !isset( $this->bag[$key] ) ) { - return false; - } - - if ( $this->expire( $key ) ) { - return false; - } - - $casToken = serialize( $this->bag[$key][0] ); - - return $this->bag[$key][0]; - } - - /** - * @param string $key - * @param mixed $value - * @param int $exptime - * @return bool - */ - function set( $key, $value, $exptime = 0 ) { - $this->bag[$key] = array( $value, $this->convertExpiry( $exptime ) ); - return true; - } - - /** - * @param mixed $casToken - * @param string $key - * @param mixed $value - * @param int $exptime - * @return bool - */ - function cas( $casToken, $key, $value, $exptime = 0 ) { - if ( serialize( $this->get( $key ) ) === $casToken ) { - return $this->set( $key, $value, $exptime ); - } - - return false; - } - - /** - * @param string $key - * @param int $time - * @return bool - */ - function delete( $key, $time = 0 ) { - if ( !isset( $this->bag[$key] ) ) { - return false; - } - - unset( $this->bag[$key] ); - - return true; - } -} diff --git a/includes/objectcache/MemcachedBagOStuff.php b/includes/objectcache/MemcachedBagOStuff.php index 53edcdde..83bee700 100644 --- a/includes/objectcache/MemcachedBagOStuff.php +++ b/includes/objectcache/MemcachedBagOStuff.php @@ -84,18 +84,17 @@ class MemcachedBagOStuff extends BagOStuff { * @param int $exptime * @return bool */ - public function cas( $casToken, $key, $value, $exptime = 0 ) { + protected function cas( $casToken, $key, $value, $exptime = 0 ) { return $this->client->cas( $casToken, $this->encodeKey( $key ), $value, $this->fixExpiry( $exptime ) ); } /** * @param string $key - * @param int $time * @return bool */ - public function delete( $key, $time = 0 ) { - return $this->client->delete( $this->encodeKey( $key ), $time ); + public function delete( $key ) { + return $this->client->delete( $this->encodeKey( $key ) ); } /** @@ -109,6 +108,14 @@ class MemcachedBagOStuff extends BagOStuff { $this->fixExpiry( $exptime ) ); } + public function merge( $key, $callback, $exptime = 0, $attempts = 10 ) { + if ( !is_callable( $callback ) ) { + throw new Exception( "Got invalid callback." ); + } + + return $this->mergeViaCas( $key, $callback, $exptime, $attempts ); + } + /** * Get the underlying client object. This is provided for debugging * purposes. @@ -145,7 +152,7 @@ class MemcachedBagOStuff extends BagOStuff { * TTLs higher than 30 days will be detected as absolute TTLs * (UNIX timestamps), and will result in the cache entry being * discarded immediately because the expiry is in the past. - * Clamp expiries >30d at 30d, unless they're >=1e9 in which + * Clamp expires >30d at 30d, unless they're >=1e9 in which * case they are likely to really be absolute (1e9 = 2011-09-09) * @param int $expiry * @return int @@ -174,6 +181,6 @@ class MemcachedBagOStuff extends BagOStuff { * @param string $text */ protected function debugLog( $text ) { - wfDebugLog( 'memcached', $text ); + $this->logger->debug( $text ); } } diff --git a/includes/objectcache/MemcachedClient.php b/includes/objectcache/MemcachedClient.php index 41eebfb5..bc4a00b2 100644 --- a/includes/objectcache/MemcachedClient.php +++ b/includes/objectcache/MemcachedClient.php @@ -64,6 +64,9 @@ * @version 0.1.2 */ +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; + // {{{ requirements // }}} @@ -233,6 +236,11 @@ class MWMemcached { */ public $_connect_attempts; + /** + * @var LoggerInterface + */ + private $_logger; + // }}} // }}} // {{{ methods @@ -263,6 +271,8 @@ class MWMemcached { $this->_connect_timeout = isset( $args['connect_timeout'] ) ? $args['connect_timeout'] : 0.1; $this->_connect_attempts = 2; + + $this->_logger = isset( $args['logger'] ) ? $args['logger'] : new NullLogger(); } // }}} @@ -413,7 +423,6 @@ class MWMemcached { * @return mixed */ public function get( $key, &$casToken = null ) { - wfProfileIn( __METHOD__ ); if ( $this->_debug ) { $this->_debugprint( "get($key)\n" ); @@ -421,19 +430,16 @@ class MWMemcached { if ( !is_array( $key ) && strval( $key ) === '' ) { $this->_debugprint( "Skipping key which equals to an empty string" ); - wfProfileOut( __METHOD__ ); return false; } if ( !$this->_active ) { - wfProfileOut( __METHOD__ ); return false; } $sock = $this->get_sock( $key ); if ( !is_resource( $sock ) ) { - wfProfileOut( __METHOD__ ); return false; } @@ -446,7 +452,6 @@ class MWMemcached { $cmd = "gets $key\r\n"; if ( !$this->_fwrite( $sock, $cmd ) ) { - wfProfileOut( __METHOD__ ); return false; } @@ -463,7 +468,6 @@ class MWMemcached { if ( isset( $val[$key] ) ) { $value = $val[$key]; } - wfProfileOut( __METHOD__ ); return $value; } @@ -1110,14 +1114,14 @@ class MWMemcached { * @param string $text */ function _debugprint( $text ) { - wfDebugLog( 'memcached', $text ); + $this->_logger->debug( $text ); } /** * @param string $text */ function _error_log( $text ) { - wfDebugLog( 'memcached-serious', "Memcached error: $text" ); + $this->_logger->error( "Memcached error: $text" ); } /** diff --git a/includes/objectcache/MemcachedPeclBagOStuff.php b/includes/objectcache/MemcachedPeclBagOStuff.php index c853bcf4..f2c49281 100644 --- a/includes/objectcache/MemcachedPeclBagOStuff.php +++ b/includes/objectcache/MemcachedPeclBagOStuff.php @@ -43,8 +43,10 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { * values, but serialization is much slower unless the php.ini option * igbinary.compact_strings is off. * @param array $params + * @throws MWException */ function __construct( $params ) { + parent::__construct( $params ); $params = $this->applyDefaultParams( $params ); if ( $params['persistent'] ) { @@ -53,7 +55,7 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { // We can only reuse a pool ID if we keep the config consistent. $this->client = new Memcached( md5( serialize( $params ) ) ); if ( count( $this->client->getServerList() ) ) { - wfDebug( __METHOD__ . ": persistent Memcached object already loaded.\n" ); + $this->logger->debug( __METHOD__ . ": persistent Memcached object already loaded." ); return; // already initialized; don't add duplicate servers } } else { @@ -119,11 +121,9 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { * @return mixed */ public function get( $key, &$casToken = null ) { - wfProfileIn( __METHOD__ ); $this->debugLog( "get($key)" ); $result = $this->client->get( $this->encodeKey( $key ), null, $casToken ); $result = $this->checkResult( $key, $result ); - wfProfileOut( __METHOD__ ); return $result; } @@ -145,19 +145,18 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { * @param int $exptime * @return bool */ - public function cas( $casToken, $key, $value, $exptime = 0 ) { + protected function cas( $casToken, $key, $value, $exptime = 0 ) { $this->debugLog( "cas($key)" ); return $this->checkResult( $key, parent::cas( $casToken, $key, $value, $exptime ) ); } /** * @param string $key - * @param int $time * @return bool */ - public function delete( $key, $time = 0 ) { + public function delete( $key ) { $this->debugLog( "delete($key)" ); - $result = parent::delete( $key, $time ); + $result = parent::delete( $key ); if ( $result === false && $this->client->getResultCode() === Memcached::RES_NOTFOUND ) { // "Not found" is counted as success in our interface return true; @@ -224,14 +223,16 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { break; default: $msg = $this->client->getResultMessage(); + $logCtx = array(); if ( $key !== false ) { $server = $this->client->getServerByKey( $key ); - $serverName = "{$server['host']}:{$server['port']}"; - $msg = "Memcached error for key \"$key\" on server \"$serverName\": $msg"; + $logCtx['memcached-server'] = "{$server['host']}:{$server['port']}"; + $logCtx['memcached-key'] = $key; + $msg = "Memcached error for key \"{memcached-key}\" on server \"{memcached-server}\": $msg"; } else { $msg = "Memcached error: $msg"; } - wfDebugLog( 'memcached-serious', $msg ); + $this->logger->error( $msg, $logCtx ); $this->setLastError( BagOStuff::ERR_UNEXPECTED ); } return $result; @@ -242,11 +243,9 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { * @return array */ public function getMulti( array $keys ) { - wfProfileIn( __METHOD__ ); $this->debugLog( 'getMulti(' . implode( ', ', $keys ) . ')' ); $callback = array( $this, 'encodeKey' ); $result = $this->client->getMulti( array_map( $callback, $keys ) ); - wfProfileOut( __METHOD__ ); $result = $result ?: array(); // must be an array return $this->checkResult( false, $result ); } @@ -257,7 +256,6 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { * @return bool */ public function setMulti( array $data, $exptime = 0 ) { - wfProfileIn( __METHOD__ ); foreach ( $data as $key => $value ) { $encKey = $this->encodeKey( $key ); if ( $encKey !== $key ) { @@ -267,7 +265,6 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { } $this->debugLog( 'setMulti(' . implode( ', ', array_keys( $data ) ) . ')' ); $result = $this->client->setMulti( $data, $this->fixExpiry( $exptime ) ); - wfProfileOut( __METHOD__ ); return $this->checkResult( false, $result ); } } diff --git a/includes/objectcache/MemcachedPhpBagOStuff.php b/includes/objectcache/MemcachedPhpBagOStuff.php index 330d2b52..6fba61ba 100644 --- a/includes/objectcache/MemcachedPhpBagOStuff.php +++ b/includes/objectcache/MemcachedPhpBagOStuff.php @@ -42,6 +42,7 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff { * @param array $params */ function __construct( $params ) { + parent::__construct( $params ); $params = $this->applyDefaultParams( $params ); $this->client = new MemCachedClientforWiki( $params ); @@ -65,23 +66,6 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff { return $this->client->get_multi( array_map( $callback, $keys ) ); } - /** - * @param string $key - * @param int $timeout - * @return bool - */ - public function lock( $key, $timeout = 0 ) { - return $this->client->lock( $this->encodeKey( $key ), $timeout ); - } - - /** - * @param string $key - * @return mixed - */ - public function unlock( $key ) { - return $this->client->unlock( $this->encodeKey( $key ) ); - } - /** * @param string $key * @param int $value diff --git a/includes/objectcache/MultiWriteBagOStuff.php b/includes/objectcache/MultiWriteBagOStuff.php index 6a691379..be54e4d3 100644 --- a/includes/objectcache/MultiWriteBagOStuff.php +++ b/includes/objectcache/MultiWriteBagOStuff.php @@ -40,11 +40,12 @@ class MultiWriteBagOStuff extends BagOStuff { * the documentation of $wgObjectCaches for more detail. * * @param array $params - * @throws MWException + * @throws InvalidArgumentException */ public function __construct( $params ) { + parent::__construct( $params ); if ( !isset( $params['caches'] ) ) { - throw new MWException( __METHOD__ . ': the caches parameter is required' ); + throw new InvalidArgumentException( __METHOD__ . ': the caches parameter is required' ); } $this->caches = array(); @@ -75,17 +76,6 @@ class MultiWriteBagOStuff extends BagOStuff { return false; } - /** - * @param mixed $casToken - * @param string $key - * @param mixed $value - * @param mixed $exptime - * @return bool - */ - public function cas( $casToken, $key, $value, $exptime = 0 ) { - throw new MWException( "CAS is not implemented in " . __CLASS__ ); - } - /** * @param string $key * @param mixed $value @@ -98,11 +88,10 @@ class MultiWriteBagOStuff extends BagOStuff { /** * @param string $key - * @param int $time * @return bool */ - public function delete( $key, $time = 0 ) { - return $this->doWrite( 'delete', $key, $time ); + public function delete( $key ) { + return $this->doWrite( 'delete', $key ); } /** @@ -136,12 +125,13 @@ class MultiWriteBagOStuff extends BagOStuff { /** * @param string $key * @param int $timeout + * @param int $expiry * @return bool */ - public function lock( $key, $timeout = 0 ) { + public function lock( $key, $timeout = 6, $expiry = 6 ) { // Lock only the first cache, to avoid deadlocks if ( isset( $this->caches[0] ) ) { - return $this->caches[0]->lock( $key, $timeout ); + return $this->caches[0]->lock( $key, $timeout, $expiry ); } else { return true; } @@ -161,12 +151,12 @@ class MultiWriteBagOStuff extends BagOStuff { /** * @param string $key - * @param Closure $callback Callback method to be executed + * @param callable $callback Callback method to be executed * @param int $exptime Either an interval in seconds or a unix timestamp for expiry * @param int $attempts The amount of times to attempt a merge in case of failure * @return bool Success */ - public function merge( $key, Closure $callback, $exptime = 0, $attempts = 10 ) { + public function merge( $key, $callback, $exptime = 0, $attempts = 10 ) { return $this->doWrite( 'merge', $key, $callback, $exptime ); } diff --git a/includes/objectcache/ObjectCache.php b/includes/objectcache/ObjectCache.php index 633b34a2..2e47e24a 100644 --- a/includes/objectcache/ObjectCache.php +++ b/includes/objectcache/ObjectCache.php @@ -21,6 +21,8 @@ * @ingroup Cache */ +use MediaWiki\Logger\LoggerFactory; + /** * Functions to get cache objects * @@ -81,6 +83,13 @@ class ObjectCache { * @return BagOStuff */ static function newFromParams( $params ) { + if ( isset( $params['loggroup'] ) ) { + $params['logger'] = LoggerFactory::getInstance( $params['loggroup'] ); + } else { + // For backwards-compatability with custom parameters, lets not + // have all logging suddenly disappear + $params['logger'] = LoggerFactory::getInstance( 'objectcache' ); + } if ( isset( $params['factory'] ) ) { return call_user_func( $params['factory'], $params ); } elseif ( isset( $params['class'] ) ) { @@ -135,7 +144,7 @@ class ObjectCache { } elseif ( function_exists( 'wincache_ucache_get' ) ) { $id = 'wincache'; } else { - if ( $fallback ) { + if ( $fallback !== null ) { return self::newFromId( $fallback ); } throw new MWException( "CACHE_ACCEL requested but no suitable object " . diff --git a/includes/objectcache/ObjectCacheSessionHandler.php b/includes/objectcache/ObjectCacheSessionHandler.php index cdf8da1e..789f1e3b 100644 --- a/includes/objectcache/ObjectCacheSessionHandler.php +++ b/includes/objectcache/ObjectCacheSessionHandler.php @@ -28,6 +28,9 @@ * @ingroup Cache */ class ObjectCacheSessionHandler { + /** @var array Map of (session ID => SHA-1 of the data) */ + protected static $hashCache = array(); + /** * Install a session handler for the current web request */ @@ -49,10 +52,11 @@ class ObjectCacheSessionHandler { /** * Get the cache storage object to use for session storage - * @return ObjectCache + * @return BagOStuff */ - static function getCache() { + protected static function getCache() { global $wgSessionCacheType; + return ObjectCache::getInstance( $wgSessionCacheType ); } @@ -62,10 +66,18 @@ class ObjectCacheSessionHandler { * @param string $id Session id * @return string Cache key */ - static function getKey( $id ) { + protected static function getKey( $id ) { return wfMemcKey( 'session', $id ); } + /** + * @param mixed $data + * @return string + */ + protected static function getHash( $data ) { + return sha1( serialize( $data ) ); + } + /** * Callback when opening a session. * @@ -95,22 +107,29 @@ class ObjectCacheSessionHandler { */ static function read( $id ) { $data = self::getCache()->get( self::getKey( $id ) ); - if ( $data === false ) { - return ''; - } - return $data; + + self::$hashCache = array( $id => self::getHash( $data ) ); + + return ( $data === false ) ? '' : $data; } /** * Callback when writing session data. * * @param string $id Session id - * @param mixed $data Session data + * @param string $data Session data * @return bool Success */ static function write( $id, $data ) { global $wgObjectCacheSessionExpiry; - self::getCache()->set( self::getKey( $id ), $data, $wgObjectCacheSessionExpiry ); + + // Only issue a write if anything changed (PHP 5.6 already does this) + if ( !isset( self::$hashCache[$id] ) + || self::getHash( $data ) !== self::$hashCache[$id] + ) { + self::getCache()->set( self::getKey( $id ), $data, $wgObjectCacheSessionExpiry ); + } + return true; } @@ -122,6 +141,7 @@ class ObjectCacheSessionHandler { */ static function destroy( $id ) { self::getCache()->delete( self::getKey( $id ) ); + return true; } diff --git a/includes/objectcache/RedisBagOStuff.php b/includes/objectcache/RedisBagOStuff.php index ae8cc5b7..de3511df 100644 --- a/includes/objectcache/RedisBagOStuff.php +++ b/includes/objectcache/RedisBagOStuff.php @@ -56,6 +56,7 @@ class RedisBagOStuff extends BagOStuff { * @param array $params */ function __construct( $params ) { + parent::__construct( $params ); $redisConf = array( 'serializer' => 'none' ); // manage that in this class foreach ( array( 'connectTimeout', 'persistent', 'password' ) as $opt ) { if ( isset( $params[$opt] ) ) { @@ -73,7 +74,6 @@ class RedisBagOStuff extends BagOStuff { } public function get( $key, &$casToken = null ) { - $section = new ProfileSection( __METHOD__ ); list( $server, $conn ) = $this->getConnection( $key ); if ( !$conn ) { @@ -93,7 +93,6 @@ class RedisBagOStuff extends BagOStuff { } public function set( $key, $value, $expiry = 0 ) { - $section = new ProfileSection( __METHOD__ ); list( $server, $conn ) = $this->getConnection( $key ); if ( !$conn ) { @@ -116,8 +115,7 @@ class RedisBagOStuff extends BagOStuff { return $result; } - public function cas( $casToken, $key, $value, $expiry = 0 ) { - $section = new ProfileSection( __METHOD__ ); + protected function cas( $casToken, $key, $value, $expiry = 0 ) { list( $server, $conn ) = $this->getConnection( $key ); if ( !$conn ) { @@ -150,8 +148,7 @@ class RedisBagOStuff extends BagOStuff { return $result; } - public function delete( $key, $time = 0 ) { - $section = new ProfileSection( __METHOD__ ); + public function delete( $key ) { list( $server, $conn ) = $this->getConnection( $key ); if ( !$conn ) { @@ -171,7 +168,6 @@ class RedisBagOStuff extends BagOStuff { } public function getMulti( array $keys ) { - $section = new ProfileSection( __METHOD__ ); $batches = array(); $conns = array(); @@ -217,7 +213,6 @@ class RedisBagOStuff extends BagOStuff { * @return bool */ public function setMulti( array $data, $expiry = 0 ) { - $section = new ProfileSection( __METHOD__ ); $batches = array(); $conns = array(); @@ -265,7 +260,6 @@ class RedisBagOStuff extends BagOStuff { public function add( $key, $value, $expiry = 0 ) { - $section = new ProfileSection( __METHOD__ ); list( $server, $conn ) = $this->getConnection( $key ); if ( !$conn ) { @@ -303,7 +297,6 @@ class RedisBagOStuff extends BagOStuff { * @return int|bool New value or false on failure */ public function incr( $key, $value = 1 ) { - $section = new ProfileSection( __METHOD__ ); list( $server, $conn ) = $this->getConnection( $key ); if ( !$conn ) { @@ -322,6 +315,15 @@ class RedisBagOStuff extends BagOStuff { $this->logRequest( 'incr', $key, $server, $result ); return $result; } + + public function merge( $key, $callback, $exptime = 0, $attempts = 10 ) { + if ( !is_callable( $callback ) ) { + throw new Exception( "Got invalid callback." ); + } + + return $this->mergeViaCas( $key, $callback, $exptime, $attempts ); + } + /** * @param mixed $data * @return string @@ -371,7 +373,7 @@ class RedisBagOStuff extends BagOStuff { * @param string $msg */ protected function logError( $msg ) { - wfDebugLog( 'redis', "Redis error: $msg" ); + $this->logger->error( "Redis error: $msg" ); } /** diff --git a/includes/objectcache/SqlBagOStuff.php b/includes/objectcache/SqlBagOStuff.php index 58720790..82eeb842 100644 --- a/includes/objectcache/SqlBagOStuff.php +++ b/includes/objectcache/SqlBagOStuff.php @@ -88,6 +88,7 @@ class SqlBagOStuff extends BagOStuff { * @param array $params */ public function __construct( $params ) { + parent::__construct( $params ); if ( isset( $params['servers'] ) ) { $this->serverInfos = $params['servers']; $this->numServers = count( $this->serverInfos ); @@ -118,6 +119,7 @@ class SqlBagOStuff extends BagOStuff { * * @param int $serverIndex * @return DatabaseBase + * @throws MWException */ protected function getDB( $serverIndex ) { global $wgDebugDBTransactions; @@ -137,12 +139,14 @@ class SqlBagOStuff extends BagOStuff { # If server connection info was given, use that if ( $this->serverInfos ) { if ( $wgDebugDBTransactions ) { - wfDebug( "Using provided serverInfo for SqlBagOStuff\n" ); + $this->logger->debug( "Using provided serverInfo for SqlBagOStuff" ); } $info = $this->serverInfos[$serverIndex]; $type = isset( $info['type'] ) ? $info['type'] : 'mysql'; $host = isset( $info['host'] ) ? $info['host'] : '[unknown]'; - wfDebug( __CLASS__ . ": connecting to $host\n" ); + $this->logger->debug( __CLASS__ . ": connecting to $host" ); + // Use a blank trx profiler to ignore expections as this is a cache + $info['trxProfiler'] = new TransactionProfiler(); $db = DatabaseBase::factory( $type, $info ); $db->clearFlag( DBO_TRX ); } else { @@ -160,7 +164,7 @@ class SqlBagOStuff extends BagOStuff { } } if ( $wgDebugDBTransactions ) { - wfDebug( sprintf( "Connection %s will be used for SqlBagOStuff\n", $db ) ); + $this->logger->debug( sprintf( "Connection %s will be used for SqlBagOStuff", $db ) ); } $this->conns[$serverIndex] = $db; } @@ -322,9 +326,7 @@ class SqlBagOStuff extends BagOStuff { if ( $exptime == 0 ) { $encExpiry = $this->getMaxDateTime( $db ); } else { - if ( $exptime < 3.16e8 ) { # ~10 years - $exptime += time(); - } + $exptime = $this->convertExpiry( $exptime ); $encExpiry = $db->timestamp( $exptime ); } foreach ( $serverKeys as $tableName => $tableKeys ) { @@ -377,10 +379,7 @@ class SqlBagOStuff extends BagOStuff { if ( $exptime == 0 ) { $encExpiry = $this->getMaxDateTime( $db ); } else { - if ( $exptime < 3.16e8 ) { # ~10 years - $exptime += time(); - } - + $exptime = $this->convertExpiry( $exptime ); $encExpiry = $db->timestamp( $exptime ); } // (bug 24425) use a replace if the db supports it instead of @@ -408,7 +407,7 @@ class SqlBagOStuff extends BagOStuff { * @param int $exptime * @return bool */ - public function cas( $casToken, $key, $value, $exptime = 0 ) { + protected function cas( $casToken, $key, $value, $exptime = 0 ) { list( $serverIndex, $tableName ) = $this->getTableByKey( $key ); try { $db = $this->getDB( $serverIndex ); @@ -421,9 +420,7 @@ class SqlBagOStuff extends BagOStuff { if ( $exptime == 0 ) { $encExpiry = $this->getMaxDateTime( $db ); } else { - if ( $exptime < 3.16e8 ) { # ~10 years - $exptime += time(); - } + $exptime = $this->convertExpiry( $exptime ); $encExpiry = $db->timestamp( $exptime ); } // (bug 24425) use a replace if the db supports it instead of @@ -452,10 +449,9 @@ class SqlBagOStuff extends BagOStuff { /** * @param string $key - * @param int $time * @return bool */ - public function delete( $key, $time = 0 ) { + public function delete( $key ) { list( $serverIndex, $tableName ) = $this->getTableByKey( $key ); try { $db = $this->getDB( $serverIndex ); @@ -520,6 +516,14 @@ class SqlBagOStuff extends BagOStuff { return $newValue; } + public function merge( $key, $callback, $exptime = 0, $attempts = 10 ) { + if ( !is_callable( $callback ) ) { + throw new Exception( "Got invalid callback." ); + } + + return $this->mergeViaCas( $key, $callback, $exptime, $attempts ); + } + /** * @param DatabaseBase $db * @param string $exptime @@ -621,7 +625,8 @@ class SqlBagOStuff extends BagOStuff { if ( $remainingSeconds > $totalSeconds ) { $totalSeconds = $remainingSeconds; } - $percent = ( $i + $remainingSeconds / $totalSeconds ) + $processedSeconds = $totalSeconds - $remainingSeconds; + $percent = ( $i + $processedSeconds / $totalSeconds ) / $this->shards * 100; } $percent = ( $percent / $this->numServers ) @@ -638,6 +643,11 @@ class SqlBagOStuff extends BagOStuff { return true; } + /** + * Delete content of shard tables in every server. + * Return true if the operation is successful, false otherwise. + * @return bool + */ public function deleteAll() { for ( $serverIndex = 0; $serverIndex < $this->numServers; $serverIndex++ ) { try { @@ -702,13 +712,13 @@ class SqlBagOStuff extends BagOStuff { if ( $exception instanceof DBConnectionError ) { $this->markServerDown( $exception, $serverIndex ); } - wfDebugLog( 'SQLBagOStuff', "DBError: {$exception->getMessage()}" ); + $this->logger->error( "DBError: {$exception->getMessage()}" ); if ( $exception instanceof DBConnectionError ) { $this->setLastError( BagOStuff::ERR_UNREACHABLE ); - wfDebug( __METHOD__ . ": ignoring connection error\n" ); + $this->logger->debug( __METHOD__ . ": ignoring connection error" ); } else { $this->setLastError( BagOStuff::ERR_UNEXPECTED ); - wfDebug( __METHOD__ . ": ignoring query error\n" ); + $this->logger->debug( __METHOD__ . ": ignoring query error" ); } } @@ -723,18 +733,21 @@ class SqlBagOStuff extends BagOStuff { $this->markServerDown( $exception, $serverIndex ); } if ( $exception->db && $exception->db->wasReadOnlyError() ) { - try { - $exception->db->rollback( __METHOD__ ); - } catch ( DBError $e ) { + if ( $exception->db->trxLevel() ) { + try { + $exception->db->rollback( __METHOD__ ); + } catch ( DBError $e ) { + } } } - wfDebugLog( 'SQLBagOStuff', "DBError: {$exception->getMessage()}" ); + + $this->logger->error( "DBError: {$exception->getMessage()}" ); if ( $exception instanceof DBConnectionError ) { $this->setLastError( BagOStuff::ERR_UNREACHABLE ); - wfDebug( __METHOD__ . ": ignoring connection error\n" ); + $this->logger->debug( __METHOD__ . ": ignoring connection error" ); } else { $this->setLastError( BagOStuff::ERR_UNEXPECTED ); - wfDebug( __METHOD__ . ": ignoring query error\n" ); + $this->logger->debug( __METHOD__ . ": ignoring query error" ); } } @@ -750,12 +763,12 @@ class SqlBagOStuff extends BagOStuff { unset( $this->connFailureTimes[$serverIndex] ); unset( $this->connFailureErrors[$serverIndex] ); } else { - wfDebug( __METHOD__ . ": Server #$serverIndex already down\n" ); + $this->logger->debug( __METHOD__ . ": Server #$serverIndex already down" ); return; } } $now = time(); - wfDebug( __METHOD__ . ": Server #$serverIndex down until " . ( $now + 60 ) . "\n" ); + $this->logger->info( __METHOD__ . ": Server #$serverIndex down until " . ( $now + 60 ) ); $this->connFailureTimes[$serverIndex] = $now; $this->connFailureErrors[$serverIndex] = $exception; } @@ -779,9 +792,3 @@ class SqlBagOStuff extends BagOStuff { } } } - -/** - * Backwards compatibility alias - */ -class MediaWikiBagOStuff extends SqlBagOStuff { -} diff --git a/includes/objectcache/WinCacheBagOStuff.php b/includes/objectcache/WinCacheBagOStuff.php deleted file mode 100644 index 78a512ce..00000000 --- a/includes/objectcache/WinCacheBagOStuff.php +++ /dev/null @@ -1,92 +0,0 @@ -isInteger( $val ) ) { - $val = intval( $val ); - } else { - $val = unserialize( $val ); - } - } elseif ( is_null( $val ) ) { - return false; - } - - return $val; - } - - /** - * Store a value in the XCache object cache - * - * @param string $key Cache key - * @param mixed $value Object to store - * @param int $expire Expiration time - * @return bool - */ - public function set( $key, $value, $expire = 0 ) { - if ( !$this->isInteger( $value ) ) { - $value = serialize( $value ); - } - - xcache_set( $key, $value, $expire ); - return true; - } - - /** - * @param mixed $casToken - * @param string $key - * @param mixed $value - * @param int $exptime - * @return bool - */ - public function cas( $casToken, $key, $value, $exptime = 0 ) { - // Can't find any documentation on xcache cas - throw new MWException( "CAS is not implemented in " . __CLASS__ ); - } - - /** - * Remove a value from the XCache object cache - * - * @param string $key Cache key - * @param int $time Not used in this implementation - * @return bool - */ - public function delete( $key, $time = 0 ) { - xcache_unset( $key ); - return true; - } - - /** - * Merge an item. - * XCache does not seem to support any way of performing CAS - this however will - * provide a way to perform CAS-like functionality. - * - * @param string $key - * @param Closure $callback Callback method to be executed - * @param int $exptime Either an interval in seconds or a unix timestamp for expiry - * @param int $attempts The amount of times to attempt a merge in case of failure - * @return bool Success - */ - public function merge( $key, Closure $callback, $exptime = 0, $attempts = 10 ) { - return $this->mergeViaLock( $key, $callback, $exptime, $attempts ); - } - - public function incr( $key, $value = 1 ) { - return xcache_inc( $key, $value ); - } - - public function decr( $key, $value = 1 ) { - return xcache_dec( $key, $value ); - } -} -- cgit v1.2.3-54-g00ecf