Skip to content

Commit

Permalink
* Rewrote ObjectCache.php to conform to the modern coding style, and …
Browse files Browse the repository at this point in the history
…to be less convoluted about how CACHE_ANYTHING and CACHE_ACCEL are resolved. Moved most functionality to static members of a new ObjectCache class.

* Moved the global functions to GlobalFunctions.php, where they are now just convenience wrappers. Made them return non-references. Updated callers (none found in extensions). 
* Added an advanced configuration method, $wgObjectCaches, which allows a lot more detail in the object cache configuration than $wgMainCacheType. 
* Made all object cache classes derive from BagOStuff. 
* Split the MWMemcached class into a generic client class and a MediaWiki-specific wrapper class. The wrapper class presents a simple BagOStuff interface to calling code, hiding memcached client internals, and will simplify the task of supporting the PECL extension.
* Added some extra constructor parameters to MWMemcached, configurable via $wgObjectCaches.
* Removed the *_multi() methods from BagOStuff, my grepping indicates that they are not used.
* Rewrote FakeMemCachedClient as a BagOStuff subclass, called EmptyBagOStuff.
* Added an optional "server" parameter to SQLBagOStuff. This allows the server holding the objectcache table to be different from the server holding the core DB.
* Added MultiWriteBagOStuff: a cache class which writes to multiple locations, and reads from them in a defined fallback sequence. This can be used to extend the cache space by adding disk-backed storage to existing in-memory caches.
* Made MWMemcached::get() return false on failure instead of null, to match the BagOStuff documentation and the other BagOStuff subclasses. Anything that was relying on it returning null would have already been broken with SqlBagOStuff.
* Fixed a bug in the memcached client causing keys with spaces or line breaks in them to break the memcached protocol, injecting arbitrary commands or parameters. Since the PECL client apparently also has this flaw, I implemented the fix in the wrapper class.
* Renamed BagOStuff::set_debug() to setDebug(), since we aren't emulating the memcached client anymore
* Fixed spelling error in MWMemcached: persistant -> persistent
  • Loading branch information
Tim Starling committed Mar 3, 2011
1 parent a9bba54 commit be76d86
Show file tree
Hide file tree
Showing 14 changed files with 427 additions and 136 deletions.
6 changes: 5 additions & 1 deletion includes/AutoLoader.php
Expand Up @@ -505,11 +505,15 @@
'BagOStuff' => 'includes/objectcache/BagOStuff.php',
'DBABagOStuff' => 'includes/objectcache/DBABagOStuff.php',
'eAccelBagOStuff' => 'includes/objectcache/eAccelBagOStuff.php',
'FakeMemCachedClient' => 'includes/objectcache/ObjectCache.php',
'EmptyBagOStuff' => 'includes/objectcache/EmptyBagOStuff.php',
'FakeMemCachedClient' => 'includes/objectcache/EmptyBagOStuff.php',
'HashBagOStuff' => 'includes/objectcache/HashBagOStuff.php',
'MWMemcached' => 'includes/objectcache/MemcachedClient.php',
'MediaWikiBagOStuff' => 'includes/objectcache/SqlBagOStuff.php',
'MemCachedClientforWiki' => 'includes/objectcache/MemcachedClient.php',
'MemcachedPhpBagOStuff' => 'includes/objectcache/MemcachedPhpBagOStuff.php',
'MultiWriteBagOStuff' => 'includes/objectcache/MultiWriteBagOStuff.php',
'ObjectCache' => 'includes/objectcache/ObjectCache.php',
'SqlBagOStuff' => 'includes/objectcache/SqlBagOStuff.php',
'WinCacheBagOStuff' => 'includes/objectcache/WinCacheBagOStuff.php',
'XCacheBagOStuff' => 'includes/objectcache/XCacheBagOStuff.php',
Expand Down
32 changes: 32 additions & 0 deletions includes/DefaultSettings.php
Expand Up @@ -1481,6 +1481,8 @@
* - CACHE_DBA: Use PHP's DBA extension to store in a DBM-style
* database. This is slow, and is not recommended for
* anything other than debugging.
* - (other): A string may be used which identifies a cache
* configuration in $wgObjectCaches.
*
* @see $wgMessageCacheType, $wgParserCacheType
*/
Expand All @@ -1502,6 +1504,36 @@
*/
$wgParserCacheType = CACHE_ANYTHING;

