r35262 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r35261‎ | r35262 | r35263 >
Date:22:14, 23 May 2008
Author:nikerabbit
Status:old
Tags:
Comment:
* New option $wgPerLanguageCaching, for wikies with many translated system messages in MediaWiki namespace.
* Sidebar is now cached for all languages
Modified paths:
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/includes/GlobalFunctions.php (modified) (history)
  • /trunk/phase3/includes/MessageCache.php (modified) (history)
  • /trunk/phase3/includes/Skin.php (modified) (history)
  • /trunk/phase3/languages/Language.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/GlobalFunctions.php
@@ -2336,6 +2336,14 @@
23372337 return $key;
23382338 }
23392339
 2340+function wfMemcKeyLang( $key, $code ) {
 2341+ if ( !is_string($code) ) {
 2342+ return $key;
 2343+ } else {
 2344+ return $key . ';L:' . $code;
 2345+ }
 2346+}
 2347+
23402348 /**
23412349 * Get a cache key for a foreign DB
23422350 */
Index: trunk/phase3/includes/MessageCache.php
@@ -22,8 +22,7 @@
2323 var $mMemcKey, $mKeys, $mParserOptions, $mParser;
2424 var $mExtensionMessages = array();
2525 var $mInitialised = false;
26 - var $mDeferred = true;
27 - var $mAllMessagesLoaded;
 26+ var $mAllMessagesLoaded; // Extension messages
2827
2928 function __construct( &$memCached, $useDB, $expiry, $memcPrefix) {
3029 wfProfileIn( __METHOD__ );
@@ -38,15 +37,14 @@
3938 $this->mInitialised = true;
4039 $this->mParser = null;
4140
42 - # When we first get asked for a message,
43 - # then we'll fill up the cache. If we
44 - # can return a cache hit, this saves
45 - # some extra milliseconds
46 - $this->mDeferred = true;
47 -
4841 wfProfileOut( __METHOD__ );
4942 }
5043
 44+
 45+ /**
 46+ * ParserOptions is lazy initialised.
 47+ * Access should probably be protected.
 48+ */
