r83208 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r83207‎ | r83208 | r83209 >
Date:06:01, 4 March 2011
Author:tstarling
Status:ok (Comments)
Tags:
Comment:
* Added an Ehcache client.
* Fixed encoding of spaces in memcached keys, in r83140 they would have been encoded as "+".
Modified paths:
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/objectcache/EhcacheBagOStuff.php (added) (history)
  • /trunk/phase3/includes/objectcache/MemcachedPhpBagOStuff.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/objectcache/MemcachedPhpBagOStuff.php
@@ -102,7 +102,7 @@
103103 }
104104
105105 protected function encodeKeyCallback( $m ) {
106 - return urlencode( $m[0] );
 106+ return rawurlencode( $m[0] );
107107 }
108108
109109 /**
Index: trunk/phase3/includes/objectcache/EhcacheBagOStuff.php
@@ -0,0 +1,226 @@
 2+<?php
 3+
 4+class EhcacheBagOStuff extends BagOStuff {
 5+ var $servers, $cacheName, $connectTimeout, $timeout, $curlOptions,
 6+ $requestData, $requestDataPos;
 7+
 8+ var $curls = array();
 9+
 10+ function __construct( $params ) {
 11+ if ( !defined( 'CURLOPT_TIMEOUT_MS' ) ) {
 12+ throw new MWException( __CLASS__.' requires curl version 7.16.2 or later.' );
 13+ }
 14+ if ( !extension_loaded( 'zlib' ) ) {
 15+ throw new MWException( __CLASS__.' requires the zlib extension' );
 16+ }
 17+ if ( !isset( $params['servers'] ) ) {
 18+ throw new MWException( __METHOD__.': servers parameter is required' );
 19+ }
 20+ $this->servers = $params['servers'];
 21+ $this->cacheName = isset( $params['cache'] ) ? $params['cache'] : 'mw';
 22+ $this->connectTimeout = isset( $params['connectTimeout'] )
 23+ ? $params['connectTimeout'] : 1;
 24+ $this->timeout = isset( $params['timeout'] ) ? $params['timeout'] : 1;
 25+ $this->curlOptions = array(
 26+ CURLOPT_CONNECTTIMEOUT_MS => intval( $this->connectTimeout * 1000 ),
 27+ CURLOPT_TIMEOUT_MS => intval( $this->timeout * 1000 ),
 28+ CURLOPT_RETURNTRANSFER => 1,
 29+ CURLOPT_CUSTOMREQUEST => 'GET',
 30+ CURLOPT_POST => 0,
 31+ CURLOPT_POSTFIELDS => '',
 32+ CURLOPT_HTTPHEADER => array(),
 33+ );
 34+ }
 35+
 36+ public function get( $key ) {
 37+ wfProfileIn( __METHOD__ );
 38+ $response = $this->doItemRequest( $key );
 39+ if ( !$response || $response['http_code'] == 404 ) {
 40+ wfProfileOut( __METHOD__ );
 41+ return false;
 42+ }
 43+ if ( $response['http_code'] >= 300 ) {
 44+ wfDebug( __METHOD__.": GET failure, got HTTP {$response['http_code']}\n" );
 45+ wfProfileOut( __METHOD__ );
 46+ return false;
 47+ }
 48+ $body = $response['body'];
 49+ $type = $response['content_type'];
 50+ if ( $type == 'application/vnd.php.serialized+deflate' ) {
 51+ $body = gzinflate( $body );
 52+ if ( !$body ) {
 53+ wfDebug( __METHOD__.": error inflating $key\n" );
 54+ wfProfileOut( __METHOD__ );
 55+ return false;
 56+ }
 57+ $data = unserialize( $body );
 58+ } elseif ( $type == 'application/vnd.php.serialized' ) {
 59+ $data = unserialize( $body );
 60+ } else {
 61+ wfDebug( __METHOD__.": unknown content type \"$type\"\n" );
 62+ wfProfileOut( __METHOD__ );
 63+ return false;
 64+ }
 65+
 66+ wfProfileOut( __METHOD__ );
 67+ return $data;
 68+ }
 69+
 70+ public function set( $key, $value, $expiry = 0 ) {
 71+ wfProfileIn( __METHOD__ );
 72+ $expiry = $this->convertExpiry( $expiry );
 73+ $ttl = $expiry ? $expiry - time() : 2147483647;
 74+ $blob = serialize( $value );
 75+ if ( strlen( $blob ) > 100 ) {
 76+ $blob = gzdeflate( $blob );
 77+ $contentType = 'application/vnd.php.serialized+deflate';
 78+ } else {
 79+ $contentType = 'application/vnd.php.serialized';
 80+ }
 81+
 82+ $code = $this->attemptPut( $key, $blob, $contentType, $ttl );
 83+
 84+ if ( $code == 404 ) {
 85+ // Maybe the cache does not exist yet, let's try creating it
 86+ if ( !$this->createCache( $key ) ) {
 87+ wfDebug( __METHOD__.": cache creation failed\n" );
 88+ wfProfileOut( __METHOD__ );
 89+ return false;
 90+ }
 91+ $code = $this->attemptPut( $key, $blob, $contentType, $ttl );
 92+ }
 93+
 94+ $result = false;
 95+ if ( !$code ) {
 96+ wfDebug( __METHOD__.": PUT failure for key $key\n" );
 97+ } elseif ( $code >= 300 ) {
 98+ wfDebug( __METHOD__.": PUT failure for key $key: HTTP $code\n" );
 99+ } else {
 100+ $result = true;
 101+ }
 102+
 103+ wfProfileOut( __METHOD__ );
 104+ return $result;
 105+ }
 106+
 107+ public function delete( $key, $time = 0 ) {
 108+ wfProfileIn( __METHOD__ );
 109+ $response = $this->doItemRequest( $key,
 110+ array( CURLOPT_CUSTOMREQUEST => 'DELETE' ) );
 111+ $code = isset( $response['http_code'] ) ? $response['http_code'] : 0;
 112+ if ( !$response || ( $code != 404 && $code >= 300 ) ) {
 113+ wfDebug( __METHOD__.": DELETE failure for key $key\n" );
 114+ $result = false;
 115+ } else {
 116+ $result = true;
 117+ }
 118+ wfProfileOut( __METHOD__ );
 119+ return $result;
 120+ }
 121+
 122+ protected function getCacheUrl( $key ) {
 123+ if ( count( $this->servers ) == 1 ) {
 124+ $server = reset( $this->servers );
 125+ } else {
 126+ // Use consistent hashing
 127+ $hashes = array();
 128+ foreach ( $this->servers as $server ) {
 129+ $hashes[$server] = md5( $server . '/' . $key );
 130+ }
 131+ asort( $hashes );
 132+ reset( $hashes );
 133+ $server = key( $hashes );
 134+ }
 135+ return "http://$server/ehcache/rest/{$this->cacheName}";
 136+ }
 137+
 138+ /**
 139+ * Get a cURL handle for the given cache URL.
 140+ * We cache the handles to allow keepalive.
 141+ */
 142+ protected function getCurl( $cacheUrl ) {
 143+ if ( !isset( $this->curls[$cacheUrl] ) ) {
 144+ $this->curls[$cacheUrl] = curl_init();
 145+ }
 146+ return $this->curls[$cacheUrl];
 147+ }
 148+
 149+ protected function attemptPut( $key, $data, $type, $ttl ) {
 150+ // In initial benchmarking, it was 30 times faster to use CURLOPT_POST
 151+ // than CURLOPT_UPLOAD with CURLOPT_READFUNCTION. This was because
 152+ // CURLOPT_UPLOAD was pushing the request headers first, then waiting
 153+ // for an ACK packet, then sending the data, whereas CURLOPT_POST just
 154+ // sends the headers and the data in a single send().
 155+ $response = $this->doItemRequest( $key,
 156+ array(
 157+ CURLOPT_POST => 1,
 158+ CURLOPT_CUSTOMREQUEST => 'PUT',
 159+ CURLOPT_POSTFIELDS => $data,
 160+ CURLOPT_HTTPHEADER => array(
 161+ 'Content-Type: ' . $type,
 162+ 'ehcacheTimeToLiveSeconds: ' . $ttl
 163+ )
 164+ )
 165+ );
 166+ if ( !$response ) {
 167+ return 0;
 168+ } else {
 169+ return $response['http_code'];
 170+ }
 171+ }
 172+
 173+ protected function createCache( $key ) {
 174+ wfDebug( __METHOD__.": creating cache for $key\n" );
 175+ $response = $this->doCacheRequest( $key,
 176+ array(
 177+ CURLOPT_POST => 1,
 178+ CURLOPT_CUSTOMREQUEST => 'PUT',
 179+ CURLOPT_POSTFIELDS => '',
 180+ ) );
 181+ if ( !$response ) {
 182+ wfDebug( __CLASS__.": failed to create cache for $key\n" );
 183+ return false;
 184+ }
 185+ if ( $response['http_code'] == 201 /* created */
 186+ || $response['http_code'] == 409 /* already there */ )
 187+ {
 188+ return true;
 189+ } else {
 190+ return false;
 191+ }
 192+ }
 193+
 194+ protected function doCacheRequest( $key, $curlOptions = array() ) {
 195+ $cacheUrl = $this->getCacheUrl( $key );
 196+ $curl = $this->getCurl( $cacheUrl );
 197+ return $this->doRequest( $curl, $cacheUrl, $curlOptions );
 198+ }
 199+
 200+ protected function doItemRequest( $key, $curlOptions = array() ) {
 201+ $cacheUrl = $this->getCacheUrl( $key );
 202+ $curl = $this->getCurl( $cacheUrl );
 203+ $url = $cacheUrl . '/' . rawurlencode( $key );
 204+ return $this->doRequest( $curl, $url, $curlOptions );
 205+ }
 206+
 207+ protected function doRequest( $curl, $url, $curlOptions = array() ) {
 208+ if ( array_diff_key( $curlOptions, $this->curlOptions ) ) {
 209+ var_dump( array_diff_key( $curlOptions, $this->curlOptions ) );
 210+ throw new MWException( __METHOD__.": to prevent options set in one doRequest() " .
 211+ "call from affecting subsequent doRequest() calls, only options listed " .
 212+ "in \$this->curlOptions may be specified in the \$curlOptions parameter." );
 213+ }
 214+ $curlOptions += $this->curlOptions;
 215+ $curlOptions[CURLOPT_URL] = $url;
 216+
 217+ curl_setopt_array( $curl, $curlOptions );
 218+ $result = curl_exec( $curl );
 219+ if ( $result === false ) {
 220+ wfDebug( __CLASS__.": curl error: " . curl_error( $curl ) . "\n" );
 221+ return false;
 222+ }
 223+ $info = curl_getinfo( $curl );
 224+ $info['body'] = $result;
 225+ return $info;
 226+ }
 227+}
