MediaWiki  master
MessageBlobStore.php
Go to the documentation of this file.
1 <?php
24 use Psr\Log\LoggerAwareInterface;
25 use Psr\Log\LoggerInterface;
26 use Psr\Log\NullLogger;
28 
38 class MessageBlobStore implements LoggerAwareInterface {
40  private $resourceloader;
41 
43  protected $logger;
44 
46  protected $wanCache;
47 
53  public function __construct(
54  ResourceLoader $rl,
55  ?LoggerInterface $logger,
56  ?WANObjectCache $wanObjectCache
57  ) {
58  $this->resourceloader = $rl;
59  $this->logger = $logger ?: new NullLogger();
60 
61  // NOTE: when changing this assignment, make sure the code in the instantiator for
62  // LocalisationCache which calls MessageBlobStore::clearGlobalCacheEntry() uses the
63  // same cache object.
64  $this->wanCache = $wanObjectCache ?: MediaWikiServices::getInstance()
65  ->getMainWANObjectCache();
66  }
67 
72  public function setLogger( LoggerInterface $logger ) {
73  $this->logger = $logger;
74  }
75 
84  public function getBlob( ResourceLoaderModule $module, $lang ) {
85  $blobs = $this->getBlobs( [ $module->getName() => $module ], $lang );
86  return $blobs[$module->getName()];
87  }
88 
97  public function getBlobs( array $modules, $lang ) {
98  // Each cache key for a message blob by module name and language code also has a generic
99  // check key without language code. This is used to invalidate any and all language subkeys
100  // that exist for a module from the updateMessage() method.
102  $checkKeys = [
103  // Global check key, see clear()
104  $cache->makeGlobalKey( __CLASS__ )
105  ];
106  $cacheKeys = [];
107  foreach ( $modules as $name => $module ) {
108  $cacheKey = $this->makeCacheKey( $module, $lang );
109  $cacheKeys[$name] = $cacheKey;
110  // Per-module check key, see updateMessage()
111  $checkKeys[$cacheKey][] = $cache->makeKey( __CLASS__, $name );
112  }
113  $curTTLs = [];
114  $result = $cache->getMulti( array_values( $cacheKeys ), $curTTLs, $checkKeys );
115 
116  $blobs = [];
117  foreach ( $modules as $name => $module ) {
118  $key = $cacheKeys[$name];
119  if ( !isset( $result[$key] ) || $curTTLs[$key] === null || $curTTLs[$key] < 0 ) {
120  $blobs[$name] = $this->recacheMessageBlob( $key, $module, $lang );
121  } else {
122  // Use unexpired cache
123  $blobs[$name] = $result[$key];
124  }
125  }
126  return $blobs;
127  }
128 
135  private function makeCacheKey( ResourceLoaderModule $module, $lang ) {
136  $messages = array_values( array_unique( $module->getMessages() ) );
137  sort( $messages );
138  return $this->wanCache->makeKey( __CLASS__, $module->getName(), $lang,
139  md5( json_encode( $messages ) )
140  );
141  }
142 
150  protected function recacheMessageBlob( $cacheKey, ResourceLoaderModule $module, $lang ) {
151  $blob = $this->generateMessageBlob( $module, $lang );
153  $cache->set( $cacheKey, $blob,
154  // Add part of a day to TTL to avoid all modules expiring at once
155  $cache::TTL_WEEK + mt_rand( 0, $cache::TTL_DAY ),
156  Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) )
157  );
158  return $blob;
159  }
160 
167  public function updateMessage( $key ) : void {
168  $moduleNames = $this->resourceloader->getModulesByMessage( $key );
169  foreach ( $moduleNames as $moduleName ) {
170  // Uses a holdoff to account for database replica DB lag (for MessageCache)
171  $this->wanCache->touchCheckKey( $this->wanCache->makeKey( __CLASS__, $moduleName ) );
172  }
173  }
174 
178  public function clear() {
179  self::clearGlobalCacheEntry( $this->wanCache );
180  }
181 
189  public static function clearGlobalCacheEntry( WANObjectCache $cache ) {
190  // Disable hold-off because:
191  // - LocalisationCache is populated by messages on-disk and don't have DB lag,
192  // thus there is no need for hold off. We only clear it after new localisation
193  // updates are known to be deployed to all servers.
194  // - This global check key invalidates message blobs for all modules for all wikis
195  // in cache contexts (e.g. languages, skins). Setting a hold-off on this key could
196  // cause a cache stampede since no values would be stored for several seconds.
197  $cache->touchCheckKey( $cache->makeGlobalKey( __CLASS__ ), $cache::HOLDOFF_TTL_NONE );
198  }
199 
206  protected function fetchMessage( $key, $lang ) {
207  $message = wfMessage( $key )->inLanguage( $lang );
208  if ( !$message->exists() ) {
209  $this->logger->warning( 'Failed to find {messageKey} ({lang})', [
210  'messageKey' => $key,
211  'lang' => $lang,
212  ] );
213  $value = null;
214  } else {
215  $value = $message->plain();
216  }
217  return $value;
218  }
219 
227  private function generateMessageBlob( ResourceLoaderModule $module, $lang ) {
228  $messages = [];
229  foreach ( $module->getMessages() as $key ) {
230  $value = $this->fetchMessage( $key, $lang );
231  if ( $value !== null ) {
232  $messages[$key] = $value;
233  }
234  }
235 
236  $json = FormatJson::encode( (object)$messages, false, FormatJson::UTF8_OK );
237  // @codeCoverageIgnoreStart
238  if ( $json === false ) {
239  $this->logger->warning( 'Failed to encode message blob for {module} ({lang})', [
240  'module' => $module->getName(),
241  'lang' => $lang,
242  ] );
243  $json = '{}';
244  }
245  // codeCoverageIgnoreEnd
246  return $json;
247  }
248 }
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:50
MessageBlobStore\clearGlobalCacheEntry
static clearGlobalCacheEntry(WANObjectCache $cache)
Invalidate cache keys for all known modules.
Definition: MessageBlobStore.php:189
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:162
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
MessageBlobStore\fetchMessage
fetchMessage( $key, $lang)
Definition: MessageBlobStore.php:206
MessageBlobStore\clear
clear()
Invalidate cache keys for all known modules.
Definition: MessageBlobStore.php:178
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1220
FormatJson\UTF8_OK
const UTF8_OK
Skip escaping most characters above U+007F for readability and compactness.
Definition: FormatJson.php:34
FormatJson\encode
static encode( $value, $pretty=false, $escaping=0)
Returns the JSON representation of a value.
Definition: FormatJson.php:115
MessageBlobStore\updateMessage
updateMessage( $key)
Invalidate cache keys for modules using this message key.
Definition: MessageBlobStore.php:167
$blob
$blob
Definition: testCompression.php:70
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2475
$modules
$modules
Definition: HTMLFormElement.php:15
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
MessageBlobStore\$logger
LoggerInterface $logger
Definition: MessageBlobStore.php:43
MessageBlobStore\setLogger
setLogger(LoggerInterface $logger)
Definition: MessageBlobStore.php:72
WANObjectCache
Multi-datacenter aware caching interface.
Definition: WANObjectCache.php:125
ResourceLoaderModule\getMessages
getMessages()
Get the messages needed for this module.
Definition: ResourceLoaderModule.php:362
MessageBlobStore\makeCacheKey
makeCacheKey(ResourceLoaderModule $module, $lang)
Definition: MessageBlobStore.php:135
ResourceLoaderModule
Abstraction for ResourceLoader modules, with name registration and maxage functionality.
Definition: ResourceLoaderModule.php:39
ResourceLoader
ResourceLoader is a loading system for JavaScript and CSS resources.
Definition: ResourceLoader.php:56
$cache
$cache
Definition: mcc.php:33
MessageBlobStore\$resourceloader
ResourceLoader $resourceloader
Definition: MessageBlobStore.php:40
MessageBlobStore\generateMessageBlob
generateMessageBlob(ResourceLoaderModule $module, $lang)
Generate the message blob for a given module in a given language.
Definition: MessageBlobStore.php:227
MessageBlobStore\getBlobs
getBlobs(array $modules, $lang)
Get the message blobs for a set of modules.
Definition: MessageBlobStore.php:97
MessageBlobStore
This class generates message blobs for use by ResourceLoader.
Definition: MessageBlobStore.php:38
MessageBlobStore\$wanCache
WANObjectCache $wanCache
Definition: MessageBlobStore.php:46
MessageBlobStore\getBlob
getBlob(ResourceLoaderModule $module, $lang)
Get the message blob for a module.
Definition: MessageBlobStore.php:84
ResourceLoaderModule\getName
getName()
Get this module's name.
Definition: ResourceLoaderModule.php:112
MessageBlobStore\__construct
__construct(ResourceLoader $rl, ?LoggerInterface $logger, ?WANObjectCache $wanObjectCache)
Definition: MessageBlobStore.php:53
MessageBlobStore\recacheMessageBlob
recacheMessageBlob( $cacheKey, ResourceLoaderModule $module, $lang)
Definition: MessageBlobStore.php:150