5149 function getParserOptions() {
5250 if ( !$this->mParserOptions ) {
5351 $this->mParserOptions = new ParserOptions;
@@ -55,22 +53,26 @@
5654 }
5755
5856 /**
59 - * Try to load the cache from a local file
 57+ * Try to load the cache from a local file.
 58+ * Actual format of the file depends on the $wgLocalMessageCacheSerialized
 59+ * setting.
 60+ *
 61+ * @param $hash String: the hash of contents, to check validity.
 62+ * @param $code Mixed: Optional language code, see documenation of load().
 63+ * @return false on failure.
6064 */
61 - function loadFromLocal( $hash ) {
 65+ function loadFromLocal( $hash, $code ) {
6266 global $wgLocalMessageCache, $wgLocalMessageCacheSerialized;
6367
64 - if ( $wgLocalMessageCache === false ) {
65 - return;
66 - }
67 -
6868 $filename = "$wgLocalMessageCache/messages-" . wfWikiID();
 69+ if ( $code ) $filename .= '-' . $code;
6970
 71+ # Check file existence
7072 wfSuppressWarnings();
7173 $file = fopen( $filename, 'r' );
7274 wfRestoreWarnings();
7375 if ( !$file ) {
74 - return;
 76+ return false; // No cache file
7577 }
7678
7779 if ( $wgLocalMessageCacheSerialized ) {
@@ -82,36 +84,36 @@
8385 while ( !feof( $file ) ) {
8486 $serialized .= fread( $file, 100000 );
8587 }
86 - $this->setCache( unserialize( $serialized ) );
 88+ fclose( $file );
 89+ return $this->setCache( unserialize( $serialized ) );
 90+ } else {
 91+ fclose( $file );
 92+ return false; // Wrong hash
8793 }
88 - fclose( $file );
8994 } else {
9095 $localHash=substr(fread($file,40),8);
9196 fclose($file);
9297 if ($hash!=$localHash) {
93 - return;
 98+ return false; // Wrong hash
9499 }
95100
96 - require("$wgLocalMessageCache/messages-" . wfWikiID());
97 - $this->setCache( $this->mCache);
 101+ require( $filename );
 102+ return $this->setCache( $this->mCache );
98103 }
99104 }
100105
101106 /**
102107 * Save the cache to a local file
103108 */
104 - function saveToLocal( $serialized, $hash ) {
105 - global $wgLocalMessageCache, $wgLocalMessageCacheSerialized;
 109+ function saveToLocal( $serialized, $hash, $code ) {
 110+ global $wgLocalMessageCache;
106111
107 - if ( $wgLocalMessageCache === false ) {
108 - return;
109 - }
110 -
111112 $filename = "$wgLocalMessageCache/messages-" . wfWikiID();
112 - $oldUmask = umask( 0 );
113 - wfMkdirParents( $wgLocalMessageCache, 0777 );
114 - umask( $oldUmask );
 113+ if ( $code ) $filename .= '-' . $code;
115114
 115+ # Make sure the directories exist
 116+ $this->createCacheDir( $wgLocalMessageCache );
 117+
116118 $file = fopen( $filename, 'w' );
117119 if ( !$file ) {
118120 wfDebug( "Unable to open local cache file for writing\n" );
@@ -123,34 +125,41 @@
124126 @chmod( $filename, 0666 );
125127 }
126128
127 - function loadFromScript( $hash ) {
128 - wfDeprecated( __METHOD__ );
129 - $this->loadFromLocal( $hash );
130 - }
131 -
132 - function saveToScript($array, $hash) {
 129+ function saveToScript( $array, $hash, $code ) {
133130 global $wgLocalMessageCache;
134 - if ( $wgLocalMessageCache === false ) {
135 - return;
136 - }
137131
138132 $filename = "$wgLocalMessageCache/messages-" . wfWikiID();
139 - $oldUmask = umask( 0 );
140 - wfMkdirParents( $wgLocalMessageCache, 0777 );
141 - umask( $oldUmask );
142 - $file = fopen( $filename.'.tmp', 'w');
 133+ if ( $code ) $filename .= '-' . $code;
 134+ $tempFilename = $filename . '.tmp';
 135+
 136+ # Make sure the directories exist
 137+ $this->createCacheDir( $wgLocalMessageCache );
 138+
 139+ $file = fopen( $tempFilename, 'w');
143140 fwrite($file,"<?php\n//$hash\n\n \$this->mCache = array(");
144141
145142 foreach ($array as $key => $message) {
146 - fwrite($file, "'". $this->escapeForScript($key).
147 - "' => '" . $this->escapeForScript($message).
148 - "',\n");
 143+ $key = $this->escapeForScript($key);
 144+ $messages = $this->escapeForScript($message);
 145+ fwrite($file, "'$key' => '$message',\n");
149146 }
 147+
150148 fwrite($file,");\n?>");
151149 fclose($file);
152 - rename($filename.'.tmp',$filename);
 150+ rename($tempFilename, $filename);
153151 }
154152
 153+ /**
 154+ * Creates directory with correct permissions.
 155+ *
 156+ * @param $path String: path that is created if it does not exist.
 157+ */
 158+ protected function createCacheDir( $path ) {
 159+ $oldUmask = umask( 0 );
 160+ wfMkdirParents( $path, 0777 );
 161+ umask( $oldUmask );
 162+ }
 163+
155164 function escapeForScript($string) {
156165 $string = str_replace( '\\', '\\\\', $string );
157166 $string = str_replace( '\'', '\\\'', $string );
@@ -162,220 +171,320 @@
163172 */
164173 function setCache( $cache ) {
165174 if ( isset( $cache['VERSION'] ) && $cache['VERSION'] == MSG_CACHE_VERSION ) {
166 - $this->mCache = $cache;
 175+ if ( $this->mCache ) {
 176+ $this->mCache = $cache + $this->mCache;
 177+ } else {
 178+ $this->mCache = $cache;
 179+ }
 180+ return true;
167181 } else {
168 - $this->mCache = false;
 182+ return false;
169183 }
170184 }
171185
172186 /**
173 - * Loads messages either from memcached or the database, if not disabled
174 - * On error, quietly switches to a fallback mode
175 - * Returns false for a reportable error, true otherwise
 187+ * Loads messages from caches or from database in this order:
 188+ * (1) local message cache (if $wgLocalMessageCache is enabled)
 189+ * (2) memcached
 190+ * (3) from the database.
 191+ *
 192+ * When succesfully loading from (2) or (3), all higher level caches are
 193+ * updated for the newest version.
 194+ *
 195+ * Nothing is loaded if member variable mDisabled is true, either manually
 196+ * set by calling code or if message loading fails (is this possible?).
 197+ *
 198+ * Returns true if cache is already populated or it was succesfully populated,
 199+ * or false if populating empty cache fails. Also returns true if MessageCache
 200+ * is disabled.
 201+ *
 202+ * @param $code Mixed: if evaluates to false, messages for all languages is
 203+ * loaded and stored in cache as one object. If code is provided
 204+ * and $wgPerLanguageCaching is true, messages are loaded per
 205+ * language and stored individually in caches 1 to 2.
176206 */
177 - function load() {
178 - global $wgLocalMessageCache, $wgLocalMessageCacheSerialized;
 207+ function load( $code = false ) {
 208+ global $wgLocalMessageCache, $wgPerLanguageCaching;
179209
 210+ if ( !$this->mUseCache ) {
 211+ return true;
 212+ }
 213+
 214+ # Keep track of loaded messages. Either array of loaded codes as keys,
 215+ # or just true if all languages are loaded.
 216+ static $loaded = false;
 217+
 218+ if ( !$wgPerLanguageCaching ) {
 219+ // Disable language separation for normal use case
 220+ $code = false;
 221+ } elseif( !is_string( $code ) ) {
 222+ # This isn't really nice, so at least make a note about it
 223+ wfDebug( __METHOD__ . " called without providing a language code\n" );
 224+ }
 225+
 226+ # Don't do double loading...
 227+ if ( $loaded === true || ($code && isset($loaded[$code])) ) return true;
 228+
 229+ # Adjust cache key if we are handling only single language
 230+ $cacheKey = wfMemcKeyLang( $this->mMemcKey, $code );
 231+
 232+ # 8 lines of code just to say (once) that message cache is disabled
180233 if ( $this->mDisable ) {
181234 static $shownDisabled = false;
182235 if ( !$shownDisabled ) {
183 - wfDebug( "MessageCache::load(): disabled\n" );
 236+ wfDebug( __METHOD__ . ": disabled\n" );
184237 $shownDisabled = true;
185238 }
186239 return true;
187240 }
188 - if ( !$this->mUseCache ) {
189 - $this->mDeferred = false;
190 - return true;
191 - }
192241
193 - $fname = 'MessageCache::load';
194 - wfProfileIn( $fname );
195 - $success = true;
 242+ # Loading code starts
 243+ wfProfileIn( __METHOD__ );
 244+ $success = false; # Keep track of success
 245+ $where = array(); # Debug info, delayed to avoid spamming debug log too much
196246
197 - $this->mCache = false;
198 -
199 - # Try local cache
 247+ # (1) local cache
 248+ # Hash of the contents is stored in memcache, to detect if local cache goes
 249+ # out of date (due to update in other thread?)
200250 if ( $wgLocalMessageCache !== false ) {
201 - wfProfileIn( $fname.'-fromlocal' );
202 - $hash = $this->mMemc->get( "{$this->mMemcKey}-hash" );
 251+ wfProfileIn( __METHOD__ . '-fromlocal' );
 252+ $hash = $this->mMemc->get( "$cacheKey-hash" );
203253 if ( $hash ) {
204 - $this->loadFromLocal( $hash );
205 - if ( $this->mCache ) {
206 - wfDebug( "MessageCache::load(): got from local cache\n" );
207 - }
 254+ $success = $this->loadFromLocal( $hash, $code );
 255+ if ( $success ) $where[] = 'got from local cache';
208256 }
209 - wfProfileOut( $fname.'-fromlocal' );
 257+ wfProfileOut( __METHOD__ . '-fromlocal' );
210258 }
211259
212 - # Try memcached
213 - if ( !$this->mCache ) {
214 - wfProfileIn( $fname.'-fromcache' );
215 - $this->setCache( $this->mMemc->get( $this->mMemcKey ) );
216 - if ( $this->mCache ) {
217 - wfDebug( "MessageCache::load(): got from global cache\n" );
218 - # Save to local cache
219 - if ( $wgLocalMessageCache !== false ) {
220 - $serialized = serialize( $this->mCache );
221 - if ( !$hash ) {
222 - $hash = md5( $serialized );
223 - $this->mMemc->set( "{$this->mMemcKey}-hash", $hash, $this->mExpiry );
224 - }
225 - if ($wgLocalMessageCacheSerialized) {
226 - $this->saveToLocal( $serialized,$hash );
227 - } else {
228 - $this->saveToScript( $this->mCache, $hash );
229 - }
230 - }
 260+ # (2) memcache
 261+ # Fails if nothing in cache, or in the wrong version.
 262+ if ( !$success ) {
 263+ wfProfileIn( __METHOD__ . '-fromcache' );
 264+ $cache = $this->mMemc->get( $cacheKey );
 265+ $success = $this->setCache( $cache );
 266+ if ( $success ) {
 267+ $where[] = 'got from global cache';
 268+ $this->saveToCaches( $cache, $cacheKey, false );
231269 }
232 - wfProfileOut( $fname.'-fromcache' );
 270+ wfProfileOut( __METHOD__ . '-fromcache' );
233271 }
234272
235273
236 - # If there's nothing in memcached, load all the messages from the database
237 - if ( !$this->mCache ) {
238 - wfDebug( "MessageCache::load(): cache is empty\n" );
239 - $this->lock();
240 - # Other threads don't need to load the messages if another thread is doing it.
241 - $success = $this->mMemc->add( $this->mMemcKey.'-status', "loading", MSG_LOAD_TIMEOUT );
 274+ # (3)
 275+ # Nothing in caches... so we need create one and store it in caches
 276+ if ( !$success ) {
 277+ $where[] = 'cache is empty';
 278+ $this->lock($cacheKey);
 279+ wfProfileIn( __METHOD__ . '-load' );
 280+ $where[] = 'loading from database';
 281+ # We want to store messages of particular language here, not everything
 282+ # from mCache.
 283+ $cache = $this->loadFromDB( $code );
 284+ $success = $this->setCache( $cache );
 285+ wfProfileOut( __METHOD__ . '-load' );
242286 if ( $success ) {
243 - wfProfileIn( $fname.'-load' );
244 - wfDebug( "MessageCache::load(): loading all messages from DB\n" );
245 - $this->loadFromDB();
246 - wfProfileOut( $fname.'-load' );
247 -
248 - # Save in memcached
249 - # Keep trying if it fails, this is kind of important
250 - wfProfileIn( $fname.'-save' );
251 - for ($i=0; $i<20 &&
252 - !$this->mMemc->set( $this->mMemcKey, $this->mCache, $this->mExpiry );
253 - $i++ ) {
254 - usleep(mt_rand(500000,1500000));
255 - }
256 -
257 - # Save to local cache
258 - if ( $wgLocalMessageCache !== false ) {
259 - $serialized = serialize( $this->mCache );
260 - $hash = md5( $serialized );
261 - $this->mMemc->set( "{$this->mMemcKey}-hash", $hash, $this->mExpiry );
262 - if ($wgLocalMessageCacheSerialized) {
263 - $this->saveToLocal( $serialized,$hash );
264 - } else {
265 - $this->saveToScript( $this->mCache, $hash );
266 - }
267 - }
268 -
269 - wfProfileOut( $fname.'-save' );
270 - if ( $i == 20 ) {
271 - $this->mMemc->set( $this->mMemcKey.'-status', 'error', 60*5 );
272 - wfDebug( "MemCached set error in MessageCache: restart memcached server!\n" );
273 - } else {
274 - $this->mMemc->delete( $this->mMemcKey.'-status' );
275 - }
 287+ $this->saveToCaches( $cache, $cacheKey );
276288 }
277 - $this->unlock();
 289+ $this->unlock($cacheKey);
278290 }
279291
280 - if ( !is_array( $this->mCache ) ) {
281 - wfDebug( "MessageCache::load(): unable to load cache, disabled\n" );
 292+ if ( !$success ) {
 293+ # Bad luck... this should not happen
 294+ $where[] = 'loading FAILED - cache is disabled';
 295+ $info = implode( ', ', $where );
 296+ wfDebug( __METHOD__ . ": Loading $code... $info\n" );
282297 $this->mDisable = true;
283298 $this->mCache = false;
 299+ } else {
 300+ # All good, just record the success
 301+ $info = implode( ', ', $where );
 302+ wfDebug( __METHOD__ . ": Loading $code... $info\n" );
 303+ if ( $code ) {
 304+ $loaded[$code] = true;
 305+ } else {
 306+ $loaded = true;
 307+ }
284308 }
285 - wfProfileOut( $fname );
286 - $this->mDeferred = false;
 309+ wfProfileOut( __METHOD__ );
287310 return $success;
288311 }
289312
290313 /**
291 - * Loads all or main part of cacheable messages from the database
 314+ * Loads cacheable messages from the database. Messages bigger than
 315+ * $wgMaxMsgCacheEntrySize are assigned a special value, and are loaded
 316+ * on-demand from the database later.
 317+ *
 318+ * @param $code Optional language code, see documenation of load().
 319+ * @return Array: Loaded messages for storing in caches.
292320 */
293 - function loadFromDB() {
294 - global $wgMaxMsgCacheEntrySize;
295 -
 321+ function loadFromDB( $code = false ) {
296322 wfProfileIn( __METHOD__ );
 323+ global $wgMaxMsgCacheEntrySize, $wgContLanguageCode;
297324 $dbr = wfGetDB( DB_SLAVE );
298 - $this->mCache = array();
 325+ $cache = array();
299326
 327+ # Common conditions
 328+ $conds = array(
 329+ 'page_is_redirect' => 0,
 330+ 'page_namespace' => NS_MEDIAWIKI,
 331+ );
 332+
 333+ if ( $code ) {
 334+ # Should $code be escaped?
 335+ # Is this fast enough. Should not matter if the filtering is done in the
 336+ # database or in code.
 337+ if ( $code !== $wgContLanguageCode ) {
 338+ # Messages for particular language
 339+ $conds[] = "page_title like '%%/$code'";
 340+ } else {
 341+ # Effectively disallows use of '/' character in NS_MEDIAWIKI for uses
 342+ # other than language code.
 343+ $conds[] = "page_title not like '%%/%%'";
 344+ }
 345+ }
 346+
 347+ # Conditions to fetch oversized pages to ignore them
 348+ $bigConds = $conds;
 349+ $bigConds[] = 'page_len > ' . intval( $wgMaxMsgCacheEntrySize );
 350+
300351 # Load titles for all oversized pages in the MediaWiki namespace
301 - $res = $dbr->select( 'page', 'page_title',
302 - array(
303 - 'page_len > ' . intval( $wgMaxMsgCacheEntrySize ),
304 - 'page_is_redirect' => 0,
305 - 'page_namespace' => NS_MEDIAWIKI,
306 - ),
307 - __METHOD__ );
 352+ $res = $dbr->select( 'page', 'page_title', $bigConds, __METHOD__ );
308353 while ( $row = $dbr->fetchObject( $res ) ) {
309 - $this->mCache[$row->page_title] = '!TOO BIG';
 354+ $cache[$row->page_title] = '!TOO BIG';
310355 }
311356 $dbr->freeResult( $res );
312357
313 - # Load text for the remaining pages
 358+ # Conditions to load the remaining pages with their contents
 359+ $smallConds = $conds;
 360+ $smallConds[] = 'page_latest=rev_id';
 361+ $smallConds[] = 'rev_text_id=old_id';
 362+ $smallConds[] = 'page_len <= ' . intval( $wgMaxMsgCacheEntrySize );
 363+
314364 $res = $dbr->select( array( 'page', 'revision', 'text' ),
315365 array( 'page_title', 'old_text', 'old_flags' ),
316 - array(
317 - 'page_is_redirect' => 0,
318 - 'page_namespace' => NS_MEDIAWIKI,
319 - 'page_latest=rev_id',
320 - 'rev_text_id=old_id',
321 - 'page_len <= ' . intval( $wgMaxMsgCacheEntrySize ) ),
322 - __METHOD__ );
 366+ $smallConds, __METHOD__ );
323367
324368 for ( $row = $dbr->fetchObject( $res ); $row; $row = $dbr->fetchObject( $res ) ) {
325 - $this->mCache[$row->page_title] = ' ' . Revision::getRevisionText( $row );
 369+ $cache[$row->page_title] = ' ' . Revision::getRevisionText( $row );
326370 }
327 - $this->mCache['VERSION'] = MSG_CACHE_VERSION;
328371 $dbr->freeResult( $res );
 372+
 373+ $cache['VERSION'] = MSG_CACHE_VERSION;
329374 wfProfileOut( __METHOD__ );
 375+ return $cache;
330376 }
331377
332 - function replace( $title, $text ) {
333 - global $wgLocalMessageCache, $wgLocalMessageCacheSerialized, $parserMemc;
334 - global $wgMaxMsgCacheEntrySize;
 378+ /**
 379+ * Updates cache as necessary when message page is changed
 380+ *
 381+ * @param $title String: name of the page changed.
 382+ * @param $text Mixed: new contents of the page.
 383+ */
 384+ public function replace( $title, $text ) {
 385+ global $wgMaxMsgCacheEntrySize, $wgPerLanguageCaching;
335386
 387+ list( $message, $code ) = $this->figureMessage( $title );
 388+
 389+ # TODO: Should we define the sidebar key name somewhere?
 390+ $sidebarKey = wfMemcKeyLang( 'sidebar' , $code );
 391+
 392+ # Load only messages for affected language, if possible
 393+ if ( !$wgPerLanguageCaching ) $code = false;
 394+ $cacheKey = wfMemcKeyLang( $this->mMemcKey, $code );
 395+
336396 wfProfileIn( __METHOD__ );
337 - $this->lock();
338 - $this->load();
339 - if ( is_array( $this->mCache ) ) {
 397+ $this->lock($cacheKey);
 398+
 399+ $cache = $this->mMemc->get( $cacheKey );
 400+ if ( is_array( $cache ) ) {
340401 if ( $text === false ) {
341402 # Article was deleted
342403 unset( $this->mCache[$title] );
343404 $this->mMemc->delete( "$this->mMemcKey:{$title}" );
344405 } elseif ( strlen( $text ) > $wgMaxMsgCacheEntrySize ) {
345 - $this->mCache[$title] = '!TOO BIG';
 406+ $cache[$title] = $this->mCache[$title] = '!TOO BIG';
346407 $this->mMemc->set( "$this->mMemcKey:{$title}", ' '.$text, $this->mExpiry );
347408 } else {
348 - $this->mCache[$title] = ' ' . $text;
 409+ $cache[$title] = $this->mCache[$title] = ' ' . $text;
349410 $this->mMemc->delete( "$this->mMemcKey:{$title}" );
350411 }
351 - $this->mMemc->set( $this->mMemcKey, $this->mCache, $this->mExpiry );
352412
353 - # Save to local cache
354 - if ( $wgLocalMessageCache !== false ) {
355 - $serialized = serialize( $this->mCache );
356 - $hash = md5( $serialized );
357 - $this->mMemc->set( "{$this->mMemcKey}-hash", $hash, $this->mExpiry );
358 - if ($wgLocalMessageCacheSerialized) {
359 - $this->saveToLocal( $serialized,$hash );
360 - } else {
361 - $this->saveToScript( $this->mCache, $hash );
362 - }
 413+ # Update per-request cache
 414+ $success = $this->setCache( $cache, $cacheKey );
 415+ if ( $success ) {
 416+ # And then all caches
 417+ $this->saveToCaches( $cache, $cacheKey );
363418 }
364419 }
365 - $this->unlock();
366 - $parserMemc->delete(wfMemcKey('sidebar'));
 420+ $this->unlock($cacheKey);
 421+
 422+ // Also delete cached sidebar... just in case it is affected
 423+ global $parserMemc;
 424+ $parserMemc->delete( $sidebarKey );
367425 wfProfileOut( __METHOD__ );
368426 }
369427
370428 /**
 429+ * Shortcut to update caches.
 430+ *
 431+ * @param $cache Array: cached messages with a version.
 432+ * @param $cacheKey String: Identifier for the cache.
 433+ * @param $memc Bool: Wether to update or not memcache.
 434+ * @return False on somekind of error.
 435+ */
 436+ protected function saveToCaches( $cache, $cacheKey, $memc = true ) {
 437+ wfProfileIn( __METHOD__ );
 438+ global $wgLocalMessageCache, $wgLocalMessageCacheSerialized;
 439+
 440+ $success = $this->mMemc->add( $cacheKey.'-status', "loading", MSG_LOAD_TIMEOUT );
 441+ if ( !$success ) return true; # Other process should be updating them now
 442+
 443+ $i = 0;
 444+ if ( $memc ) {
 445+ # Save in memcached
 446+ # Keep trying if it fails, this is kind of important
 447+
 448+ for ($i=0; $i<20 &&
 449+ !$this->mMemc->set( $cacheKey, $cache, $this->mExpiry );
 450+ $i++ ) {
 451+ usleep(mt_rand(500000,1500000));
 452+ }
 453+ }
 454+
 455+ # Save to local cache
 456+ if ( $wgLocalMessageCache !== false ) {
 457+ $serialized = serialize( $cache );
 458+ $hash = md5( $serialized );
 459+ $this->mMemc->set( "$cacheKey-hash", $hash, $this->mExpiry );
 460+ if ($wgLocalMessageCacheSerialized) {
 461+ $this->saveToLocal( $serialized, $hash, $code );
 462+ } else {
 463+ $this->saveToScript( $cache, $hash, $code );
 464+ }
 465+ }
 466+
 467+ if ( $i == 20 ) {
 468+ $this->mMemc->set( $cacheKey.'-status', 'error', 60*5 );
 469+ wfDebug( "MemCached set error in MessageCache: restart memcached server!\n" );
 470+ $success = false;
 471+ } else {
 472+ $this->mMemc->delete( $cacheKey.'-status' );
 473+ $success = true;
 474+ }
 475+ wfProfileOut( __METHOD__ );
 476+ return $success;
 477+ }
 478+
 479+ /**
371480 * Returns success
372481 * Represents a write lock on the messages key
373482 */
374 - function lock() {
 483+ function lock($key) {
375484 if ( !$this->mUseCache ) {
376485 return true;
377486 }
378487
379 - $lockKey = $this->mMemcKey . 'lock';
 488+ $lockKey = $key . 'lock';
380489 for ($i=0; $i < MSG_WAIT_TIMEOUT && !$this->mMemc->add( $lockKey, 1, MSG_LOCK_TIMEOUT ); $i++ ) {
381490 sleep(1);
382491 }
@@ -383,12 +492,12 @@
384493 return $i >= MSG_WAIT_TIMEOUT;
385494 }
386495
387 - function unlock() {
 496+ function unlock($key) {
388497 if ( !$this->mUseCache ) {
389498 return;
390499 }
391500
392 - $lockKey = $this->mMemcKey . 'lock';
 501+ $lockKey = $key . 'lock';
393502 $this->mMemc->delete( $lockKey );
394503 }
395504
@@ -438,10 +547,6 @@
439548 if( !$this->mInitialised ) {
440549 return '&lt;' . htmlspecialchars($key) . '&gt;';
441550 }
442 - # If cache initialization was deferred, start it now.
443 - if( $this->mDeferred && !$this->mDisable && $useDB ) {
444 - $this->load();
445 - }
446551
447552 $message = false;
448553
@@ -455,10 +560,11 @@
456561 if(!$isFullKey && ($langcode != $wgContLanguageCode) ) {
457562 $title .= '/' . $langcode;
458563 }
459 - $message = $this->getMsgFromNamespace( $title );
 564+ $message = $this->getMsgFromNamespace( $title, $langcode );
460565 }
 566+
461567 # Try the extension array
462 - if( $message === false && isset( $this->mExtensionMessages[$langcode][$lckey] ) ) {
 568+ if ( $message === false && isset( $this->mExtensionMessages[$langcode][$lckey] ) ) {
463569 $message = $this->mExtensionMessages[$langcode][$lckey];
464570 }
465571 if ( $message === false && isset( $this->mExtensionMessages['en'][$lckey] ) ) {
@@ -466,11 +572,8 @@
467573 }
468574
469575 # Try the array in the language object
470 - if( $message === false ) {
471 - #wfDebug( "Trying language object for message $key\n" );
472 - wfSuppressWarnings();
 576+ if ( $message === false ) {
473577 $message = $lang->getMessage( $lckey );
474 - wfRestoreWarnings();
475578 if ( is_null( $message ) ) {
476579 $message = false;
477580 }
@@ -498,7 +601,7 @@
499602 if( ($message === false || $message === '-' ) &&
500603 !$this->mDisable && $useDB &&
501604 !$isFullKey && ($langcode != $wgContLanguageCode) ) {
502 - $message = $this->getMsgFromNamespace( $wgContLang->ucfirst( $lckey ) );
 605+ $message = $this->getMsgFromNamespace( $wgContLang->ucfirst( $lckey ), $wgContLanguageCode );
503606 }
504607
505608 # Final fallback
@@ -512,19 +615,26 @@
513616 * Get a message from the MediaWiki namespace, with caching. The key must
514617 * first be converted to two-part lang/msg form if necessary.
515618 *
516 - * @param string $title Message cache key with initial uppercase letter
 619+ * @param $title String: Message cache key with initial uppercase letter.
 620+ * @param $code String: code denoting the language to try.
517621 */
518 - function getMsgFromNamespace( $title ) {
 622+ function getMsgFromNamespace( $title, $code ) {
519623 $message = false;
520624 $type = false;
521625
522 - # Try the cache
523 - if( $this->mUseCache && isset( $this->mCache[$title] ) ) {
524 - $entry = $this->mCache[$title];
525 - $type = substr( $entry, 0, 1 );
526 - if ( $type == ' ' ) {
527 - return substr( $entry, 1 );
 626+ if ( $this->mUseCache ) {
 627+ if ( !isset($this->mCache[$title]) ) {
 628+ # Delay loading as far as possible, and only load messages for requested
 629+ # language.
 630+ $this->load( $code );
528631 }
 632+ if (isset( $this->mCache[$title] ) ) {
 633+ $entry = $this->mCache[$title];
 634+ $type = substr( $entry, 0, 1 );
 635+ if ( $type == ' ' ) {
 636+ return substr( $entry, 1 );
 637+ }
 638+ }
529639 }
530640
531641 # Call message hooks, in case they are defined
@@ -547,6 +657,7 @@
548658 $type = substr( $entry, 0, 1 );
549659
550660 if ( $type == ' ' ) {
 661+ # Ok!
551662 $message = substr( $entry, 1 );
552663 $this->mCache[$title] = $entry;
553664 return $message;
@@ -574,7 +685,6 @@
575686 $this->mMemc->set( $memcKey, '!NONEXISTENT', $this->mExpiry );
576687 $this->mCache[$title] = false;
577688 }
578 -
579689 return $message;
580690 }
581691
@@ -598,7 +708,7 @@
599709
600710 function disable() { $this->mDisable = true; }
601711 function enable() { $this->mDisable = false; }
602 -
 712+
603713 /** @deprecated */
604714 function disableTransform(){
605715 wfDeprecated( __METHOD__ );
@@ -761,4 +871,15 @@
762872 $this->addMessages( $mergedMessages, $langcode );
763873 }
764874
 875+ public function figureMessage( $key ) {
 876+ global $wgContLanguageCode;
 877+ $pieces = explode('/', $key, 2);
 878+
 879+ $key = $pieces[0];
 880+
 881+ # Language the user is translating to
 882+ $langCode = isset($pieces[1]) ? $pieces[1] : $wgContLanguageCode;
 883+ return array( $key, $langCode );
 884+ }
 885+
765886 }
Index: trunk/phase3/includes/Skin.php
@@ -1650,24 +1650,18 @@
16511651 * Build an array that represents the sidebar(s), the navigation bar among them
16521652 *
16531653 * @return array
1654 - * @private
16551654 */
16561655 function buildSidebar() {
16571656 global $parserMemc, $wgEnableSidebarCache, $wgSidebarCacheExpiry;
1658 - global $wgLang, $wgContLang;
 1657+ global $wgLang;
 1658+ wfProfileIn( __METHOD__ );
16591659
1660 - $fname = 'SkinTemplate::buildSidebar';
 1660+ $key = wfMemcKeyLang( wfMemcKey( 'sidebar' ), $wgLang->getCode() );
16611661
1662 - wfProfileIn( $fname );
1663 -
1664 - $key = wfMemcKey( 'sidebar' );
1665 - $cacheSidebar = $wgEnableSidebarCache &&
1666 - ($wgLang->getCode() == $wgContLang->getCode());
1667 -
1668 - if ($cacheSidebar) {
 1662+ if ( $wgEnableSidebarCache ) {
16691663 $cachedsidebar = $parserMemc->get( $key );
1670 - if ($cachedsidebar!="") {
1671 - wfProfileOut($fname);
 1664+ if ( $cachedsidebar ) {
 1665+ wfProfileOut( __METHOD__ );
16721666 return $cachedsidebar;
16731667 }
16741668 }
@@ -1683,7 +1677,7 @@
16841678 $heading = $line;
16851679 } else {
16861680 if (strpos($line, '|') !== false) { // sanity check
1687 - $line = explode( '|' , trim($line, '* '), 2 );
 1681+ $line = array_map('trim', explode( '|' , trim($line, '* '), 2 ) );
16881682 $link = wfMsgForContent( $line[0] );
16891683 if ($link == '-')
16901684 continue;
@@ -1713,9 +1707,9 @@
17141708 } else { continue; }
17151709 }
17161710 }
1717 - if ($cacheSidebar)
1718 - $parserMemc->set( $key, $bar, $wgSidebarCacheExpiry );
1719 - wfProfileOut( $fname );
 1711+ if ( $wgEnableSidebarCache ) $parserMemc->set( $key, $bar, $wgSidebarCacheExpiry );
 1712+ wfProfileOut( __METHOD__ );
17201713 return $bar;
17211714 }
17221715 }
 1716+
Index: trunk/phase3/languages/Language.php
@@ -2093,8 +2093,9 @@
20942094 }
20952095
20962096 # Try the global cache
2097 - $memcKey = wfMemcKey('localisation', $code );
2098 - $fbMemcKey = wfMemcKey('fallback', $cache['fallback'] );
 2097+
 2098+ $memcKey = wfMemcKeyLang( wfMemcKey('localisation'), $code );
 2099+ $fbMemcKey = wfMemcKeyLang( wfMemcKey('fallback'), $cache['fallback'] );
20992100 $cache = $wgMemc->get( $memcKey );
21002101 if ( $cache ) {
21012102 if ( self::isLocalisationOutOfDate( $cache ) ) {
@@ -2232,7 +2233,7 @@
22332234
22342235 // Try memcache
22352236 global $wgMemc;
2236 - $memcKey = wfMemcKey( 'fallback', $code );
 2237+ $memcKey = wfMemcKeyLang( wfMemcKey('fallback'), $code );
22372238 $fbcode = $wgMemc->get( $memcKey );
22382239
22392240 if ( is_string($fbcode) ) {
Index: trunk/phase3/RELEASE-NOTES
@@ -47,6 +47,8 @@
4848 image page already exists.
4949 * $wgMaximumMovedPages restricts the number of pages that can be moved at once
5050 (default 100) with the new subpage-move functionality of Special:Movepage.
 51+* New option $wgPerLanguageCaching, for wikies with many translated system
 52+ messages in MediaWiki namespace.
5153
5254 === New features in 1.13 ===
5355
@@ -124,6 +126,7 @@
125127 * (bug 13232) importScript(), importStylesheet() funcs available to custom JS
126128 * (bug 13095) Search by first letters or digits in [[Special:Categories]]
127129 * Users moving a page can now move all subpages automatically as well
 130+* Sidebar is now cached for all languages
128131
129132 === Bug fixes in 1.13 ===
130133

Follow-up revisions

RevisionCommit summaryAuthorDate
r35667Revert r35478, r35264, r35262: $wgPerLanguageCaching feature. Bug found with ...tstarling03:27, 1 June 2008

Status & tagging log