Property changes on: trunk/phase3/includes/objectcache/EhcacheBagOStuff.php
___________________________________________________________________
Added: svn:eol-style
1228 + native
Index: trunk/phase3/includes/AutoLoader.php
@@ -505,6 +505,7 @@
506506 'BagOStuff' => 'includes/objectcache/BagOStuff.php',
507507 'DBABagOStuff' => 'includes/objectcache/DBABagOStuff.php',
508508 'eAccelBagOStuff' => 'includes/objectcache/eAccelBagOStuff.php',
 509+ 'EhcacheBagOStuff' => 'includes/objectcache/EhcacheBagOStuff.php',
509510 'EmptyBagOStuff' => 'includes/objectcache/EmptyBagOStuff.php',
510511 'FakeMemCachedClient' => 'includes/objectcache/EmptyBagOStuff.php',
511512 'HashBagOStuff' => 'includes/objectcache/HashBagOStuff.php',

Follow-up revisions

RevisionCommit summaryAuthorDate
r83492Comment the var_dump() and add a comment....platonides00:20, 8 March 2011
r84727Merged in the ObjectCache refactor and the Ehcache client. MFT r83135, r83136...tstarling03:28, 25 March 2011

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r83140* Rewrote ObjectCache.php to conform to the modern coding style, and to be le...tstarling09:37, 3 March 2011

Comments

#Comment by Platonides (talk | contribs)   17:05, 6 March 2011

Links to the server: w:EHcache http://ehcache.org/

Should be added to the installer.

Did you really intend to leave that var_dump() there?

#Comment by Tim Starling (talk | contribs)   00:01, 7 March 2011

Perhaps a more relevant link would be http://ehcache.org/documentation/cache_server.html since this is a client for the Ehcache RESTful web service, not for Ehcache in general.

It can be added to the installer once it has a simplified configuration method, similar to what memcached has, and some more documentation.

The var_dump() should be unreachable, but can be removed if it concerns you. Note that it is essential debugging information if that exception is hit.

Status & tagging log