/**
* Advanced object cache configuration.
*
* Use this to define the class names and constructor parameters which are used
* for the various cache types. Custom cache types may be defined here and
* referenced from $wgMainCacheType, $wgMessageCacheType or $wgParserCacheType.
*
* The format is an associative array where the key is a cache identifier, and
* the value is an associative array of parameters. The "class" parameter is the
* class name which will be used. Alternatively, a "factory" parameter may be
* given, giving a callable function which will generate a suitable cache object.
*
* The other parameters are dependent on the class used.
*/
$wgObjectCaches = array(
CACHE_NONE => array( 'class' => 'FakeMemCachedClient' ),
CACHE_DB => array( 'class' => 'SqlBagOStuff', 'table' => 'objectcache' ),
CACHE_DBA => array( 'class' => 'DBABagOStuff' ),

CACHE_ANYTHING => array( 'factory' => 'ObjectCache::newAnything' ),
CACHE_ACCEL => array( 'factory' => 'ObjectCache::newAccelerator' ),
CACHE_MEMCACHED => array( 'factory' => 'ObjectCache::newMemcached' ),

'eaccelerator' => array( 'class' => 'eAccelBagOStuff' ),
'apc' => array( 'class' => 'APCBagOStuff' ),
'xcache' => array( 'class' => 'XCacheBagOStuff' ),
'wincache' => array( 'class' => 'WinCacheBagOStuff' ),
'memcached-php' => array( 'class' => 'MemcachedPhpBagOStuff' ),
);

/**
* The expiry time for the parser cache, in seconds. The default is 86.4k
* seconds, otherwise known as a day.
Expand Down
30 changes: 30 additions & 0 deletions includes/GlobalFunctions.php
Expand Up @@ -3357,3 +3357,33 @@ function wfArrayMap( $function, $input ) {
}
return $ret;
}


/**
* Get a cache object.
* @param $inputType Cache type, one the the CACHE_* constants.
*
* @return BagOStuff
*/
function wfGetCache( $inputType ) {
return ObjectCache::getInstance( $inputType );
}

/** Get the main cache object */
function wfGetMainCache() {
global $wgMainCacheType;
return ObjectCache::getInstance( $wgMainCacheType );
}

/** Get the cache object used by the message cache */
function wfGetMessageCacheStorage() {
global $wgMessageCacheType;
return ObjectCache::getInstance( $wgMessageCacheType );
}

/** Get the cache object used by the parser cache */
function wfGetParserCacheStorage() {
global $wgParserCacheType;
return ObjectCache::getInstance( $wgParserCacheType );
}

7 changes: 3 additions & 4 deletions includes/Setup.php
Expand Up @@ -283,7 +283,6 @@
require_once( "$IP/includes/Hooks.php" );
require_once( "$IP/includes/Namespace.php" );
require_once( "$IP/includes/ProxyTools.php" );
require_once( "$IP/includes/objectcache/ObjectCache.php" );
require_once( "$IP/includes/ImageFunctions.php" );
wfProfileOut( $fname . '-includes' );
wfProfileIn( $fname . '-misc1' );
Expand Down Expand Up @@ -323,9 +322,9 @@
wfProfileOut( $fname . '-misc1' );
wfProfileIn( $fname . '-memcached' );

$wgMemc =& wfGetMainCache();
$messageMemc =& wfGetMessageCacheStorage();
$parserMemc =& wfGetParserCacheStorage();
$wgMemc = wfGetMainCache();
$messageMemc = wfGetMessageCacheStorage();
$parserMemc = wfGetParserCacheStorage();

