diff options
Diffstat (limited to 'includes/objectcache')
-rw-r--r-- | includes/objectcache/APCBagOStuff.php | 30 | ||||
-rw-r--r-- | includes/objectcache/BagOStuff.php | 180 | ||||
-rw-r--r-- | includes/objectcache/DBABagOStuff.php | 307 | ||||
-rw-r--r-- | includes/objectcache/EhcacheBagOStuff.php | 324 | ||||
-rw-r--r-- | includes/objectcache/EmptyBagOStuff.php | 37 | ||||
-rw-r--r-- | includes/objectcache/HashBagOStuff.php | 31 | ||||
-rw-r--r-- | includes/objectcache/MemcachedBagOStuff.php | 58 | ||||
-rw-r--r-- | includes/objectcache/MemcachedClient.php | 274 | ||||
-rw-r--r-- | includes/objectcache/MemcachedPeclBagOStuff.php | 87 | ||||
-rw-r--r-- | includes/objectcache/MemcachedPhpBagOStuff.php | 28 | ||||
-rw-r--r-- | includes/objectcache/MultiWriteBagOStuff.php | 83 | ||||
-rw-r--r-- | includes/objectcache/ObjectCache.php | 28 | ||||
-rw-r--r-- | includes/objectcache/ObjectCacheSessionHandler.php | 31 | ||||
-rw-r--r-- | includes/objectcache/RedisBagOStuff.php | 215 | ||||
-rw-r--r-- | includes/objectcache/SqlBagOStuff.php | 222 | ||||
-rw-r--r-- | includes/objectcache/WinCacheBagOStuff.php | 22 | ||||
-rw-r--r-- | includes/objectcache/XCacheBagOStuff.php | 30 |
17 files changed, 797 insertions, 1190 deletions
diff --git a/includes/objectcache/APCBagOStuff.php b/includes/objectcache/APCBagOStuff.php index 3fb80835..4cbb32df 100644 --- a/includes/objectcache/APCBagOStuff.php +++ b/includes/objectcache/APCBagOStuff.php @@ -28,8 +28,8 @@ */ class APCBagOStuff extends BagOStuff { /** - * @param $key string - * @param $casToken[optional] int + * @param string $key + * @param int $casToken [optional] * @return mixed */ public function get( $key, &$casToken = null ) { @@ -49,9 +49,9 @@ class APCBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $value mixed - * @param $exptime int + * @param string $key + * @param mixed $value + * @param int $exptime * @return bool */ public function set( $key, $value, $exptime = 0 ) { @@ -65,10 +65,10 @@ class APCBagOStuff extends BagOStuff { } /** - * @param $casToken mixed - * @param $key string - * @param $value mixed - * @param $exptime int + * @param mixed $casToken + * @param string $key + * @param mixed $value + * @param int $exptime * @return bool */ public function cas( $casToken, $key, $value, $exptime = 0 ) { @@ -77,8 +77,8 @@ class APCBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $time int + * @param string $key + * @param int $time * @return bool */ public function delete( $key, $time = 0 ) { @@ -88,13 +88,13 @@ class APCBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $callback closure Callback method to be executed + * @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 + * @return bool Success */ - public function merge( $key, closure $callback, $exptime = 0, $attempts = 10 ) { + public function merge( $key, Closure $callback, $exptime = 0, $attempts = 10 ) { return $this->mergeViaLock( $key, $callback, $exptime, $attempts ); } diff --git a/includes/objectcache/BagOStuff.php b/includes/objectcache/BagOStuff.php index 857943ee..1978c3ea 100644 --- a/includes/objectcache/BagOStuff.php +++ b/includes/objectcache/BagOStuff.php @@ -3,7 +3,7 @@ * Classes to cache objects in PHP accelerators, SQL database or DBA files * * Copyright © 2003-2004 Brion Vibber <brion@pobox.com> - * http://www.mediawiki.org/ + * 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 @@ -43,8 +43,16 @@ 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 + * @param bool $bool */ public function setDebug( $bool ) { $this->debugMode = $bool; @@ -55,34 +63,34 @@ abstract class BagOStuff { /** * Get an item with the given key. Returns false if it does not exist. - * @param $key string - * @param $casToken[optional] mixed + * @param string $key + * @param mixed $casToken [optional] * @return mixed Returns false on failure */ abstract public function get( $key, &$casToken = null ); /** * Set an item. - * @param $key string - * @param $value mixed + * @param string $key + * @param mixed $value * @param int $exptime Either an interval in seconds or a unix timestamp for expiry - * @return bool success + * @return bool Success */ abstract public function set( $key, $value, $exptime = 0 ); /** * Check and set an item. - * @param $casToken mixed - * @param $key string - * @param $value mixed + * @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 + * @return bool Success */ abstract public function cas( $casToken, $key, $value, $exptime = 0 ); /** * Delete an item. - * @param $key string + * @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 */ @@ -93,26 +101,26 @@ abstract class BagOStuff { * 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 $key string - * @param $callback closure Callback method to be executed + * @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 + * @return bool Success */ - public function merge( $key, closure $callback, $exptime = 0, $attempts = 10 ) { + public function merge( $key, Closure $callback, $exptime = 0, $attempts = 10 ) { return $this->mergeViaCas( $key, $callback, $exptime, $attempts ); } /** * @see BagOStuff::merge() * - * @param $key string - * @param $callback closure Callback method to be executed + * @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 + * @return bool Success */ - protected function mergeViaCas( $key, closure $callback, $exptime = 0, $attempts = 10 ) { + 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 @@ -135,14 +143,14 @@ abstract class BagOStuff { /** * @see BagOStuff::merge() * - * @param $key string - * @param $callback closure Callback method to be executed + * @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 + * @return bool Success */ - protected function mergeViaLock( $key, closure $callback, $exptime = 0, $attempts = 10 ) { - if ( !$this->lock( $key, 60 ) ) { + protected function mergeViaLock( $key, Closure $callback, $exptime = 0, $attempts = 10 ) { + if ( !$this->lock( $key, 6 ) ) { return false; } @@ -164,14 +172,17 @@ abstract class BagOStuff { } /** - * @param $key string - * @param $timeout integer [optional] - * @return bool success + * @param string $key + * @param int $timeout [optional] + * @return bool Success */ - public function lock( $key, $timeout = 60 ) { + 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) @@ -186,15 +197,19 @@ abstract class BagOStuff { $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 $key string - * @return bool success + * @param string $key + * @return bool Success */ public function unlock( $key ) { return $this->delete( "{$key}:lock" ); @@ -203,11 +218,11 @@ abstract class BagOStuff { /** * Delete all objects expiring before a certain date. * @param string $date The reference date in MW format - * @param $progressCallback callback|bool Optional, a function which will be called + * @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 on success, false if unimplemented + * @return bool Success, false if unimplemented */ public function deleteObjectsExpiringBefore( $date, $progressCallback = false ) { // stub @@ -219,7 +234,7 @@ abstract class BagOStuff { /** * Get an associative array containing the item for each of the keys that have items. * @param array $keys List of strings - * @return Array + * @return array */ public function getMulti( array $keys ) { $res = array(); @@ -233,36 +248,40 @@ abstract class BagOStuff { } /** - * @param $key string - * @param $value mixed - * @param $exptime integer - * @return bool success + * 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 add( $key, $value, $exptime = 0 ) { - if ( $this->get( $key ) === false ) { - return $this->set( $key, $value, $exptime ); + public function setMulti( array $data, $exptime = 0 ) { + $res = true; + foreach ( $data as $key => $value ) { + if ( !$this->set( $key, $value, $exptime ) ) { + $res = false; + } } - return false; // key already set + return $res; } /** - * @param $key string - * @param $value mixed - * @param $exptime int - * @return bool success + * @param string $key + * @param mixed $value + * @param int $exptime + * @return bool Success */ - public function replace( $key, $value, $exptime = 0 ) { - if ( $this->get( $key ) !== false ) { + public function add( $key, $value, $exptime = 0 ) { + if ( $this->get( $key ) === false ) { return $this->set( $key, $value, $exptime ); } - return false; // key not already set + return false; // key already set } /** * Increase stored value of $key by $value while preserving its TTL * @param string $key Key to increase - * @param $value Integer: Value to add to $key (Default 1) - * @return integer|bool New value or false on failure + * @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 ) ) { @@ -282,16 +301,59 @@ abstract class BagOStuff { /** * Decrease stored value of $key by $value while preserving its TTL - * @param $key String - * @param $value Integer - * @return integer + * @param string $key + * @param int $value + * @return int */ public function decr( $key, $value = 1 ) { return $this->incr( $key, - $value ); } /** - * @param $text string + * 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 ) { @@ -302,7 +364,7 @@ abstract class BagOStuff { /** * Convert an optionally relative time to an absolute time - * @param $exptime integer + * @param int $exptime * @return int */ protected function convertExpiry( $exptime ) { @@ -317,8 +379,8 @@ abstract class BagOStuff { * 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 $exptime integer - * @return integer + * @param int $exptime + * @return int */ protected function convertToRelative( $exptime ) { if ( $exptime >= 86400 * 3650 /* 10 years */ ) { @@ -335,7 +397,7 @@ abstract class BagOStuff { /** * Check if a value is an integer * - * @param $value mixed + * @param mixed $value * @return bool */ protected function isInteger( $value ) { diff --git a/includes/objectcache/DBABagOStuff.php b/includes/objectcache/DBABagOStuff.php deleted file mode 100644 index c82b3aa4..00000000 --- a/includes/objectcache/DBABagOStuff.php +++ /dev/null @@ -1,307 +0,0 @@ -<?php -/** - * Object caching using DBA backend. - * - * 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 - */ - -/** - * Cache that uses DBA as a backend. - * Slow due to the need to constantly open and close the file to avoid holding - * writer locks. Intended for development use only, as a memcached workalike - * for systems that don't have it. - * - * On construction you can pass array( 'dir' => '/some/path' ); as a parameter - * to override the default DBA files directory (wfTempDir()). - * - * @ingroup Cache - */ -class DBABagOStuff extends BagOStuff { - var $mHandler, $mFile, $mReader, $mWriter, $mDisabled; - - /** - * @param $params array - */ - public function __construct( $params ) { - global $wgDBAhandler; - - if ( !isset( $params['dir'] ) ) { - $params['dir'] = wfTempDir(); - } - - $this->mFile = $params['dir'] . '/mw-cache-' . wfWikiID() . '.db'; - wfDebug( __CLASS__ . ": using cache file {$this->mFile}\n" ); - $this->mHandler = $wgDBAhandler; - } - - /** - * Encode value and expiry for storage - * @param $value - * @param $expiry - * - * @return string - */ - protected function encode( $value, $expiry ) { - # Convert to absolute time - $expiry = $this->convertExpiry( $expiry ); - - return sprintf( '%010u', intval( $expiry ) ) . ' ' . serialize( $value ); - } - - /** - * @param $blob string - * @return array list containing value first and expiry second - */ - protected function decode( $blob ) { - if ( !is_string( $blob ) ) { - return array( false, 0 ); - } else { - return array( - unserialize( substr( $blob, 11 ) ), - intval( substr( $blob, 0, 10 ) ) - ); - } - } - - /** - * @return resource - */ - protected function getReader() { - if ( file_exists( $this->mFile ) ) { - $handle = dba_open( $this->mFile, 'rl', $this->mHandler ); - } else { - $handle = $this->getWriter(); - } - - if ( !$handle ) { - wfDebug( "Unable to open DBA cache file {$this->mFile}\n" ); - } - - return $handle; - } - - /** - * @return resource - */ - protected function getWriter() { - $handle = dba_open( $this->mFile, 'cl', $this->mHandler ); - - if ( !$handle ) { - wfDebug( "Unable to open DBA cache file {$this->mFile}\n" ); - } - - return $handle; - } - - /** - * @param $key string - * @param $casToken[optional] mixed - * @return mixed - */ - public function get( $key, &$casToken = null ) { - wfProfileIn( __METHOD__ ); - wfDebug( __METHOD__ . "($key)\n" ); - - $handle = $this->getReader(); - if ( !$handle ) { - wfProfileOut( __METHOD__ ); - return false; - } - - $val = dba_fetch( $key, $handle ); - list( $val, $expiry ) = $this->decode( $val ); - - # Must close ASAP because locks are held - dba_close( $handle ); - - if ( $val !== false && $expiry && $expiry < time() ) { - # Key is expired, delete it - $handle = $this->getWriter(); - dba_delete( $key, $handle ); - dba_close( $handle ); - wfDebug( __METHOD__ . ": $key expired\n" ); - $val = false; - } - - $casToken = $val; - - wfProfileOut( __METHOD__ ); - - return $val; - } - - /** - * @param $key string - * @param $value mixed - * @param $exptime int - * @return bool - */ - public function set( $key, $value, $exptime = 0 ) { - wfProfileIn( __METHOD__ ); - wfDebug( __METHOD__ . "($key)\n" ); - - $blob = $this->encode( $value, $exptime ); - - $handle = $this->getWriter(); - if ( !$handle ) { - wfProfileOut( __METHOD__ ); - return false; - } - - $ret = dba_replace( $key, $blob, $handle ); - dba_close( $handle ); - - wfProfileOut( __METHOD__ ); - return $ret; - } - - /** - * @param $casToken mixed - * @param $key string - * @param $value mixed - * @param $exptime int - * @return bool - */ - public function cas( $casToken, $key, $value, $exptime = 0 ) { - wfProfileIn( __METHOD__ ); - wfDebug( __METHOD__ . "($key)\n" ); - - $blob = $this->encode( $value, $exptime ); - - $handle = $this->getWriter(); - if ( !$handle ) { - wfProfileOut( __METHOD__ ); - return false; - } - - // DBA is locked to any other write connection, so we can safely - // compare the current & previous value before saving new value - $val = dba_fetch( $key, $handle ); - list( $val, $exptime ) = $this->decode( $val ); - if ( $casToken !== $val ) { - dba_close( $handle ); - wfProfileOut( __METHOD__ ); - return false; - } - - $ret = dba_replace( $key, $blob, $handle ); - dba_close( $handle ); - - wfProfileOut( __METHOD__ ); - return $ret; - } - - /** - * @param $key string - * @param $time int - * @return bool - */ - public function delete( $key, $time = 0 ) { - wfProfileIn( __METHOD__ ); - wfDebug( __METHOD__ . "($key)\n" ); - - $handle = $this->getWriter(); - if ( !$handle ) { - wfProfileOut( __METHOD__ ); - return false; - } - - $ret = !dba_exists( $key, $handle ) || dba_delete( $key, $handle ); - dba_close( $handle ); - - wfProfileOut( __METHOD__ ); - return $ret; - } - - /** - * @param $key string - * @param $value mixed - * @param $exptime int - * @return bool - */ - public function add( $key, $value, $exptime = 0 ) { - wfProfileIn( __METHOD__ ); - - $blob = $this->encode( $value, $exptime ); - - $handle = $this->getWriter(); - - if ( !$handle ) { - wfProfileOut( __METHOD__ ); - return false; - } - - $ret = dba_insert( $key, $blob, $handle ); - - # Insert failed, check to see if it failed due to an expired key - if ( !$ret ) { - list( , $expiry ) = $this->decode( dba_fetch( $key, $handle ) ); - - if ( $expiry && $expiry < time() ) { - # Yes expired, delete and try again - dba_delete( $key, $handle ); - $ret = dba_insert( $key, $blob, $handle ); - # This time if it failed then it will be handled by the caller like any other race - } - } - - dba_close( $handle ); - - wfProfileOut( __METHOD__ ); - return $ret; - } - - /** - * @param $key string - * @param $step integer - * @return integer|bool - */ - public function incr( $key, $step = 1 ) { - wfProfileIn( __METHOD__ ); - - $handle = $this->getWriter(); - - if ( !$handle ) { - wfProfileOut( __METHOD__ ); - return false; - } - - list( $value, $expiry ) = $this->decode( dba_fetch( $key, $handle ) ); - if ( $value !== false ) { - if ( $expiry && $expiry < time() ) { - # Key is expired, delete it - dba_delete( $key, $handle ); - wfDebug( __METHOD__ . ": $key expired\n" ); - $value = false; - } else { - $value += $step; - $blob = $this->encode( $value, $expiry ); - - $ret = dba_replace( $key, $blob, $handle ); - $value = $ret ? $value : false; - } - } - - dba_close( $handle ); - - wfProfileOut( __METHOD__ ); - - return ( $value === false ) ? false : (int)$value; - } -} diff --git a/includes/objectcache/EhcacheBagOStuff.php b/includes/objectcache/EhcacheBagOStuff.php deleted file mode 100644 index 960668f5..00000000 --- a/includes/objectcache/EhcacheBagOStuff.php +++ /dev/null @@ -1,324 +0,0 @@ -<?php -/** - * Object caching using the Ehcache RESTful web service. - * - * 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 - */ - -/** - * Client for the Ehcache RESTful web service - http://ehcache.org/documentation/cache_server.html - * TODO: Simplify configuration and add to the installer. - * - * @ingroup Cache - */ -class EhcacheBagOStuff extends BagOStuff { - var $servers, $cacheName, $connectTimeout, $timeout, $curlOptions, - $requestData, $requestDataPos; - - var $curls = array(); - - /** - * @param $params array - * @throws MWException - */ - function __construct( $params ) { - if ( !defined( 'CURLOPT_TIMEOUT_MS' ) ) { - throw new MWException( __CLASS__ . ' requires curl version 7.16.2 or later.' ); - } - if ( !extension_loaded( 'zlib' ) ) { - throw new MWException( __CLASS__ . ' requires the zlib extension' ); - } - if ( !isset( $params['servers'] ) ) { - throw new MWException( __METHOD__ . ': servers parameter is required' ); - } - $this->servers = $params['servers']; - $this->cacheName = isset( $params['cache'] ) ? $params['cache'] : 'mw'; - $this->connectTimeout = isset( $params['connectTimeout'] ) - ? $params['connectTimeout'] : 1; - $this->timeout = isset( $params['timeout'] ) ? $params['timeout'] : 1; - $this->curlOptions = array( - CURLOPT_CONNECTTIMEOUT_MS => intval( $this->connectTimeout * 1000 ), - CURLOPT_TIMEOUT_MS => intval( $this->timeout * 1000 ), - CURLOPT_RETURNTRANSFER => 1, - CURLOPT_CUSTOMREQUEST => 'GET', - CURLOPT_POST => 0, - CURLOPT_POSTFIELDS => '', - CURLOPT_HTTPHEADER => array(), - ); - } - - /** - * @param $key string - * @param $casToken[optional] mixed - * @return bool|mixed - */ - public function get( $key, &$casToken = null ) { - wfProfileIn( __METHOD__ ); - $response = $this->doItemRequest( $key ); - if ( !$response || $response['http_code'] == 404 ) { - wfProfileOut( __METHOD__ ); - return false; - } - if ( $response['http_code'] >= 300 ) { - wfDebug( __METHOD__ . ": GET failure, got HTTP {$response['http_code']}\n" ); - wfProfileOut( __METHOD__ ); - return false; - } - $body = $response['body']; - $type = $response['content_type']; - if ( $type == 'application/vnd.php.serialized+deflate' ) { - $body = gzinflate( $body ); - if ( !$body ) { - wfDebug( __METHOD__ . ": error inflating $key\n" ); - wfProfileOut( __METHOD__ ); - return false; - } - $data = unserialize( $body ); - } elseif ( $type == 'application/vnd.php.serialized' ) { - $data = unserialize( $body ); - } else { - wfDebug( __METHOD__ . ": unknown content type \"$type\"\n" ); - wfProfileOut( __METHOD__ ); - return false; - } - - $casToken = $body; - - wfProfileOut( __METHOD__ ); - return $data; - } - - /** - * @param $key string - * @param $value mixed - * @param $expiry int - * @return bool - */ - public function set( $key, $value, $expiry = 0 ) { - wfProfileIn( __METHOD__ ); - $expiry = $this->convertExpiry( $expiry ); - $ttl = $expiry ? $expiry - time() : 2147483647; - $blob = serialize( $value ); - if ( strlen( $blob ) > 100 ) { - $blob = gzdeflate( $blob ); - $contentType = 'application/vnd.php.serialized+deflate'; - } else { - $contentType = 'application/vnd.php.serialized'; - } - - $code = $this->attemptPut( $key, $blob, $contentType, $ttl ); - - if ( $code == 404 ) { - // Maybe the cache does not exist yet, let's try creating it - if ( !$this->createCache( $key ) ) { - wfDebug( __METHOD__ . ": cache creation failed\n" ); - wfProfileOut( __METHOD__ ); - return false; - } - $code = $this->attemptPut( $key, $blob, $contentType, $ttl ); - } - - $result = false; - if ( !$code ) { - wfDebug( __METHOD__ . ": PUT failure for key $key\n" ); - } elseif ( $code >= 300 ) { - wfDebug( __METHOD__ . ": PUT failure for key $key: HTTP $code\n" ); - } else { - $result = true; - } - - wfProfileOut( __METHOD__ ); - return $result; - } - - /** - * @param $casToken mixed - * @param $key string - * @param $value mixed - * @param $exptime int - * @return bool - */ - public function cas( $casToken, $key, $value, $exptime = 0 ) { - // Not sure if we can implement CAS for ehcache. There appears to be CAS-support per - // http://ehcache.org/documentation/get-started/consistency-options#cas-cache-operations, - // but I can't find any docs for our current implementation. - throw new MWException( "CAS is not implemented in " . __CLASS__ ); - } - - /** - * @param $key string - * @param $time int - * @return bool - */ - public function delete( $key, $time = 0 ) { - wfProfileIn( __METHOD__ ); - $response = $this->doItemRequest( $key, - array( CURLOPT_CUSTOMREQUEST => 'DELETE' ) ); - $code = isset( $response['http_code'] ) ? $response['http_code'] : 0; - if ( !$response || ( $code != 404 && $code >= 300 ) ) { - wfDebug( __METHOD__ . ": DELETE failure for key $key\n" ); - $result = false; - } else { - $result = true; - } - wfProfileOut( __METHOD__ ); - return $result; - } - - /** - * @see BagOStuff::merge() - * @return bool success - */ - public function merge( $key, closure $callback, $exptime = 0, $attempts = 10 ) { - return $this->mergeViaLock( $key, $callback, $exptime, $attempts ); - } - - /** - * @param $key string - * @return string - */ - protected function getCacheUrl( $key ) { - if ( count( $this->servers ) == 1 ) { - $server = reset( $this->servers ); - } else { - // Use consistent hashing - $hashes = array(); - foreach ( $this->servers as $server ) { - $hashes[$server] = md5( $server . '/' . $key ); - } - asort( $hashes ); - reset( $hashes ); - $server = key( $hashes ); - } - return "http://$server/ehcache/rest/{$this->cacheName}"; - } - - /** - * Get a cURL handle for the given cache URL. - * We cache the handles to allow keepalive. - */ - protected function getCurl( $cacheUrl ) { - if ( !isset( $this->curls[$cacheUrl] ) ) { - $this->curls[$cacheUrl] = curl_init(); - } - return $this->curls[$cacheUrl]; - } - - /** - * @param $key string - * @param $data - * @param $type - * @param $ttl - * @return int - */ - protected function attemptPut( $key, $data, $type, $ttl ) { - // In initial benchmarking, it was 30 times faster to use CURLOPT_POST - // than CURLOPT_UPLOAD with CURLOPT_READFUNCTION. This was because - // CURLOPT_UPLOAD was pushing the request headers first, then waiting - // for an ACK packet, then sending the data, whereas CURLOPT_POST just - // sends the headers and the data in a single send(). - $response = $this->doItemRequest( $key, - array( - CURLOPT_POST => 1, - CURLOPT_CUSTOMREQUEST => 'PUT', - CURLOPT_POSTFIELDS => $data, - CURLOPT_HTTPHEADER => array( - 'Content-Type: ' . $type, - 'ehcacheTimeToLiveSeconds: ' . $ttl - ) - ) - ); - if ( !$response ) { - return 0; - } else { - return $response['http_code']; - } - } - - /** - * @param $key string - * @return bool - */ - protected function createCache( $key ) { - wfDebug( __METHOD__ . ": creating cache for $key\n" ); - $response = $this->doCacheRequest( $key, - array( - CURLOPT_POST => 1, - CURLOPT_CUSTOMREQUEST => 'PUT', - CURLOPT_POSTFIELDS => '', - ) ); - if ( !$response ) { - wfDebug( __CLASS__ . ": failed to create cache for $key\n" ); - return false; - } - return ( $response['http_code'] == 201 /* created */ - || $response['http_code'] == 409 /* already there */ ); - } - - /** - * @param $key string - * @param $curlOptions array - * @return array|bool|mixed - */ - protected function doCacheRequest( $key, $curlOptions = array() ) { - $cacheUrl = $this->getCacheUrl( $key ); - $curl = $this->getCurl( $cacheUrl ); - return $this->doRequest( $curl, $cacheUrl, $curlOptions ); - } - - /** - * @param $key string - * @param $curlOptions array - * @return array|bool|mixed - */ - protected function doItemRequest( $key, $curlOptions = array() ) { - $cacheUrl = $this->getCacheUrl( $key ); - $curl = $this->getCurl( $cacheUrl ); - $url = $cacheUrl . '/' . rawurlencode( $key ); - return $this->doRequest( $curl, $url, $curlOptions ); - } - - /** - * @param $curl - * @param $url string - * @param $curlOptions array - * @return array|bool|mixed - * @throws MWException - */ - protected function doRequest( $curl, $url, $curlOptions = array() ) { - if ( array_diff_key( $curlOptions, $this->curlOptions ) ) { - // var_dump( array_diff_key( $curlOptions, $this->curlOptions ) ); - throw new MWException( __METHOD__ . ": to prevent options set in one doRequest() " . - "call from affecting subsequent doRequest() calls, only options listed " . - "in \$this->curlOptions may be specified in the \$curlOptions parameter." ); - } - $curlOptions += $this->curlOptions; - $curlOptions[CURLOPT_URL] = $url; - - curl_setopt_array( $curl, $curlOptions ); - $result = curl_exec( $curl ); - if ( $result === false ) { - wfDebug( __CLASS__ . ": curl error: " . curl_error( $curl ) . "\n" ); - return false; - } - $info = curl_getinfo( $curl ); - $info['body'] = $result; - return $info; - } -} diff --git a/includes/objectcache/EmptyBagOStuff.php b/includes/objectcache/EmptyBagOStuff.php index 62060579..9595b83c 100644 --- a/includes/objectcache/EmptyBagOStuff.php +++ b/includes/objectcache/EmptyBagOStuff.php @@ -29,8 +29,8 @@ class EmptyBagOStuff extends BagOStuff { /** - * @param $key string - * @param $casToken[optional] mixed + * @param string $key + * @param mixed $casToken [optional] * @return bool */ function get( $key, &$casToken = null ) { @@ -38,9 +38,9 @@ class EmptyBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $value mixed - * @param $exp int + * @param string $key + * @param mixed $value + * @param int $exp * @return bool */ function set( $key, $value, $exp = 0 ) { @@ -48,10 +48,10 @@ class EmptyBagOStuff extends BagOStuff { } /** - * @param $casToken mixed - * @param $key string - * @param $value mixed - * @param $exp int + * @param mixed $casToken + * @param string $key + * @param mixed $value + * @param int $exp * @return bool */ function cas( $casToken, $key, $value, $exp = 0 ) { @@ -59,8 +59,8 @@ class EmptyBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $time int + * @param string $key + * @param int $time * @return bool */ function delete( $key, $time = 0 ) { @@ -68,20 +68,13 @@ class EmptyBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $callback closure Callback method to be executed + * @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 + * @return bool Success */ - public function merge( $key, closure $callback, $exptime = 0, $attempts = 10 ) { + public function merge( $key, Closure $callback, $exptime = 0, $attempts = 10 ) { return true; } } - -/** - * Backwards compatibility alias for EmptyBagOStuff - * @deprecated since 1.18 - */ -class FakeMemCachedClient extends EmptyBagOStuff { -} diff --git a/includes/objectcache/HashBagOStuff.php b/includes/objectcache/HashBagOStuff.php index d061eff0..6e50a8c3 100644 --- a/includes/objectcache/HashBagOStuff.php +++ b/includes/objectcache/HashBagOStuff.php @@ -28,14 +28,15 @@ * @ingroup Cache */ class HashBagOStuff extends BagOStuff { - var $bag; + /** @var array */ + protected $bag; function __construct() { $this->bag = array(); } /** - * @param $key string + * @param string $key * @return bool */ protected function expire( $key ) { @@ -51,8 +52,8 @@ class HashBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $casToken[optional] mixed + * @param string $key + * @param mixed $casToken [optional] * @return bool|mixed */ function get( $key, &$casToken = null ) { @@ -64,15 +65,15 @@ class HashBagOStuff extends BagOStuff { return false; } - $casToken = $this->bag[$key][0]; + $casToken = serialize( $this->bag[$key][0] ); return $this->bag[$key][0]; } /** - * @param $key string - * @param $value mixed - * @param $exptime int + * @param string $key + * @param mixed $value + * @param int $exptime * @return bool */ function set( $key, $value, $exptime = 0 ) { @@ -81,14 +82,14 @@ class HashBagOStuff extends BagOStuff { } /** - * @param $casToken mixed - * @param $key string - * @param $value mixed - * @param $exptime int + * @param mixed $casToken + * @param string $key + * @param mixed $value + * @param int $exptime * @return bool */ function cas( $casToken, $key, $value, $exptime = 0 ) { - if ( $this->get( $key ) === $casToken ) { + if ( serialize( $this->get( $key ) ) === $casToken ) { return $this->set( $key, $value, $exptime ); } @@ -96,8 +97,8 @@ class HashBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $time int + * @param string $key + * @param int $time * @return bool */ function delete( $key, $time = 0 ) { diff --git a/includes/objectcache/MemcachedBagOStuff.php b/includes/objectcache/MemcachedBagOStuff.php index f1644edb..53edcdde 100644 --- a/includes/objectcache/MemcachedBagOStuff.php +++ b/includes/objectcache/MemcachedBagOStuff.php @@ -32,6 +32,8 @@ class MemcachedBagOStuff extends BagOStuff { /** * Fill in the defaults for any parameters missing from $params, using the * backwards-compatible global variables + * @param array $params + * @return array */ protected function applyDefaultParams( $params ) { if ( !isset( $params['servers'] ) ) { @@ -56,18 +58,18 @@ class MemcachedBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $casToken[optional] mixed - * @return Mixed + * @param string $key + * @param mixed $casToken [optional] + * @return mixed */ public function get( $key, &$casToken = null ) { return $this->client->get( $this->encodeKey( $key ), $casToken ); } /** - * @param $key string - * @param $value - * @param $exptime int + * @param string $key + * @param mixed $value + * @param int $exptime * @return bool */ public function set( $key, $value, $exptime = 0 ) { @@ -76,10 +78,10 @@ class MemcachedBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $casToken mixed - * @param $value - * @param $exptime int + * @param mixed $casToken + * @param string $key + * @param mixed $value + * @param int $exptime * @return bool */ public function cas( $casToken, $key, $value, $exptime = 0 ) { @@ -88,8 +90,8 @@ class MemcachedBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $time int + * @param string $key + * @param int $time * @return bool */ public function delete( $key, $time = 0 ) { @@ -97,10 +99,10 @@ class MemcachedBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $value int + * @param string $key + * @param int $value * @param int $exptime (default 0) - * @return Mixed + * @return mixed */ public function add( $key, $value, $exptime = 0 ) { return $this->client->add( $this->encodeKey( $key ), $value, @@ -108,19 +110,9 @@ class MemcachedBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $value int - * @param $exptime - * @return Mixed - */ - public function replace( $key, $value, $exptime = 0 ) { - return $this->client->replace( $this->encodeKey( $key ), $value, - $this->fixExpiry( $exptime ) ); - } - - /** * Get the underlying client object. This is provided for debugging * purposes. + * @return BagOStuff */ public function getClient() { return $this->client; @@ -133,7 +125,7 @@ class MemcachedBagOStuff extends BagOStuff { * the other control characters for compatibility with libmemcached * verify_key. We leave other punctuation alone, to maximise backwards * compatibility. - * @param $key string + * @param string $key * @return string */ public function encodeKey( $key ) { @@ -142,7 +134,7 @@ class MemcachedBagOStuff extends BagOStuff { } /** - * @param $m array + * @param array $m * @return string */ protected function encodeKeyCallback( $m ) { @@ -155,19 +147,21 @@ class MemcachedBagOStuff extends BagOStuff { * discarded immediately because the expiry is in the past. * Clamp expiries >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 */ function fixExpiry( $expiry ) { if ( $expiry > 2592000 && $expiry < 1000000000 ) { $expiry = 2592000; } - return $expiry; + return (int)$expiry; } /** * Decode a key encoded with encodeKey(). This is provided as a convenience * function for debugging. * - * @param $key string + * @param string $key * * @return string */ @@ -177,11 +171,9 @@ class MemcachedBagOStuff extends BagOStuff { /** * Send a debug message to the log + * @param string $text */ protected function debugLog( $text ) { - if ( substr( $text, -1 ) !== "\n" ) { - $text .= "\n"; - } wfDebugLog( 'memcached', $text ); } } diff --git a/includes/objectcache/MemcachedClient.php b/includes/objectcache/MemcachedClient.php index e5f60b55..41eebfb5 100644 --- a/includes/objectcache/MemcachedClient.php +++ b/includes/objectcache/MemcachedClient.php @@ -1,4 +1,5 @@ <?php +// @codingStandardsIgnoreFile It's an external lib and it isn't. Let's not bother. /** * Memcached client for PHP. * @@ -102,10 +103,10 @@ class MWMemcached { /** * Command statistics * - * @var array - * @access public + * @var array + * @access public */ - var $stats; + public $stats; // }}} // {{{ private @@ -113,124 +114,124 @@ class MWMemcached { /** * Cached Sockets that are connected * - * @var array - * @access private + * @var array + * @access private */ - var $_cache_sock; + public $_cache_sock; /** * Current debug status; 0 - none to 9 - profiling * - * @var boolean - * @access private + * @var bool + * @access private */ - var $_debug; + public $_debug; /** * Dead hosts, assoc array, 'host'=>'unixtime when ok to check again' * - * @var array - * @access private + * @var array + * @access private */ - var $_host_dead; + public $_host_dead; /** * Is compression available? * - * @var boolean - * @access private + * @var bool + * @access private */ - var $_have_zlib; + public $_have_zlib; /** * Do we want to use compression? * - * @var boolean - * @access private + * @var bool + * @access private */ - var $_compress_enable; + public $_compress_enable; /** * At how many bytes should we compress? * - * @var integer - * @access private + * @var int + * @access private */ - var $_compress_threshold; + public $_compress_threshold; /** * Are we using persistent links? * - * @var boolean - * @access private + * @var bool + * @access private */ - var $_persistent; + public $_persistent; /** * If only using one server; contains ip:port to connect to * - * @var string - * @access private + * @var string + * @access private */ - var $_single_sock; + public $_single_sock; /** * Array containing ip:port or array(ip:port, weight) * - * @var array - * @access private + * @var array + * @access private */ - var $_servers; + public $_servers; /** * Our bit buckets * - * @var array - * @access private + * @var array + * @access private */ - var $_buckets; + public $_buckets; /** * Total # of bit buckets we have * - * @var integer - * @access private + * @var int + * @access private */ - var $_bucketcount; + public $_bucketcount; /** * # of total servers we have * - * @var integer - * @access private + * @var int + * @access private */ - var $_active; + public $_active; /** * Stream timeout in seconds. Applies for example to fread() * - * @var integer - * @access private + * @var int + * @access private */ - var $_timeout_seconds; + public $_timeout_seconds; /** * Stream timeout in microseconds * - * @var integer - * @access private + * @var int + * @access private */ - var $_timeout_microseconds; + public $_timeout_microseconds; /** * Connect timeout in seconds */ - var $_connect_timeout; + public $_connect_timeout; /** * Number of connection attempts for each server */ - var $_connect_attempts; + public $_connect_attempts; // }}} // }}} @@ -243,7 +244,7 @@ class MWMemcached { * * @param array $args Associative array of settings * - * @return mixed + * @return mixed */ public function __construct( $args ) { $this->set_servers( isset( $args['servers'] ) ? $args['servers'] : array() ); @@ -271,15 +272,15 @@ class MWMemcached { * Adds a key/value to the memcache server if one isn't already set with * that key * - * @param string $key key to set with data - * @param $val Mixed: value to store - * @param $exp Integer: (optional) Expiration time. This can be a number of seconds + * @param string $key Key to set with data + * @param mixed $val Value to store + * @param int $exp (optional) Expiration time. This can be a number of seconds * to cache for (up to 30 days inclusive). Any timespans of 30 days + 1 second or * longer must be the timestamp of the time at which the mapping should expire. It * is safe to use timestamps in all cases, regardless of expiration * eg: strtotime("+3 hour") * - * @return Boolean + * @return bool */ public function add( $key, $val, $exp = 0 ) { return $this->_set( 'add', $key, $val, $exp ); @@ -291,10 +292,10 @@ class MWMemcached { /** * Decrease a value stored on the memcache server * - * @param string $key key to decrease - * @param $amt Integer: (optional) amount to decrease + * @param string $key Key to decrease + * @param int $amt (optional) amount to decrease * - * @return Mixed: FALSE on failure, value on success + * @return mixed False on failure, value on success */ public function decr( $key, $amt = 1 ) { return $this->_incrdecr( 'decr', $key, $amt ); @@ -306,10 +307,10 @@ class MWMemcached { /** * Deletes a key from the server, optionally after $time * - * @param string $key key to delete - * @param $time Integer: (optional) how long to wait before deleting + * @param string $key Key to delete + * @param int $time (optional) how long to wait before deleting * - * @return Boolean: TRUE on success, FALSE on failure + * @return bool True on success, false on failure */ public function delete( $key, $time = 0 ) { if ( !$this->_active ) { @@ -346,8 +347,8 @@ class MWMemcached { } /** - * @param $key - * @param $timeout int + * @param string $key + * @param int $timeout * @return bool */ public function lock( $key, $timeout = 0 ) { @@ -356,7 +357,7 @@ class MWMemcached { } /** - * @param $key + * @param string $key * @return bool */ public function unlock( $key ) { @@ -384,7 +385,7 @@ class MWMemcached { /** * Enable / Disable compression * - * @param $enable Boolean: TRUE to enable, FALSE to disable + * @param bool $enable True to enable, false to disable */ public function enable_compress( $enable ) { $this->_compress_enable = $enable; @@ -407,9 +408,9 @@ class MWMemcached { * Retrieves the value associated with the key from the memcache server * * @param array|string $key key to retrieve - * @param $casToken[optional] Float + * @param float $casToken [optional] * - * @return Mixed + * @return mixed */ public function get( $key, &$casToken = null ) { wfProfileIn( __METHOD__ ); @@ -418,6 +419,12 @@ class MWMemcached { $this->_debugprint( "get($key)\n" ); } + 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; @@ -466,9 +473,9 @@ class MWMemcached { /** * Get multiple keys from the server(s) * - * @param array $keys keys to retrieve + * @param array $keys Keys to retrieve * - * @return Array + * @return array */ public function get_multi( $keys ) { if ( !$this->_active ) { @@ -530,10 +537,10 @@ class MWMemcached { /** * Increments $key (optionally) by $amt * - * @param string $key key to increment - * @param $amt Integer: (optional) amount to increment + * @param string $key Key to increment + * @param int $amt (optional) amount to increment * - * @return Integer: null if the key does not exist yet (this does NOT + * @return int|null Null if the key does not exist yet (this does NOT * create new mappings if the key does not exist). If the key does * exist, this returns the new value for that key. */ @@ -547,15 +554,15 @@ class MWMemcached { /** * Overwrites an existing value for key; only works if key is already set * - * @param string $key key to set value as - * @param $value Mixed: value to store - * @param $exp Integer: (optional) Expiration time. This can be a number of seconds + * @param string $key Key to set value as + * @param mixed $value Value to store + * @param int $exp (optional) Expiration time. This can be a number of seconds * to cache for (up to 30 days inclusive). Any timespans of 30 days + 1 second or * longer must be the timestamp of the time at which the mapping should expire. It * is safe to use timestamps in all cases, regardless of exipration * eg: strtotime("+3 hour") * - * @return Boolean + * @return bool */ public function replace( $key, $value, $exp = 0 ) { return $this->_set( 'replace', $key, $value, $exp ); @@ -568,10 +575,10 @@ class MWMemcached { * Passes through $cmd to the memcache server connected by $sock; returns * output as an array (null array if no output) * - * @param $sock Resource: socket to send command on - * @param string $cmd command to run + * @param Resource $sock Socket to send command on + * @param string $cmd Command to run * - * @return Array: output array + * @return array Output array */ public function run_command( $sock, $cmd ) { if ( !is_resource( $sock ) ) { @@ -603,15 +610,15 @@ class MWMemcached { * Unconditionally sets a key to a given value in the memcache. Returns true * if set successfully. * - * @param string $key key to set value as - * @param $value Mixed: value to set - * @param $exp Integer: (optional) Expiration time. This can be a number of seconds + * @param string $key Key to set value as + * @param mixed $value Value to set + * @param int $exp (optional) Expiration time. This can be a number of seconds * to cache for (up to 30 days inclusive). Any timespans of 30 days + 1 second or * longer must be the timestamp of the time at which the mapping should expire. It * is safe to use timestamps in all cases, regardless of exipration * eg: strtotime("+3 hour") * - * @return Boolean: TRUE on success + * @return bool True on success */ public function set( $key, $value, $exp = 0 ) { return $this->_set( 'set', $key, $value, $exp ); @@ -624,16 +631,16 @@ class MWMemcached { * Sets a key to a given value in the memcache if the current value still corresponds * to a known, given value. Returns true if set successfully. * - * @param $casToken Float: current known value - * @param string $key key to set value as - * @param $value Mixed: value to set - * @param $exp Integer: (optional) Expiration time. This can be a number of seconds + * @param float $casToken Current known value + * @param string $key Key to set value as + * @param mixed $value Value to set + * @param int $exp (optional) Expiration time. This can be a number of seconds * to cache for (up to 30 days inclusive). Any timespans of 30 days + 1 second or * longer must be the timestamp of the time at which the mapping should expire. It * is safe to use timestamps in all cases, regardless of exipration * eg: strtotime("+3 hour") * - * @return Boolean: TRUE on success + * @return bool True on success */ public function cas( $casToken, $key, $value, $exp = 0 ) { return $this->_set( 'cas', $key, $value, $exp, $casToken ); @@ -645,7 +652,7 @@ class MWMemcached { /** * Sets the compression threshold * - * @param $thresh Integer: threshold to compress if larger than + * @param int $thresh Threshold to compress if larger than */ public function set_compress_threshold( $thresh ) { $this->_compress_threshold = $thresh; @@ -657,9 +664,9 @@ class MWMemcached { /** * Sets the debug flag * - * @param $dbg Boolean: TRUE for debugging, FALSE otherwise + * @param bool $dbg True for debugging, false otherwise * - * @see MWMemcached::__construct + * @see MWMemcached::__construct */ public function set_debug( $dbg ) { $this->_debug = $dbg; @@ -671,9 +678,9 @@ class MWMemcached { /** * Sets the server list to distribute key gets and puts between * - * @param array $list of servers to connect to + * @param array $list Array of servers to connect to * - * @see MWMemcached::__construct() + * @see MWMemcached::__construct() */ public function set_servers( $list ) { $this->_servers = $list; @@ -690,8 +697,8 @@ class MWMemcached { /** * Sets the timeout for new connections * - * @param $seconds Integer: number of seconds - * @param $microseconds Integer: number of microseconds + * @param int $seconds Number of seconds + * @param int $microseconds Number of microseconds */ public function set_timeout( $seconds, $microseconds ) { $this->_timeout_seconds = $seconds; @@ -706,9 +713,9 @@ class MWMemcached { /** * Close the specified socket * - * @param string $sock socket to close + * @param string $sock Socket to close * - * @access private + * @access private */ function _close_sock( $sock ) { $host = array_search( $sock, $this->_cache_sock ); @@ -722,14 +729,14 @@ class MWMemcached { /** * Connects $sock to $host, timing out after $timeout * - * @param $sock Integer: socket to connect + * @param int $sock Socket to connect * @param string $host Host:IP to connect to * - * @return boolean - * @access private + * @return bool + * @access private */ function _connect_sock( &$sock, $host ) { - list( $ip, $port ) = explode( ':', $host ); + list( $ip, $port ) = preg_split( '/:(?=\d)/', $host ); $sock = false; $timeout = $this->_connect_timeout; $errno = $errstr = null; @@ -765,9 +772,9 @@ class MWMemcached { /** * Marks a host as dead until 30-40 seconds in the future * - * @param string $sock socket to mark as dead + * @param string $sock Socket to mark as dead * - * @access private + * @access private */ function _dead_sock( $sock ) { $host = array_search( $sock, $this->_cache_sock ); @@ -775,7 +782,7 @@ class MWMemcached { } /** - * @param $host + * @param string $host */ function _dead_host( $host ) { $parts = explode( ':', $host ); @@ -791,9 +798,9 @@ class MWMemcached { /** * get_sock * - * @param string $key key to retrieve value for; + * @param string $key Key to retrieve value for; * - * @return Mixed: resource on success, false on failure + * @return Resource|bool Resource on success, false on failure * @access private */ function get_sock( $key ) { @@ -840,9 +847,9 @@ class MWMemcached { /** * Creates a hash integer based on the $key * - * @param string $key key to hash + * @param string $key Key to hash * - * @return Integer: hash value + * @return int Hash value * @access private */ function _hashfunc( $key ) { @@ -858,11 +865,11 @@ class MWMemcached { /** * Perform increment/decriment on $key * - * @param string $cmd command to perform - * @param string|array $key key to perform it on - * @param $amt Integer amount to adjust + * @param string $cmd Command to perform + * @param string|array $key Key to perform it on + * @param int $amt Amount to adjust * - * @return Integer: new value of $key + * @return int New value of $key * @access private */ function _incrdecr( $cmd, $key, $amt = 1 ) { @@ -899,10 +906,10 @@ class MWMemcached { /** * Load items into $ret from $sock * - * @param $sock Resource: socket to read from + * @param Resource $sock Socket to read from * @param array $ret returned values - * @param $casToken[optional] Float - * @return boolean True for success, false for failure + * @param float $casToken [optional] + * @return bool True for success, false for failure * * @access private */ @@ -985,17 +992,17 @@ class MWMemcached { /** * Performs the requested storage operation to the memcache server * - * @param string $cmd command to perform - * @param string $key key to act on - * @param $val Mixed: what we need to store - * @param $exp Integer: (optional) Expiration time. This can be a number of seconds + * @param string $cmd Command to perform + * @param string $key Key to act on + * @param mixed $val What we need to store + * @param int $exp (optional) Expiration time. This can be a number of seconds * to cache for (up to 30 days inclusive). Any timespans of 30 days + 1 second or * longer must be the timestamp of the time at which the mapping should expire. It * is safe to use timestamps in all cases, regardless of exipration * eg: strtotime("+3 hour") - * @param $casToken[optional] Float + * @param float $casToken [optional] * - * @return Boolean + * @return bool * @access private */ function _set( $cmd, $key, $val, $exp, $casToken = null ) { @@ -1026,9 +1033,9 @@ class MWMemcached { $len = strlen( $val ); - if ( $this->_have_zlib && $this->_compress_enable && - $this->_compress_threshold && $len >= $this->_compress_threshold ) - { + if ( $this->_have_zlib && $this->_compress_enable + && $this->_compress_threshold && $len >= $this->_compress_threshold + ) { $c_val = gzcompress( $val, 9 ); $c_len = strlen( $c_val ); @@ -1070,7 +1077,7 @@ class MWMemcached { * * @param string $host Host:IP to get socket for * - * @return Mixed: IO Stream or false + * @return Resource|bool IO Stream or false * @access private */ function sock_to_host( $host ) { @@ -1100,14 +1107,14 @@ class MWMemcached { } /** - * @param $text string + * @param string $text */ function _debugprint( $text ) { wfDebugLog( 'memcached', $text ); } /** - * @param $text string + * @param string $text */ function _error_log( $text ) { wfDebugLog( 'memcached-serious', "Memcached error: $text" ); @@ -1116,8 +1123,8 @@ class MWMemcached { /** * Write to a stream. If there is an error, mark the socket dead. * - * @param $sock The socket - * @param $buf The string to write + * @param Resource $sock The socket + * @param string $buf The string to write * @return bool True on success, false on failure */ function _fwrite( $sock, $buf ) { @@ -1143,6 +1150,9 @@ class MWMemcached { /** * Handle an I/O error. Mark the socket dead and log an error. + * + * @param Resource $sock + * @param string $msg */ function _handle_error( $sock, $msg ) { $peer = stream_socket_get_name( $sock, true /** remote **/ ); @@ -1161,9 +1171,9 @@ class MWMemcached { * Read the specified number of bytes from a stream. If there is an error, * mark the socket dead. * - * @param $sock The socket - * @param $len The number of bytes to read - * @return The string on success, false on failure. + * @param Resource $sock The socket + * @param int $len The number of bytes to read + * @return string|bool The string on success, false on failure. */ function _fread( $sock, $len ) { $buf = ''; @@ -1193,8 +1203,8 @@ class MWMemcached { * Read a line from a stream. If there is an error, mark the socket dead. * The \r\n line ending is stripped from the response. * - * @param $sock The socket - * @return The string on success, false on failure + * @param Resource $sock The socket + * @return string|bool The string on success, false on failure */ function _fgets( $sock ) { $result = fgets( $sock ); @@ -1223,7 +1233,7 @@ class MWMemcached { /** * Flush the read buffer of a stream - * @param $f Resource + * @param Resource $f */ function _flush_read_buffer( $f ) { if ( !is_resource( $f ) ) { diff --git a/includes/objectcache/MemcachedPeclBagOStuff.php b/includes/objectcache/MemcachedPeclBagOStuff.php index 0c3b228f..c853bcf4 100644 --- a/includes/objectcache/MemcachedPeclBagOStuff.php +++ b/includes/objectcache/MemcachedPeclBagOStuff.php @@ -42,6 +42,7 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { * - serializer: May be either "php" or "igbinary". Igbinary produces more compact * values, but serialization is much slower unless the php.ini option * igbinary.compact_strings is off. + * @param array $params */ function __construct( $params ) { $params = $this->applyDefaultParams( $params ); @@ -113,9 +114,9 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { } /** - * @param $key string - * @param $casToken[optional] float - * @return Mixed + * @param string $key + * @param float $casToken [optional] + * @return mixed */ public function get( $key, &$casToken = null ) { wfProfileIn( __METHOD__ ); @@ -127,9 +128,9 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { } /** - * @param $key string - * @param $value - * @param $exptime int + * @param string $key + * @param mixed $value + * @param int $exptime * @return bool */ public function set( $key, $value, $exptime = 0 ) { @@ -138,10 +139,10 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { } /** - * @param $casToken float - * @param $key string - * @param $value - * @param $exptime int + * @param float $casToken + * @param string $key + * @param mixed $value + * @param int $exptime * @return bool */ public function cas( $casToken, $key, $value, $exptime = 0 ) { @@ -150,8 +151,8 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { } /** - * @param $key string - * @param $time int + * @param string $key + * @param int $time * @return bool */ public function delete( $key, $time = 0 ) { @@ -166,10 +167,10 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { } /** - * @param $key string - * @param $value int - * @param $exptime int - * @return Mixed + * @param string $key + * @param int $value + * @param int $exptime + * @return mixed */ public function add( $key, $value, $exptime = 0 ) { $this->debugLog( "add($key)" ); @@ -177,20 +178,9 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { } /** - * @param $key string - * @param $value int - * @param $exptime - * @return Mixed - */ - public function replace( $key, $value, $exptime = 0 ) { - $this->debugLog( "replace($key)" ); - return $this->checkResult( $key, parent::replace( $key, $value, $exptime ) ); - } - - /** - * @param $key string - * @param $value int - * @return Mixed + * @param string $key + * @param int $value + * @return mixed */ public function incr( $key, $value = 1 ) { $this->debugLog( "incr($key)" ); @@ -199,9 +189,9 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { } /** - * @param $key string - * @param $value int - * @return Mixed + * @param string $key + * @param int $value + * @return mixed */ public function decr( $key, $value = 1 ) { $this->debugLog( "decr($key)" ); @@ -217,8 +207,8 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { * different. * * @param string $key The key used by the caller, or false if there wasn't one. - * @param $result Mixed The return value - * @return Mixed + * @param mixed $result The return value + * @return mixed */ protected function checkResult( $key, $result ) { if ( $result !== false ) { @@ -242,13 +232,14 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { $msg = "Memcached error: $msg"; } wfDebugLog( 'memcached-serious', $msg ); + $this->setLastError( BagOStuff::ERR_UNEXPECTED ); } return $result; } /** - * @param $keys Array - * @return Array + * @param array $keys + * @return array */ public function getMulti( array $keys ) { wfProfileIn( __METHOD__ ); @@ -256,11 +247,27 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff { $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 ); } - /* NOTE: there is no cas() method here because it is currently not supported - * by the BagOStuff interface and other BagOStuff subclasses, such as - * SqlBagOStuff. + /** + * @param array $data + * @param int $exptime + * @return bool */ + public function setMulti( array $data, $exptime = 0 ) { + wfProfileIn( __METHOD__ ); + foreach ( $data as $key => $value ) { + $encKey = $this->encodeKey( $key ); + if ( $encKey !== $key ) { + $data[$encKey] = $value; + unset( $data[$key] ); + } + } + $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 33a134c7..330d2b52 100644 --- a/includes/objectcache/MemcachedPhpBagOStuff.php +++ b/includes/objectcache/MemcachedPhpBagOStuff.php @@ -39,7 +39,7 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff { * - timeout: The read timeout in microseconds * - connect_timeout: The connect timeout in seconds * - * @param $params array + * @param array $params */ function __construct( $params ) { $params = $this->applyDefaultParams( $params ); @@ -50,15 +50,15 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff { } /** - * @param $debug bool + * @param bool $debug */ public function setDebug( $debug ) { $this->client->set_debug( $debug ); } /** - * @param $keys Array - * @return Array + * @param array $keys + * @return array */ public function getMulti( array $keys ) { $callback = array( $this, 'encodeKey' ); @@ -66,8 +66,8 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff { } /** - * @param $key - * @param $timeout int + * @param string $key + * @param int $timeout * @return bool */ public function lock( $key, $timeout = 0 ) { @@ -75,26 +75,26 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff { } /** - * @param $key string - * @return Mixed + * @param string $key + * @return mixed */ public function unlock( $key ) { return $this->client->unlock( $this->encodeKey( $key ) ); } /** - * @param $key string - * @param $value int - * @return Mixed + * @param string $key + * @param int $value + * @return mixed */ public function incr( $key, $value = 1 ) { return $this->client->incr( $this->encodeKey( $key ), $value ); } /** - * @param $key string - * @param $value int - * @return Mixed + * @param string $key + * @param int $value + * @return mixed */ public function decr( $key, $value = 1 ) { return $this->client->decr( $this->encodeKey( $key ), $value ); diff --git a/includes/objectcache/MultiWriteBagOStuff.php b/includes/objectcache/MultiWriteBagOStuff.php index e550c0d0..6a691379 100644 --- a/includes/objectcache/MultiWriteBagOStuff.php +++ b/includes/objectcache/MultiWriteBagOStuff.php @@ -29,7 +29,8 @@ * @ingroup Cache */ class MultiWriteBagOStuff extends BagOStuff { - var $caches; + /** @var array BagOStuff[] */ + protected $caches; /** * Constructor. Parameters are: @@ -38,7 +39,7 @@ class MultiWriteBagOStuff extends BagOStuff { * structures, in the style required by $wgObjectCaches. See * the documentation of $wgObjectCaches for more detail. * - * @param $params array + * @param array $params * @throws MWException */ public function __construct( $params ) { @@ -53,15 +54,15 @@ class MultiWriteBagOStuff extends BagOStuff { } /** - * @param $debug bool + * @param bool $debug */ public function setDebug( $debug ) { $this->doWrite( 'setDebug', $debug ); } /** - * @param $key string - * @param $casToken[optional] mixed + * @param string $key + * @param mixed $casToken [optional] * @return bool|mixed */ public function get( $key, &$casToken = null ) { @@ -75,10 +76,10 @@ class MultiWriteBagOStuff extends BagOStuff { } /** - * @param $casToken mixed - * @param $key string - * @param $value mixed - * @param $exptime int + * @param mixed $casToken + * @param string $key + * @param mixed $value + * @param mixed $exptime * @return bool */ public function cas( $casToken, $key, $value, $exptime = 0 ) { @@ -86,9 +87,9 @@ class MultiWriteBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $value mixed - * @param $exptime int + * @param string $key + * @param mixed $value + * @param int $exptime * @return bool */ public function set( $key, $value, $exptime = 0 ) { @@ -96,8 +97,8 @@ class MultiWriteBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $time int + * @param string $key + * @param int $time * @return bool */ public function delete( $key, $time = 0 ) { @@ -105,9 +106,9 @@ class MultiWriteBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $value mixed - * @param $exptime int + * @param string $key + * @param mixed $value + * @param int $exptime * @return bool */ public function add( $key, $value, $exptime = 0 ) { @@ -115,18 +116,8 @@ class MultiWriteBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $value mixed - * @param $exptime int - * @return bool - */ - public function replace( $key, $value, $exptime = 0 ) { - return $this->doWrite( 'replace', $key, $value, $exptime ); - } - - /** - * @param $key string - * @param $value int + * @param string $key + * @param int $value * @return bool|null */ public function incr( $key, $value = 1 ) { @@ -134,8 +125,8 @@ class MultiWriteBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $value int + * @param string $key + * @param int $value * @return bool */ public function decr( $key, $value = 1 ) { @@ -143,8 +134,8 @@ class MultiWriteBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $timeout int + * @param string $key + * @param int $timeout * @return bool */ public function lock( $key, $timeout = 0 ) { @@ -157,7 +148,7 @@ class MultiWriteBagOStuff extends BagOStuff { } /** - * @param $key string + * @param string $key * @return bool */ public function unlock( $key ) { @@ -169,18 +160,28 @@ class MultiWriteBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $callback closure Callback method to be executed + * @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 + * @return bool Success */ - public function merge( $key, closure $callback, $exptime = 0, $attempts = 10 ) { + public function merge( $key, Closure $callback, $exptime = 0, $attempts = 10 ) { return $this->doWrite( 'merge', $key, $callback, $exptime ); } + public function getLastError() { + return isset( $this->caches[0] ) ? $this->caches[0]->getLastError() : self::ERR_NONE; + } + + public function clearLastError() { + if ( isset( $this->caches[0] ) ) { + $this->caches[0]->clearLastError(); + } + } + /** - * @param $method string + * @param string $method * @return bool */ protected function doWrite( $method /*, ... */ ) { @@ -200,8 +201,8 @@ class MultiWriteBagOStuff extends BagOStuff { * Delete objects expiring before a certain date. * * Succeed if any of the child caches succeed. - * @param $date string - * @param $progressCallback bool|callback + * @param string $date + * @param bool|callable $progressCallback * @return bool */ public function deleteObjectsExpiringBefore( $date, $progressCallback = false ) { diff --git a/includes/objectcache/ObjectCache.php b/includes/objectcache/ObjectCache.php index 6c1433a9..633b34a2 100644 --- a/includes/objectcache/ObjectCache.php +++ b/includes/objectcache/ObjectCache.php @@ -27,12 +27,12 @@ * @ingroup Cache */ class ObjectCache { - static $instances = array(); + public static $instances = array(); /** * Get a cached instance of the specified type of cache object. * - * @param $id string + * @param string $id * * @return BagOStuff */ @@ -56,7 +56,7 @@ class ObjectCache { /** * Create a new cache object of the specified type. * - * @param $id string + * @param string $id * * @throws MWException * @return BagOStuff @@ -75,7 +75,7 @@ class ObjectCache { /** * Create a new cache object from parameters * - * @param $params array + * @param array $params * * @throws MWException * @return BagOStuff @@ -87,8 +87,9 @@ class ObjectCache { $class = $params['class']; return new $class( $params ); } else { - throw new MWException( "The definition of cache type \"" . print_r( $params, true ) . "\" lacks both " . - "factory and class parameters." ); + throw new MWException( "The definition of cache type \"" + . print_r( $params, true ) . "\" lacks both " + . "factory and class parameters." ); } } @@ -101,7 +102,7 @@ class ObjectCache { * be an alias to the configured cache choice for that. * If no cache choice is configured (by default $wgMainCacheType is CACHE_NONE), * then CACHE_ANYTHING will forward to CACHE_DB. - * @param $params array + * @param array $params * @return BagOStuff */ static function newAnything( $params ) { @@ -118,11 +119,15 @@ class ObjectCache { /** * Factory function referenced from DefaultSettings.php for CACHE_ACCEL. * - * @param $params array + * This will look for any APC style server-local cache. + * A fallback cache can be specified if none is found. + * + * @param array $params + * @param int|string $fallback Fallback cache, e.g. (CACHE_NONE, "hash") (since 1.24) * @throws MWException * @return BagOStuff */ - static function newAccelerator( $params ) { + static function newAccelerator( $params, $fallback = null ) { if ( function_exists( 'apc_fetch' ) ) { $id = 'apc'; } elseif ( function_exists( 'xcache_get' ) && wfIniGetBool( 'xcache.var_size' ) ) { @@ -130,6 +135,9 @@ class ObjectCache { } elseif ( function_exists( 'wincache_ucache_get' ) ) { $id = 'wincache'; } else { + if ( $fallback ) { + return self::newFromId( $fallback ); + } throw new MWException( "CACHE_ACCEL requested but no suitable object " . "cache is present. You may want to install APC." ); } @@ -143,7 +151,7 @@ class ObjectCache { * hashing scheme and a different interpretation of the flags bitfield, so * switching between the two clients randomly would be disastrous. * - * @param $params array + * @param array $params * * @return MemcachedPhpBagOStuff */ diff --git a/includes/objectcache/ObjectCacheSessionHandler.php b/includes/objectcache/ObjectCacheSessionHandler.php index 7cf960e7..cdf8da1e 100644 --- a/includes/objectcache/ObjectCacheSessionHandler.php +++ b/includes/objectcache/ObjectCacheSessionHandler.php @@ -49,6 +49,7 @@ class ObjectCacheSessionHandler { /** * Get the cache storage object to use for session storage + * @return ObjectCache */ static function getCache() { global $wgSessionCacheType; @@ -58,8 +59,8 @@ class ObjectCacheSessionHandler { /** * Get a cache key for the given session id. * - * @param string $id session id - * @return String: cache key + * @param string $id Session id + * @return string Cache key */ static function getKey( $id ) { return wfMemcKey( 'session', $id ); @@ -68,9 +69,9 @@ class ObjectCacheSessionHandler { /** * Callback when opening a session. * - * @param $save_path String: path used to store session files, unused - * @param $session_name String: session name - * @return Boolean: success + * @param string $save_path Path used to store session files, unused + * @param string $session_name Session name + * @return bool Success */ static function open( $save_path, $session_name ) { return true; @@ -80,7 +81,7 @@ class ObjectCacheSessionHandler { * Callback when closing a session. * NOP. * - * @return Boolean: success + * @return bool Success */ static function close() { return true; @@ -89,8 +90,8 @@ class ObjectCacheSessionHandler { /** * Callback when reading session data. * - * @param string $id session id - * @return Mixed: session data + * @param string $id Session id + * @return mixed Session data */ static function read( $id ) { $data = self::getCache()->get( self::getKey( $id ) ); @@ -103,9 +104,9 @@ class ObjectCacheSessionHandler { /** * Callback when writing session data. * - * @param string $id session id - * @param $data Mixed: session data - * @return Boolean: success + * @param string $id Session id + * @param mixed $data Session data + * @return bool Success */ static function write( $id, $data ) { global $wgObjectCacheSessionExpiry; @@ -116,8 +117,8 @@ class ObjectCacheSessionHandler { /** * Callback to destroy a session when calling session_destroy(). * - * @param string $id session id - * @return Boolean: success + * @param string $id Session id + * @return bool Success */ static function destroy( $id ) { self::getCache()->delete( self::getKey( $id ) ); @@ -128,8 +129,8 @@ class ObjectCacheSessionHandler { * Callback to execute garbage collection. * NOP: Object caches perform garbage collection implicitly * - * @param $maxlifetime Integer: maximum session life time - * @return Boolean: success + * @param int $maxlifetime Maximum session life time + * @return bool Success */ static function gc( $maxlifetime ) { return true; diff --git a/includes/objectcache/RedisBagOStuff.php b/includes/objectcache/RedisBagOStuff.php index 135e0e83..ae8cc5b7 100644 --- a/includes/objectcache/RedisBagOStuff.php +++ b/includes/objectcache/RedisBagOStuff.php @@ -23,7 +23,7 @@ class RedisBagOStuff extends BagOStuff { /** @var RedisConnectionPool */ protected $redisPool; - /** @var Array List of server names */ + /** @var array List of server names */ protected $servers; /** @var bool */ protected $automaticFailover; @@ -53,9 +53,10 @@ class RedisBagOStuff extends BagOStuff { * consistent hashing algorithm). True by default. This has the * potential to create consistency issues if a server is slow enough to * flap, for example if it is in swap death. + * @param array $params */ function __construct( $params ) { - $redisConf = array( 'serializer' => 'php' ); + $redisConf = array( 'serializer' => 'none' ); // manage that in this class foreach ( array( 'connectTimeout', 'persistent', 'password' ) as $opt ) { if ( isset( $params[$opt] ) ) { $redisConf[$opt] = $params[$opt]; @@ -72,96 +73,88 @@ class RedisBagOStuff extends BagOStuff { } public function get( $key, &$casToken = null ) { - wfProfileIn( __METHOD__ ); + $section = new ProfileSection( __METHOD__ ); + list( $server, $conn ) = $this->getConnection( $key ); if ( !$conn ) { - wfProfileOut( __METHOD__ ); return false; } try { - $result = $conn->get( $key ); + $value = $conn->get( $key ); + $casToken = $value; + $result = $this->unserialize( $value ); } catch ( RedisException $e ) { $result = false; - $this->handleException( $server, $conn, $e ); + $this->handleException( $conn, $e ); } - $casToken = $result; + $this->logRequest( 'get', $key, $server, $result ); - wfProfileOut( __METHOD__ ); return $result; } public function set( $key, $value, $expiry = 0 ) { - wfProfileIn( __METHOD__ ); + $section = new ProfileSection( __METHOD__ ); + list( $server, $conn ) = $this->getConnection( $key ); if ( !$conn ) { - wfProfileOut( __METHOD__ ); return false; } $expiry = $this->convertToRelative( $expiry ); try { - if ( !$expiry ) { - // No expiry, that is very different from zero expiry in Redis - $result = $conn->set( $key, $value ); + if ( $expiry ) { + $result = $conn->setex( $key, $expiry, $this->serialize( $value ) ); } else { - $result = $conn->setex( $key, $expiry, $value ); + // No expiry, that is very different from zero expiry in Redis + $result = $conn->set( $key, $this->serialize( $value ) ); } } catch ( RedisException $e ) { $result = false; - $this->handleException( $server, $conn, $e ); + $this->handleException( $conn, $e ); } $this->logRequest( 'set', $key, $server, $result ); - wfProfileOut( __METHOD__ ); return $result; } public function cas( $casToken, $key, $value, $expiry = 0 ) { - wfProfileIn( __METHOD__ ); + $section = new ProfileSection( __METHOD__ ); + list( $server, $conn ) = $this->getConnection( $key ); if ( !$conn ) { - wfProfileOut( __METHOD__ ); return false; } $expiry = $this->convertToRelative( $expiry ); try { $conn->watch( $key ); - if ( $this->get( $key ) !== $casToken ) { - wfProfileOut( __METHOD__ ); + if ( $this->serialize( $this->get( $key ) ) !== $casToken ) { + $conn->unwatch(); return false; } + // multi()/exec() will fail atomically if the key changed since watch() $conn->multi(); - - if ( !$expiry ) { - // No expiry, that is very different from zero expiry in Redis - $conn->set( $key, $value ); + if ( $expiry ) { + $conn->setex( $key, $expiry, $this->serialize( $value ) ); } else { - $conn->setex( $key, $expiry, $value ); + // No expiry, that is very different from zero expiry in Redis + $conn->set( $key, $this->serialize( $value ) ); } - - /* - * multi()/exec() (transactional mode) allows multiple values to - * be set/get at once and will return an array of results, in - * the order they were set/get. In this case, we only set 1 - * value, which should (in case of success) result in true. - */ $result = ( $conn->exec() == array( true ) ); } catch ( RedisException $e ) { $result = false; - $this->handleException( $server, $conn, $e ); + $this->handleException( $conn, $e ); } $this->logRequest( 'cas', $key, $server, $result ); - wfProfileOut( __METHOD__ ); return $result; } public function delete( $key, $time = 0 ) { - wfProfileIn( __METHOD__ ); + $section = new ProfileSection( __METHOD__ ); + list( $server, $conn ) = $this->getConnection( $key ); if ( !$conn ) { - wfProfileOut( __METHOD__ ); return false; } try { @@ -170,15 +163,16 @@ class RedisBagOStuff extends BagOStuff { $result = true; } catch ( RedisException $e ) { $result = false; - $this->handleException( $server, $conn, $e ); + $this->handleException( $conn, $e ); } + $this->logRequest( 'delete', $key, $server, $result ); - wfProfileOut( __METHOD__ ); return $result; } public function getMulti( array $keys ) { - wfProfileIn( __METHOD__ ); + $section = new ProfileSection( __METHOD__ ); + $batches = array(); $conns = array(); foreach ( $keys as $key ) { @@ -204,78 +198,152 @@ class RedisBagOStuff extends BagOStuff { } foreach ( $batchResult as $i => $value ) { if ( $value !== false ) { - $result[$batchKeys[$i]] = $value; + $result[$batchKeys[$i]] = $this->unserialize( $value ); } } } catch ( RedisException $e ) { - $this->handleException( $server, $conn, $e ); + $this->handleException( $conn, $e ); } } $this->debug( "getMulti for " . count( $keys ) . " keys " . "returned " . count( $result ) . " results" ); - wfProfileOut( __METHOD__ ); return $result; } + /** + * @param array $data + * @param int $expiry + * @return bool + */ + public function setMulti( array $data, $expiry = 0 ) { + $section = new ProfileSection( __METHOD__ ); + + $batches = array(); + $conns = array(); + foreach ( $data as $key => $value ) { + list( $server, $conn ) = $this->getConnection( $key ); + if ( !$conn ) { + continue; + } + $conns[$server] = $conn; + $batches[$server][] = $key; + } + + $expiry = $this->convertToRelative( $expiry ); + $result = true; + foreach ( $batches as $server => $batchKeys ) { + $conn = $conns[$server]; + try { + $conn->multi( Redis::PIPELINE ); + foreach ( $batchKeys as $key ) { + if ( $expiry ) { + $conn->setex( $key, $expiry, $this->serialize( $data[$key] ) ); + } else { + $conn->set( $key, $this->serialize( $data[$key] ) ); + } + } + $batchResult = $conn->exec(); + if ( $batchResult === false ) { + $this->debug( "setMulti request to $server failed" ); + continue; + } + foreach ( $batchResult as $value ) { + if ( $value === false ) { + $result = false; + } + } + } catch ( RedisException $e ) { + $this->handleException( $server, $conn, $e ); + $result = false; + } + } + + return $result; + } + + + public function add( $key, $value, $expiry = 0 ) { - wfProfileIn( __METHOD__ ); + $section = new ProfileSection( __METHOD__ ); + list( $server, $conn ) = $this->getConnection( $key ); if ( !$conn ) { - wfProfileOut( __METHOD__ ); return false; } $expiry = $this->convertToRelative( $expiry ); try { - $result = $conn->setnx( $key, $value ); - if ( $result && $expiry ) { + if ( $expiry ) { + $conn->multi(); + $conn->setnx( $key, $this->serialize( $value ) ); $conn->expire( $key, $expiry ); + $result = ( $conn->exec() == array( true, true ) ); + } else { + $result = $conn->setnx( $key, $this->serialize( $value ) ); } } catch ( RedisException $e ) { $result = false; - $this->handleException( $server, $conn, $e ); + $this->handleException( $conn, $e ); } + $this->logRequest( 'add', $key, $server, $result ); - wfProfileOut( __METHOD__ ); return $result; } /** - * Non-atomic implementation of replace(). Could perhaps be done atomically - * with WATCH or scripting, but this function is rarely used. + * Non-atomic implementation of incr(). + * + * Probably all callers actually want incr() to atomically initialise + * values to zero if they don't exist, as provided by the Redis INCR + * command. But we are constrained by the memcached-like interface to + * return null in that case. Once the key exists, further increments are + * atomic. + * @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 replace( $key, $value, $expiry = 0 ) { - wfProfileIn( __METHOD__ ); + public function incr( $key, $value = 1 ) { + $section = new ProfileSection( __METHOD__ ); + list( $server, $conn ) = $this->getConnection( $key ); if ( !$conn ) { - wfProfileOut( __METHOD__ ); return false; } if ( !$conn->exists( $key ) ) { - wfProfileOut( __METHOD__ ); - return false; + return null; } - - $expiry = $this->convertToRelative( $expiry ); try { - if ( !$expiry ) { - $result = $conn->set( $key, $value ); - } else { - $result = $conn->setex( $key, $expiry, $value ); - } + $result = $conn->incrBy( $key, $value ); } catch ( RedisException $e ) { $result = false; - $this->handleException( $server, $conn, $e ); + $this->handleException( $conn, $e ); } - $this->logRequest( 'replace', $key, $server, $result ); - wfProfileOut( __METHOD__ ); + $this->logRequest( 'incr', $key, $server, $result ); return $result; } + /** + * @param mixed $data + * @return string + */ + protected function serialize( $data ) { + // Serialize anything but integers so INCR/DECR work + // Do not store integer-like strings as integers to avoid type confusion (bug 60563) + return is_int( $data ) ? $data : serialize( $data ); + } + + /** + * @param string $data + * @return mixed + */ + protected function unserialize( $data ) { + return ctype_digit( $data ) ? intval( $data ) : unserialize( $data ); + } /** * Get a Redis object with a connection suitable for fetching the specified key - * @return Array (server, RedisConnRef) or (false, false) + * @param string $key + * @return array (server, RedisConnRef) or (false, false) */ protected function getConnection( $key ) { if ( count( $this->servers ) === 1 ) { @@ -294,14 +362,16 @@ class RedisBagOStuff extends BagOStuff { return array( $server, $conn ); } } + $this->setLastError( BagOStuff::ERR_UNREACHABLE ); return array( false, false ); } /** * Log a fatal error + * @param string $msg */ protected function logError( $msg ) { - wfDebugLog( 'redis', "Redis error: $msg\n" ); + wfDebugLog( 'redis', "Redis error: $msg" ); } /** @@ -309,13 +379,20 @@ class RedisBagOStuff extends BagOStuff { * and protocol errors. Sometimes it also closes the connection, sometimes * not. The safest response for us is to explicitly destroy the connection * object and let it be reopened during the next request. + * @param RedisConnRef $conn + * @param Exception $e */ - protected function handleException( $server, RedisConnRef $conn, $e ) { - $this->redisPool->handleException( $server, $conn, $e ); + protected function handleException( RedisConnRef $conn, $e ) { + $this->setLastError( BagOStuff::ERR_UNEXPECTED ); + $this->redisPool->handleError( $conn, $e ); } /** * Send information about a single request to the debug log + * @param string $method + * @param string $key + * @param string $server + * @param bool $result */ public function logRequest( $method, $key, $server, $result ) { $this->debug( "$method $key on $server: " . diff --git a/includes/objectcache/SqlBagOStuff.php b/includes/objectcache/SqlBagOStuff.php index acf27036..58720790 100644 --- a/includes/objectcache/SqlBagOStuff.php +++ b/includes/objectcache/SqlBagOStuff.php @@ -27,22 +27,37 @@ * @ingroup Cache */ class SqlBagOStuff extends BagOStuff { - /** - * @var LoadBalancer - */ - var $lb; + /** @var LoadBalancer */ + protected $lb; + + protected $serverInfos; + + /** @var array */ + protected $serverNames; + + /** @var int */ + protected $numServers; + + /** @var array */ + protected $conns; + + /** @var int */ + protected $lastExpireAll = 0; + + /** @var int */ + protected $purgePeriod = 100; - var $serverInfos; - var $serverNames; - var $numServers; - var $conns; - var $lastExpireAll = 0; - var $purgePeriod = 100; - var $shards = 1; - var $tableName = 'objectcache'; + /** @var int */ + protected $shards = 1; - protected $connFailureTimes = array(); // UNIX timestamps - protected $connFailureErrors = array(); // exceptions + /** @var string */ + protected $tableName = 'objectcache'; + + /** @var array UNIX timestamps */ + protected $connFailureTimes = array(); + + /** @var array Exceptions */ + protected $connFailureErrors = array(); /** * Constructor. Parameters are: @@ -70,7 +85,7 @@ class SqlBagOStuff extends BagOStuff { * distributed across all tables by key hash. This is for * MySQL bugs 61735 and 61736. * - * @param $params array + * @param array $params */ public function __construct( $params ) { if ( isset( $params['servers'] ) ) { @@ -101,7 +116,7 @@ class SqlBagOStuff extends BagOStuff { /** * Get a connection to the specified database * - * @param $serverIndex integer + * @param int $serverIndex * @return DatabaseBase */ protected function getDB( $serverIndex ) { @@ -114,8 +129,8 @@ class SqlBagOStuff extends BagOStuff { # Don't keep timing out trying to connect for each call if the DB is down if ( isset( $this->connFailureErrors[$serverIndex] ) - && ( time() - $this->connFailureTimes[$serverIndex] ) < 60 ) - { + && ( time() - $this->connFailureTimes[$serverIndex] ) < 60 + ) { throw $this->connFailureErrors[$serverIndex]; } @@ -155,8 +170,8 @@ class SqlBagOStuff extends BagOStuff { /** * Get the server index and table name for a given key - * @param $key string - * @return Array: server index and table name + * @param string $key + * @return array Server index and table name */ protected function getTableByKey( $key ) { if ( $this->shards > 1 ) { @@ -178,7 +193,7 @@ class SqlBagOStuff extends BagOStuff { /** * Get the table name for a given shard index - * @param $index int + * @param int $index * @return string */ protected function getTableNameByShard( $index ) { @@ -192,8 +207,8 @@ class SqlBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $casToken[optional] mixed + * @param string $key + * @param mixed $casToken [optional] * @return mixed */ public function get( $key, &$casToken = null ) { @@ -206,8 +221,8 @@ class SqlBagOStuff extends BagOStuff { } /** - * @param $keys array - * @return Array + * @param array $keys + * @return array */ public function getMulti( array $keys ) { $values = array(); // array of (key => value) @@ -228,7 +243,15 @@ class SqlBagOStuff extends BagOStuff { $res = $db->select( $tableName, array( 'keyname', 'value', 'exptime' ), array( 'keyname' => $tableKeys ), - __METHOD__ ); + __METHOD__, + // Approximate write-on-the-fly BagOStuff API via blocking. + // This approximation fails if a ROLLBACK happens (which is rare). + // We do not want to flush the TRX as that can break callers. + $db->trxLevel() ? array( 'LOCK IN SHARE MODE' ) : array() + ); + if ( $res === false ) { + continue; + } foreach ( $res as $row ) { $row->serverIndex = $serverIndex; $row->tableName = $tableName; @@ -248,14 +271,11 @@ class SqlBagOStuff extends BagOStuff { $db = $this->getDB( $row->serverIndex ); if ( $this->isExpired( $db, $row->exptime ) ) { // MISS $this->debug( "get: key has expired, deleting" ); - $db->begin( __METHOD__ ); # Put the expiry time in the WHERE condition to avoid deleting a # newly-inserted value $db->delete( $row->tableName, array( 'keyname' => $key, 'exptime' => $row->exptime ), __METHOD__ ); - $db->commit( __METHOD__ ); - $values[$key] = false; } else { // HIT $values[$key] = $this->unserialize( $db->decodeBlob( $row->value ) ); } @@ -263,7 +283,6 @@ class SqlBagOStuff extends BagOStuff { $this->handleWriteError( $e, $row->serverIndex ); } } else { // MISS - $values[$key] = false; $this->debug( 'get: no matching rows' ); } } @@ -272,9 +291,77 @@ class SqlBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $value mixed - * @param $exptime int + * @param array $data + * @param int $expiry + * @return bool + */ + public function setMulti( array $data, $expiry = 0 ) { + $keysByTable = array(); + foreach ( $data as $key => $value ) { + list( $serverIndex, $tableName ) = $this->getTableByKey( $key ); + $keysByTable[$serverIndex][$tableName][] = $key; + } + + $this->garbageCollect(); // expire old entries if any + + $result = true; + $exptime = (int)$expiry; + foreach ( $keysByTable as $serverIndex => $serverKeys ) { + try { + $db = $this->getDB( $serverIndex ); + } catch ( DBError $e ) { + $this->handleWriteError( $e, $serverIndex ); + $result = false; + continue; + } + + if ( $exptime < 0 ) { + $exptime = 0; + } + + if ( $exptime == 0 ) { + $encExpiry = $this->getMaxDateTime( $db ); + } else { + if ( $exptime < 3.16e8 ) { # ~10 years + $exptime += time(); + } + $encExpiry = $db->timestamp( $exptime ); + } + foreach ( $serverKeys as $tableName => $tableKeys ) { + $rows = array(); + foreach ( $tableKeys as $key ) { + $rows[] = array( + 'keyname' => $key, + 'value' => $db->encodeBlob( $this->serialize( $data[$key] ) ), + 'exptime' => $encExpiry, + ); + } + + try { + $db->replace( + $tableName, + array( 'keyname' ), + $rows, + __METHOD__ + ); + } catch ( DBError $e ) { + $this->handleWriteError( $e, $serverIndex ); + $result = false; + } + + } + + } + + return $result; + } + + + + /** + * @param string $key + * @param mixed $value + * @param int $exptime * @return bool */ public function set( $key, $value, $exptime = 0 ) { @@ -296,7 +383,6 @@ class SqlBagOStuff extends BagOStuff { $encExpiry = $db->timestamp( $exptime ); } - $db->begin( __METHOD__ ); // (bug 24425) use a replace if the db supports it instead of // delete/insert to avoid clashes with conflicting keynames $db->replace( @@ -307,7 +393,6 @@ class SqlBagOStuff extends BagOStuff { 'value' => $db->encodeBlob( $this->serialize( $value ) ), 'exptime' => $encExpiry ), __METHOD__ ); - $db->commit( __METHOD__ ); } catch ( DBError $e ) { $this->handleWriteError( $e, $serverIndex ); return false; @@ -317,10 +402,10 @@ class SqlBagOStuff extends BagOStuff { } /** - * @param $casToken mixed - * @param $key string - * @param $value mixed - * @param $exptime int + * @param mixed $casToken + * @param string $key + * @param mixed $value + * @param int $exptime * @return bool */ public function cas( $casToken, $key, $value, $exptime = 0 ) { @@ -341,7 +426,6 @@ class SqlBagOStuff extends BagOStuff { } $encExpiry = $db->timestamp( $exptime ); } - $db->begin( __METHOD__ ); // (bug 24425) use a replace if the db supports it instead of // delete/insert to avoid clashes with conflicting keynames $db->update( @@ -357,7 +441,6 @@ class SqlBagOStuff extends BagOStuff { ), __METHOD__ ); - $db->commit( __METHOD__ ); } catch ( DBQueryError $e ) { $this->handleWriteError( $e, $serverIndex ); @@ -368,20 +451,18 @@ class SqlBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $time int + * @param string $key + * @param int $time * @return bool */ public function delete( $key, $time = 0 ) { list( $serverIndex, $tableName ) = $this->getTableByKey( $key ); try { $db = $this->getDB( $serverIndex ); - $db->begin( __METHOD__ ); $db->delete( $tableName, array( 'keyname' => $key ), __METHOD__ ); - $db->commit( __METHOD__ ); } catch ( DBError $e ) { $this->handleWriteError( $e, $serverIndex ); return false; @@ -391,8 +472,8 @@ class SqlBagOStuff extends BagOStuff { } /** - * @param $key string - * @param $step int + * @param string $key + * @param int $step * @return int|null */ public function incr( $key, $step = 1 ) { @@ -400,7 +481,6 @@ class SqlBagOStuff extends BagOStuff { try { $db = $this->getDB( $serverIndex ); $step = intval( $step ); - $db->begin( __METHOD__ ); $row = $db->selectRow( $tableName, array( 'value', 'exptime' ), @@ -409,14 +489,12 @@ class SqlBagOStuff extends BagOStuff { array( 'FOR UPDATE' ) ); if ( $row === false ) { // Missing - $db->commit( __METHOD__ ); return null; } $db->delete( $tableName, array( 'keyname' => $key ), __METHOD__ ); if ( $this->isExpired( $db, $row->exptime ) ) { // Expired, do not reinsert - $db->commit( __METHOD__ ); return null; } @@ -434,7 +512,6 @@ class SqlBagOStuff extends BagOStuff { // Race condition. See bug 28611 $newValue = null; } - $db->commit( __METHOD__ ); } catch ( DBError $e ) { $this->handleWriteError( $e, $serverIndex ); return null; @@ -444,7 +521,8 @@ class SqlBagOStuff extends BagOStuff { } /** - * @param $exptime string + * @param DatabaseBase $db + * @param string $exptime * @return bool */ protected function isExpired( $db, $exptime ) { @@ -452,6 +530,7 @@ class SqlBagOStuff extends BagOStuff { } /** + * @param DatabaseBase $db * @return string */ protected function getMaxDateTime( $db ) { @@ -485,8 +564,8 @@ class SqlBagOStuff extends BagOStuff { /** * Delete objects from the database which expire before a certain date. - * @param $timestamp string - * @param $progressCallback bool|callback + * @param string $timestamp + * @param bool|callable $progressCallback * @return bool */ public function deleteObjectsExpiringBefore( $timestamp, $progressCallback = false ) { @@ -509,7 +588,7 @@ class SqlBagOStuff extends BagOStuff { $conds, __METHOD__, array( 'LIMIT' => 100, 'ORDER BY' => 'exptime' ) ); - if ( !$rows->numRows() ) { + if ( $rows === false || !$rows->numRows() ) { break; } $keys = array(); @@ -524,7 +603,6 @@ class SqlBagOStuff extends BagOStuff { $maxExpTime = $row->exptime; } - $db->begin( __METHOD__ ); $db->delete( $this->getTableNameByShard( $i ), array( @@ -533,7 +611,6 @@ class SqlBagOStuff extends BagOStuff { 'keyname' => $keys ), __METHOD__ ); - $db->commit( __METHOD__ ); if ( $progressCallback ) { if ( intval( $totalSeconds ) === 0 ) { @@ -566,9 +643,7 @@ class SqlBagOStuff extends BagOStuff { try { $db = $this->getDB( $serverIndex ); for ( $i = 0; $i < $this->shards; $i++ ) { - $db->begin( __METHOD__ ); $db->delete( $this->getTableNameByShard( $i ), '*', __METHOD__ ); - $db->commit( __METHOD__ ); } } catch ( DBError $e ) { $this->handleWriteError( $e, $serverIndex ); @@ -583,7 +658,7 @@ class SqlBagOStuff extends BagOStuff { * On typical message and page data, this can provide a 3X decrease * in storage requirements. * - * @param $data mixed + * @param mixed $data * @return string */ protected function serialize( &$data ) { @@ -598,7 +673,7 @@ class SqlBagOStuff extends BagOStuff { /** * Unserialize and, if necessary, decompress an object. - * @param $serial string + * @param string $serial * @return mixed */ protected function unserialize( $serial ) { @@ -619,6 +694,9 @@ class SqlBagOStuff extends BagOStuff { /** * Handle a DBError which occurred during a read operation. + * + * @param DBError $exception + * @param int $serverIndex */ protected function handleReadError( DBError $exception, $serverIndex ) { if ( $exception instanceof DBConnectionError ) { @@ -626,14 +704,19 @@ class SqlBagOStuff extends BagOStuff { } wfDebugLog( 'SQLBagOStuff', "DBError: {$exception->getMessage()}" ); if ( $exception instanceof DBConnectionError ) { + $this->setLastError( BagOStuff::ERR_UNREACHABLE ); wfDebug( __METHOD__ . ": ignoring connection error\n" ); } else { + $this->setLastError( BagOStuff::ERR_UNEXPECTED ); wfDebug( __METHOD__ . ": ignoring query error\n" ); } } /** * Handle a DBQueryError which occurred during a write operation. + * + * @param DBError $exception + * @param int $serverIndex */ protected function handleWriteError( DBError $exception, $serverIndex ) { if ( $exception instanceof DBConnectionError ) { @@ -642,18 +725,24 @@ class SqlBagOStuff extends BagOStuff { if ( $exception->db && $exception->db->wasReadOnlyError() ) { try { $exception->db->rollback( __METHOD__ ); - } catch ( DBError $e ) {} + } catch ( DBError $e ) { + } } wfDebugLog( 'SQLBagOStuff', "DBError: {$exception->getMessage()}" ); if ( $exception instanceof DBConnectionError ) { + $this->setLastError( BagOStuff::ERR_UNREACHABLE ); wfDebug( __METHOD__ . ": ignoring connection error\n" ); } else { + $this->setLastError( BagOStuff::ERR_UNEXPECTED ); wfDebug( __METHOD__ . ": ignoring query error\n" ); } } /** * Mark a server down due to a DBConnectionError exception + * + * @param DBError $exception + * @param int $serverIndex */ protected function markServerDown( $exception, $serverIndex ) { if ( isset( $this->connFailureTimes[$serverIndex] ) ) { @@ -677,19 +766,15 @@ class SqlBagOStuff extends BagOStuff { public function createTables() { for ( $serverIndex = 0; $serverIndex < $this->numServers; $serverIndex++ ) { $db = $this->getDB( $serverIndex ); - if ( $db->getType() !== 'mysql' - || version_compare( $db->getServerVersion(), '4.1.0', '<' ) ) - { + if ( $db->getType() !== 'mysql' ) { throw new MWException( __METHOD__ . ' is not supported on this DB server' ); } for ( $i = 0; $i < $this->shards; $i++ ) { - $db->begin( __METHOD__ ); $db->query( 'CREATE TABLE ' . $db->tableName( $this->getTableNameByShard( $i ) ) . ' LIKE ' . $db->tableName( 'objectcache' ), __METHOD__ ); - $db->commit( __METHOD__ ); } } } @@ -698,4 +783,5 @@ class SqlBagOStuff extends BagOStuff { /** * Backwards compatibility alias */ -class MediaWikiBagOStuff extends SqlBagOStuff { } +class MediaWikiBagOStuff extends SqlBagOStuff { +} diff --git a/includes/objectcache/WinCacheBagOStuff.php b/includes/objectcache/WinCacheBagOStuff.php index 6d9b47ad..78a512ce 100644 --- a/includes/objectcache/WinCacheBagOStuff.php +++ b/includes/objectcache/WinCacheBagOStuff.php @@ -32,8 +32,8 @@ class WinCacheBagOStuff extends BagOStuff { /** * Get a value from the WinCache object cache * - * @param string $key cache key - * @param $casToken[optional] int: cas token + * @param string $key Cache key + * @param int $casToken [optional] Cas token * @return mixed */ public function get( $key, &$casToken = null ) { @@ -51,9 +51,9 @@ class WinCacheBagOStuff extends BagOStuff { /** * Store a value in the WinCache object cache * - * @param string $key cache key - * @param $value Mixed: object to store - * @param int $expire expiration time + * @param string $key Cache key + * @param mixed $value Value to store + * @param int $expire Expiration time * @return bool */ public function set( $key, $value, $expire = 0 ) { @@ -67,10 +67,10 @@ class WinCacheBagOStuff extends BagOStuff { /** * Store a value in the WinCache object cache, race condition-safe * - * @param int $casToken cas token - * @param string $key cache key - * @param int $value object to store - * @param int $exptime expiration time + * @param int $casToken Cas token + * @param string $key Cache key + * @param int $value Object to store + * @param int $exptime Expiration time * @return bool */ public function cas( $casToken, $key, $value, $exptime = 0 ) { @@ -80,8 +80,8 @@ class WinCacheBagOStuff extends BagOStuff { /** * Remove a value from the WinCache object cache * - * @param string $key cache key - * @param int $time not used in this implementation + * @param string $key Cache key + * @param int $time Not used in this implementation * @return bool */ public function delete( $key, $time = 0 ) { diff --git a/includes/objectcache/XCacheBagOStuff.php b/includes/objectcache/XCacheBagOStuff.php index 0f45db73..8e2a160d 100644 --- a/includes/objectcache/XCacheBagOStuff.php +++ b/includes/objectcache/XCacheBagOStuff.php @@ -31,8 +31,8 @@ class XCacheBagOStuff extends BagOStuff { /** * Get a value from the XCache object cache * - * @param string $key cache key - * @param $casToken mixed: cas token + * @param string $key Cache key + * @param mixed $casToken Cas token * @return mixed */ public function get( $key, &$casToken = null ) { @@ -54,9 +54,9 @@ class XCacheBagOStuff extends BagOStuff { /** * Store a value in the XCache object cache * - * @param string $key cache key - * @param $value Mixed: object to store - * @param int $expire expiration time + * @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 ) { @@ -69,10 +69,10 @@ class XCacheBagOStuff extends BagOStuff { } /** - * @param $casToken mixed - * @param $key string - * @param $value mixed - * @param $exptime int + * @param mixed $casToken + * @param string $key + * @param mixed $value + * @param int $exptime * @return bool */ public function cas( $casToken, $key, $value, $exptime = 0 ) { @@ -83,8 +83,8 @@ class XCacheBagOStuff extends BagOStuff { /** * Remove a value from the XCache object cache * - * @param string $key cache key - * @param int $time not used in this implementation + * @param string $key Cache key + * @param int $time Not used in this implementation * @return bool */ public function delete( $key, $time = 0 ) { @@ -97,13 +97,13 @@ class XCacheBagOStuff extends BagOStuff { * XCache does not seem to support any way of performing CAS - this however will * provide a way to perform CAS-like functionality. * - * @param $key string - * @param $callback closure Callback method to be executed + * @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 + * @return bool Success */ - public function merge( $key, closure $callback, $exptime = 0, $attempts = 10 ) { + public function merge( $key, Closure $callback, $exptime = 0, $attempts = 10 ) { return $this->mergeViaLock( $key, $callback, $exptime, $attempts ); } |