wfDebug( 'CACHES: ' . get_class( $wgMemc ) . '[main] ' .
get_class( $messageMemc ) . '[message] ' .
Expand Down
30 changes: 1 addition & 29 deletions includes/objectcache/BagOStuff.php
Expand Up @@ -43,7 +43,7 @@
abstract class BagOStuff {
var $debugMode = false;

public function set_debug( $bool ) {
public function setDebug( $bool ) {
$this->debugMode = $bool;
}

Expand Down Expand Up @@ -87,22 +87,6 @@ public function keys() {
}

/* *** Emulated functions *** */
/* Better performance can likely be got with custom written versions */
public function get_multi( $keys ) {
$out = array();

foreach ( $keys as $key ) {
$out[$key] = $this->get( $key );
}

return $out;
}

public function set_multi( $hash, $exptime = 0 ) {
foreach ( $hash as $key => $value ) {
$this->set( $key, $value, $exptime );
}
}

public function add( $key, $value, $exptime = 0 ) {
if ( !$this->get( $key ) ) {
Expand All @@ -112,18 +96,6 @@ public function add( $key, $value, $exptime = 0 ) {
}
}

public function add_multi( $hash, $exptime = 0 ) {
foreach ( $hash as $key => $value ) {
$this->add( $key, $value, $exptime );
}
}

public function delete_multi( $keys, $time = 0 ) {
foreach ( $keys as $key ) {
$this->delete( $key, $time );
}
}

public function replace( $key, $value, $exptime = 0 ) {
if ( $this->get( $key ) !== false ) {
$this->set( $key, $value, $exptime );
Expand Down
27 changes: 27 additions & 0 deletions includes/objectcache/EmptyBagOStuff.php
@@ -0,0 +1,27 @@
<?php

/**
* A BagOStuff object with no objects in it. Used to provide a no-op object to calling code.
*
* @ingroup Cache
*/
class EmptyBagOStuff extends BagOStuff {
function get( $key ) {
return false;
}

function set( $key, $value, $exp = 0 ) {
return true;
}

function delete( $key, $time = 0 ) {
return true;
}
}

/**
* Backwards compatibility alias for EmptyBagOStuff
* @deprecated
*/
class FakeMemCachedClient extends EmptyBagOStuff {
}
21 changes: 12 additions & 9 deletions includes/objectcache/MemcachedClient.php
Expand Up @@ -51,7 +51,7 @@
* '127.0.0.1:10020'),
* 'debug' => false,
* 'compress_threshold' => 10240,
* 'persistant' => true));
* 'persistent' => true));
*
* $mc->add('key', array('some', 'array'));
* $mc->replace('key', 'some random string');
Expand Down Expand Up @@ -158,12 +158,12 @@ class MWMemcached {
var $_compress_threshold;

/**
* Are we using persistant links?
* Are we using persistent links?
*
* @var boolean
* @access private
*/
var $_persistant;
var $_persistent;

/**
* If only using one server; contains ip:port to connect to
Expand Down Expand Up @@ -245,22 +245,21 @@ class MWMemcached {
* @return mixed
*/
public function __construct( $args ) {
global $wgMemCachedTimeout;
$this->set_servers( isset( $args['servers'] ) ? $args['servers'] : array() );
$this->_debug = isset( $args['debug'] ) ? $args['debug'] : false;
$this->stats = array();
$this->_compress_threshold = isset( $args['compress_threshold'] ) ? $args['compress_threshold'] : 0;
$this->_persistant = isset( $args['persistant'] ) ? $args['persistant'] : false;
$this->_persistent = isset( $args['persistent'] ) ? $args['persistent'] : false;
$this->_compress_enable = true;
$this->_have_zlib = function_exists( 'gzcompress' );

$this->_cache_sock = array();
$this->_host_dead = array();

$this->_timeout_seconds = 0;
$this->_timeout_microseconds = $wgMemCachedTimeout;
$this->_timeout_microseconds = isset( $args['timeout'] ) ? $args['timeout'] : 100000;

$this->_connect_timeout = 0.01;
$this->_connect_timeout = isset( $args['connect_timeout'] ) ? $args['connect_timeout'] : 0.1;
$this->_connect_attempts = 2;
}

Expand Down Expand Up @@ -433,7 +432,11 @@ public function get( $key ) {
}

wfProfileOut( __METHOD__ );
return @$val[$key];
if ( isset( $val[$key] ) ) {
return $val[$key];
} else {
return false;
}
}

// }}}
Expand Down Expand Up @@ -695,7 +698,7 @@ function _connect_sock( &$sock, $host ) {
$errno = $errstr = null;
for( $i = 0; !$sock && $i < $this->_connect_attempts; $i++ ) {
wfSuppressWarnings();
if ( $this->_persistant == 1 ) {
if ( $this->_persistent == 1 ) {
$sock = pfsockopen( $ip, $port, $errno, $errstr, $timeout );
} else {
$sock = fsockopen( $ip, $port, $errno, $errstr, $timeout );
Expand Down
116 changes: 116 additions & 0 deletions includes/objectcache/MemcachedPhpBagOStuff.php
@@ -0,0 +1,116 @@
<?php

/**
* A wrapper class for the pure-PHP memcached client, exposing a BagOStuff interface.
*/
class MemcachedPhpBagOStuff extends BagOStuff {
/**
* Constructor.
*
* Available parameters are:
* - servers: The list of IP:port combinations holding the memcached servers.
* - debug: Whether to set the debug flag in the underlying client.
* - persistent: Whether to use a persistent connection
* - compress_threshold: The minimum size an object must be before it is compressed
* - timeout: The read timeout in microseconds
* - connect_timeout: The connect timeout in seconds
*/
function __construct( $params ) {
if ( !isset( $params['servers'] ) ) {
$params['servers'] = $GLOBALS['wgMemCachedServers'];
}
if ( !isset( $params['debug'] ) ) {
$params['debug'] = $GLOBALS['wgMemCachedDebug'];
}
if ( !isset( $params['persistent'] ) ) {
$params['persistent'] = $GLOBALS['wgMemCachedPersistent'];
}
if ( !isset( $params['compress_threshold'] ) ) {
$params['compress_threshold'] = 1500;
}
if ( !isset( $params['timeout'] ) ) {
$params['timeout'] = $GLOBALS['wgMemCachedTimeout'];
}
if ( !isset( $params['connect_timeout'] ) ) {
$params['connect_timeout'] = 0.1;
}

$this->client = new MemCachedClientforWiki( $params );
$this->client->set_servers( $params['servers'] );
$this->client->set_debug( $params['debug'] );
}

public function setDebug( $debug ) {
$this->client->set_debug( $debug );
}

public function get( $key ) {
return $this->client->get( $this->encodeKey( $key ) );
}

public function set( $key, $value, $exptime = 0 ) {
return $this->client->set( $this->encodeKey( $key ), $value, $exptime );
}

public function delete( $key, $time = 0 ) {
return $this->client->delete( $this->encodeKey( $key ), $time );
}

public function lock( $key, $timeout = 0 ) {
return $this->client->lock( $this->encodeKey( $key ), $timeout );
}

public function unlock( $key ) {
return $this->client->unlock( $this->encodeKey( $key ) );
}

public function add( $key, $value, $exptime = 0 ) {
return $this->client->add( $this->encodeKey( $key ), $value, $exptime );
}

public function replace( $key, $value, $exptime = 0 ) {
return $this->client->replace( $this->encodeKey( $key ), $value, $exptime );
}

public function incr( $key, $value = 1 ) {
return $this->client->incr( $this->encodeKey( $key ), $value );
}

public function decr( $key, $value = 1 ) {
return $this->client->decr( $this->encodeKey( $key ), $value );
}

/**
* Get the underlying client object. This is provided for debugging
* purposes.
*/
public function getClient() {
return $this->client;
}

/**
* Encode a key for use on the wire inside the memcached protocol.
*
* We encode spaces and line breaks to avoid protocol errors. We encode
* the other control characters for compatibility with libmemcached
* verify_key. We leave other punctuation alone, to maximise backwards
* compatibility.
*/
public function encodeKey( $key ) {
return preg_replace_callback( '/[\x00-\x20\x25\x7f]+/',
array( $this, 'encodeKeyCallback' ), $key );
}

protected function encodeKeyCallback( $m ) {
return urlencode( $m[0] );
}

/**
* Decode a key encoded with encodeKey(). This is provided as a convenience
* function for debugging.
*/
public function decodeKey( $key ) {
return urldecode( $key );
}
}

0 comments on commit be76d86

Please sign in to comment.