MediaWiki  1.32.0
WANObjectCache.php
Go to the documentation of this file.
1 <?php
22 use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface;
23 use Psr\Log\LoggerAwareInterface;
24 use Psr\Log\LoggerInterface;
25 use Psr\Log\NullLogger;
26 
118 class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
120  protected $cache;
122  protected $processCaches = [];
124  protected $purgeChannel;
126  protected $purgeRelayer;
128  protected $mcrouterAware;
130  protected $region;
132  protected $cluster;
134  protected $logger;
136  protected $stats;
138  protected $useInterimHoldOffCaching = true;
140  protected $asyncHandler;
142  protected $epoch;
143 
146 
148  private $callbackDepth = 0;
150  private $warmupCache = [];
152  private $warmupKeyMisses = 0;
153 
156 
158  const MAX_COMMIT_DELAY = 3;
160  const MAX_READ_LAG = 7;
162  const HOLDOFF_TTL = 11; // MAX_COMMIT_DELAY + MAX_READ_LAG + 1
163 
167  const INTERIM_KEY_TTL = 1;
168 
170  const LOCK_TTL = 10;
172  const LOW_TTL = 30;
173 
175  const AGE_NEW = 60;
177  const HOT_TTR = 900;
179  const HIT_RATE_HIGH = 1;
181  const RAMPUP_TTL = 30;
182 
184  const TTL_UNCACHEABLE = -1;
186  const TSE_NONE = -1;
188  const TTL_LAGGED = 30;
190  const HOLDOFF_NONE = 0;
192  const STALE_TTL_NONE = 0;
194  const GRACE_TTL_NONE = 0;
195 
197  const MIN_TIMESTAMP_NONE = 0.0;
198 
200  const TINY_NEGATIVE = -0.000001;
201 
203  const VERSION = 1;
204 
205  const FLD_VERSION = 0; // key to cache version number
206  const FLD_VALUE = 1; // key to the cached value
207  const FLD_TTL = 2; // key to the original TTL
208  const FLD_TIME = 3; // key to the cache time
209  const FLD_FLAGS = 4; // key to the flags bitfield
210  const FLD_HOLDOFF = 5; // key to any hold-off TTL
211 
213  const FLG_STALE = 1;
214 
215  const ERR_NONE = 0; // no error
216  const ERR_NO_RESPONSE = 1; // no response
217  const ERR_UNREACHABLE = 2; // can't connect
218  const ERR_UNEXPECTED = 3; // response gave some error
219  const ERR_RELAY = 4; // relay broadcast failed
220 
221  const VALUE_KEY_PREFIX = 'WANCache:v:';
222  const INTERIM_KEY_PREFIX = 'WANCache:i:';
223  const TIME_KEY_PREFIX = 'WANCache:t:';
224  const MUTEX_KEY_PREFIX = 'WANCache:m:';
225 
226  const PURGE_VAL_PREFIX = 'PURGED:';
227 
228  const VFLD_DATA = 'WOC:d'; // key to the value of versioned data
229  const VFLD_VERSION = 'WOC:v'; // key to the version of the value present
230 
231  const PC_PRIMARY = 'primary:1000'; // process cache name and max key count
232 
233  const DEFAULT_PURGE_CHANNEL = 'wancache-purge';
234 
261  public function __construct( array $params ) {
262  $this->cache = $params['cache'];
263  $this->purgeChannel = $params['channels']['purge'] ?? self::DEFAULT_PURGE_CHANNEL;
264  $this->purgeRelayer = $params['relayers']['purge'] ?? new EventRelayerNull( [] );
265  $this->region = $params['region'] ?? 'main';
266  $this->cluster = $params['cluster'] ?? 'wan-main';
267  $this->mcrouterAware = !empty( $params['mcrouterAware'] );
268  $this->epoch = $params['epoch'] ?? 1.0;
269 
270  $this->setLogger( $params['logger'] ?? new NullLogger() );
271  $this->stats = $params['stats'] ?? new NullStatsdDataFactory();
272  $this->asyncHandler = $params['asyncHandler'] ?? null;
273  }
274 
278  public function setLogger( LoggerInterface $logger ) {
279  $this->logger = $logger;
280  }
281 
287  public static function newEmpty() {
288  return new static( [
289  'cache' => new EmptyBagOStuff()
290  ] );
291  }
292 
334  final public function get( $key, &$curTTL = null, array $checkKeys = [], &$asOf = null ) {
335  $curTTLs = [];
336  $asOfs = [];
337  $values = $this->getMulti( [ $key ], $curTTLs, $checkKeys, $asOfs );
338  $curTTL = $curTTLs[$key] ?? null;
339  $asOf = $asOfs[$key] ?? null;
340 
341  return $values[$key] ?? false;
342  }
343 
356  final public function getMulti(
357  array $keys, &$curTTLs = [], array $checkKeys = [], array &$asOfs = []
358  ) {
359  $result = [];
360  $curTTLs = [];
361  $asOfs = [];
362 
363  $vPrefixLen = strlen( self::VALUE_KEY_PREFIX );
364  $valueKeys = self::prefixCacheKeys( $keys, self::VALUE_KEY_PREFIX );
365 
366  $checkKeysForAll = [];
367  $checkKeysByKey = [];
368  $checkKeysFlat = [];
369  foreach ( $checkKeys as $i => $checkKeyGroup ) {
370  $prefixed = self::prefixCacheKeys( (array)$checkKeyGroup, self::TIME_KEY_PREFIX );
371  $checkKeysFlat = array_merge( $checkKeysFlat, $prefixed );
372  // Is this check keys for a specific cache key, or for all keys being fetched?
373  if ( is_int( $i ) ) {
374  $checkKeysForAll = array_merge( $checkKeysForAll, $prefixed );
375  } else {
376  $checkKeysByKey[$i] = isset( $checkKeysByKey[$i] )
377  ? array_merge( $checkKeysByKey[$i], $prefixed )
378  : $prefixed;
379  }
380  }
381 
382  // Fetch all of the raw values
383  $keysGet = array_merge( $valueKeys, $checkKeysFlat );
384  if ( $this->warmupCache ) {
385  $wrappedValues = array_intersect_key( $this->warmupCache, array_flip( $keysGet ) );
386  $keysGet = array_diff( $keysGet, array_keys( $wrappedValues ) ); // keys left to fetch
387  $this->warmupKeyMisses += count( $keysGet );
388  } else {
389  $wrappedValues = [];
390  }
391  if ( $keysGet ) {
392  $wrappedValues += $this->cache->getMulti( $keysGet );
393  }
394  // Time used to compare/init "check" keys (derived after getMulti() to be pessimistic)
395  $now = $this->getCurrentTime();
396 
397  // Collect timestamps from all "check" keys
398  $purgeValuesForAll = $this->processCheckKeys( $checkKeysForAll, $wrappedValues, $now );
399  $purgeValuesByKey = [];
400  foreach ( $checkKeysByKey as $cacheKey => $checks ) {
401  $purgeValuesByKey[$cacheKey] =
402  $this->processCheckKeys( $checks, $wrappedValues, $now );
403  }
404 
405  // Get the main cache value for each key and validate them
406  foreach ( $valueKeys as $vKey ) {
407  if ( !isset( $wrappedValues[$vKey] ) ) {
408  continue; // not found
409  }
410 
411  $key = substr( $vKey, $vPrefixLen ); // unprefix
412 
413  list( $value, $curTTL ) = $this->unwrap( $wrappedValues[$vKey], $now );
414  if ( $value !== false ) {
415  $result[$key] = $value;
416  // Force dependent keys to be seen as stale for a while after purging
417  // to reduce race conditions involving stale data getting cached
418  $purgeValues = $purgeValuesForAll;
419  if ( isset( $purgeValuesByKey[$key] ) ) {
420  $purgeValues = array_merge( $purgeValues, $purgeValuesByKey[$key] );
421  }
422  foreach ( $purgeValues as $purge ) {
423  $safeTimestamp = $purge[self::FLD_TIME] + $purge[self::FLD_HOLDOFF];
424  if ( $safeTimestamp >= $wrappedValues[$vKey][self::FLD_TIME] ) {
425  // How long ago this value was invalidated by *this* check key
426  $ago = min( $purge[self::FLD_TIME] - $now, self::TINY_NEGATIVE );
427  // How long ago this value was invalidated by *any* known check key
428  $curTTL = min( $curTTL, $ago );
429  }
430  }
431  }
432  $curTTLs[$key] = $curTTL;
433  $asOfs[$key] = ( $value !== false ) ? $wrappedValues[$vKey][self::FLD_TIME] : null;
434  }
435 
436  return $result;
437  }
438 
446  private function processCheckKeys( array $timeKeys, array $wrappedValues, $now ) {
447  $purgeValues = [];
448  foreach ( $timeKeys as $timeKey ) {
449  $purge = isset( $wrappedValues[$timeKey] )
450  ? $this->parsePurgeValue( $wrappedValues[$timeKey] )
451  : false;
452  if ( $purge === false ) {
453  // Key is not set or malformed; regenerate
454  $newVal = $this->makePurgeValue( $now, self::HOLDOFF_TTL );
455  $this->cache->add( $timeKey, $newVal, self::CHECK_KEY_TTL );
456  $purge = $this->parsePurgeValue( $newVal );
457  }
458  $purgeValues[] = $purge;
459  }
460  return $purgeValues;
461  }
462 
528  final public function set( $key, $value, $ttl = 0, array $opts = [] ) {
529  $now = $this->getCurrentTime();
530  $lockTSE = $opts['lockTSE'] ?? self::TSE_NONE;
531  $staleTTL = $opts['staleTTL'] ?? self::STALE_TTL_NONE;
532  $age = isset( $opts['since'] ) ? max( 0, $now - $opts['since'] ) : 0;
533  $lag = $opts['lag'] ?? 0;
534 
535  // Do not cache potentially uncommitted data as it might get rolled back
536  if ( !empty( $opts['pending'] ) ) {
537  $this->logger->info( 'Rejected set() for {cachekey} due to pending writes.',
538  [ 'cachekey' => $key ] );
539 
540  return true; // no-op the write for being unsafe
541  }
542 
543  $wrapExtra = []; // additional wrapped value fields
544  // Check if there's a risk of writing stale data after the purge tombstone expired
545  if ( $lag === false || ( $lag + $age ) > self::MAX_READ_LAG ) {
546  // Case A: read lag with "lockTSE"; save but record value as stale
547  if ( $lockTSE >= 0 ) {
548  $ttl = max( 1, (int)$lockTSE ); // set() expects seconds
549  $wrapExtra[self::FLD_FLAGS] = self::FLG_STALE; // mark as stale
550  // Case B: any long-running transaction; ignore this set()
551  } elseif ( $age > self::MAX_READ_LAG ) {
552  $this->logger->info( 'Rejected set() for {cachekey} due to snapshot lag.',
553  [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] );
554 
555  return true; // no-op the write for being unsafe
556  // Case C: high replication lag; lower TTL instead of ignoring all set()s
557  } elseif ( $lag === false || $lag > self::MAX_READ_LAG ) {
558  $ttl = $ttl ? min( $ttl, self::TTL_LAGGED ) : self::TTL_LAGGED;
559  $this->logger->warning( 'Lowered set() TTL for {cachekey} due to replication lag.',
560  [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] );
561  // Case D: medium length request with medium replication lag; ignore this set()
562  } else {
563  $this->logger->info( 'Rejected set() for {cachekey} due to high read lag.',
564  [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] );
565 
566  return true; // no-op the write for being unsafe
567  }
568  }
569 
570  // Wrap that value with time/TTL/version metadata
571  $wrapped = $this->wrap( $value, $ttl, $now ) + $wrapExtra;
572 
573  $func = function ( $cache, $key, $cWrapped ) use ( $wrapped ) {
574  return ( is_string( $cWrapped ) )
575  ? false // key is tombstoned; do nothing
576  : $wrapped;
577  };
578 
579  return $this->cache->merge( self::VALUE_KEY_PREFIX . $key, $func, $ttl + $staleTTL, 1 );
580  }
581 
643  final public function delete( $key, $ttl = self::HOLDOFF_TTL ) {
644  $key = self::VALUE_KEY_PREFIX . $key;
645 
646  if ( $ttl <= 0 ) {
647  // Publish the purge to all datacenters
648  $ok = $this->relayDelete( $key );
649  } else {
650  // Publish the purge to all datacenters
651  $ok = $this->relayPurge( $key, $ttl, self::HOLDOFF_NONE );
652  }
653 
654  return $ok;
655  }
656 
676  final public function getCheckKeyTime( $key ) {
677  return $this->getMultiCheckKeyTime( [ $key ] )[$key];
678  }
679 
741  final public function getMultiCheckKeyTime( array $keys ) {
742  $rawKeys = [];
743  foreach ( $keys as $key ) {
744  $rawKeys[$key] = self::TIME_KEY_PREFIX . $key;
745  }
746 
747  $rawValues = $this->cache->getMulti( $rawKeys );
748  $rawValues += array_fill_keys( $rawKeys, false );
749 
750  $times = [];
751  foreach ( $rawKeys as $key => $rawKey ) {
752  $purge = $this->parsePurgeValue( $rawValues[$rawKey] );
753  if ( $purge !== false ) {
754  $time = $purge[self::FLD_TIME];
755  } else {
756  // Casting assures identical floats for the next getCheckKeyTime() calls
757  $now = (string)$this->getCurrentTime();
758  $this->cache->add(
759  $rawKey,
760  $this->makePurgeValue( $now, self::HOLDOFF_TTL ),
761  self::CHECK_KEY_TTL
762  );
763  $time = (float)$now;
764  }
765 
766  $times[$key] = $time;
767  }
768 
769  return $times;
770  }
771 
806  final public function touchCheckKey( $key, $holdoff = self::HOLDOFF_TTL ) {
807  // Publish the purge to all datacenters
808  return $this->relayPurge( self::TIME_KEY_PREFIX . $key, self::CHECK_KEY_TTL, $holdoff );
809  }
810 
838  final public function resetCheckKey( $key ) {
839  // Publish the purge to all datacenters
840  return $this->relayDelete( self::TIME_KEY_PREFIX . $key );
841  }
842 
1090  final public function getWithSetCallback( $key, $ttl, $callback, array $opts = [] ) {
1091  $pcTTL = $opts['pcTTL'] ?? self::TTL_UNCACHEABLE;
1092 
1093  // Try the process cache if enabled and the cache callback is not within a cache callback.
1094  // Process cache use in nested callbacks is not lag-safe with regard to HOLDOFF_TTL since
1095  // the in-memory value is further lagged than the shared one since it uses a blind TTL.
1096  if ( $pcTTL >= 0 && $this->callbackDepth == 0 ) {
1097  $group = $opts['pcGroup'] ?? self::PC_PRIMARY;
1098  $procCache = $this->getProcessCache( $group );
1099  $value = $procCache->has( $key, $pcTTL ) ? $procCache->get( $key ) : false;
1100  } else {
1101  $procCache = false;
1102  $value = false;
1103  }
1104 
1105  if ( $value === false ) {
1106  // Fetch the value over the network
1107  if ( isset( $opts['version'] ) ) {
1108  $version = $opts['version'];
1109  $asOf = null;
1110  $cur = $this->doGetWithSetCallback(
1111  $key,
1112  $ttl,
1113  function ( $oldValue, &$ttl, &$setOpts, $oldAsOf )
1114  use ( $callback, $version ) {
1115  if ( is_array( $oldValue )
1116  && array_key_exists( self::VFLD_DATA, $oldValue )
1117  && array_key_exists( self::VFLD_VERSION, $oldValue )
1118  && $oldValue[self::VFLD_VERSION] === $version
1119  ) {
1120  $oldData = $oldValue[self::VFLD_DATA];
1121  } else {
1122  // VFLD_DATA is not set if an old, unversioned, key is present
1123  $oldData = false;
1124  $oldAsOf = null;
1125  }
1126 
1127  return [
1128  self::VFLD_DATA => $callback( $oldData, $ttl, $setOpts, $oldAsOf ),
1129  self::VFLD_VERSION => $version
1130  ];
1131  },
1132  $opts,
1133  $asOf
1134  );
1135  if ( $cur[self::VFLD_VERSION] === $version ) {
1136  // Value created or existed before with version; use it
1137  $value = $cur[self::VFLD_DATA];
1138  } else {
1139  // Value existed before with a different version; use variant key.
1140  // Reflect purges to $key by requiring that this key value be newer.
1141  $value = $this->doGetWithSetCallback(
1142  $this->makeGlobalKey( 'WANCache-key-variant', md5( $key ), $version ),
1143  $ttl,
1144  $callback,
1145  // Regenerate value if not newer than $key
1146  [ 'version' => null, 'minAsOf' => $asOf ] + $opts
1147  );
1148  }
1149  } else {
1150  $value = $this->doGetWithSetCallback( $key, $ttl, $callback, $opts );
1151  }
1152 
1153  // Update the process cache if enabled
1154  if ( $procCache && $value !== false ) {
1155  $procCache->set( $key, $value );
1156  }
1157  }
1158 
1159  return $value;
1160  }
1161 
1175  protected function doGetWithSetCallback( $key, $ttl, $callback, array $opts, &$asOf = null ) {
1176  $lowTTL = $opts['lowTTL'] ?? min( self::LOW_TTL, $ttl );
1177  $lockTSE = $opts['lockTSE'] ?? self::TSE_NONE;
1178  $staleTTL = $opts['staleTTL'] ?? self::STALE_TTL_NONE;
1179  $graceTTL = $opts['graceTTL'] ?? self::GRACE_TTL_NONE;
1180  $checkKeys = $opts['checkKeys'] ?? [];
1181  $busyValue = $opts['busyValue'] ?? null;
1182  $popWindow = $opts['hotTTR'] ?? self::HOT_TTR;
1183  $ageNew = $opts['ageNew'] ?? self::AGE_NEW;
1184  $minTime = $opts['minAsOf'] ?? self::MIN_TIMESTAMP_NONE;
1185  $versioned = isset( $opts['version'] );
1186 
1187  // Get a collection name to describe this class of key
1188  $kClass = $this->determineKeyClass( $key );
1189 
1190  // Get the current key value
1191  $curTTL = null;
1192  $cValue = $this->get( $key, $curTTL, $checkKeys, $asOf ); // current value
1193  $value = $cValue; // return value
1194 
1195  $preCallbackTime = $this->getCurrentTime();
1196  // Determine if a cached value regeneration is needed or desired
1197  if ( $value !== false
1198  && $this->isAliveOrInGracePeriod( $curTTL, $graceTTL )
1199  && $this->isValid( $value, $versioned, $asOf, $minTime )
1200  ) {
1201  $preemptiveRefresh = (
1202  $this->worthRefreshExpiring( $curTTL, $lowTTL ) ||
1203  $this->worthRefreshPopular( $asOf, $ageNew, $popWindow, $preCallbackTime )
1204  );
1205 
1206  if ( !$preemptiveRefresh ) {
1207  $this->stats->increment( "wanobjectcache.$kClass.hit.good" );
1208 
1209  return $value;
1210  } elseif ( $this->asyncHandler ) {
1211  // Update the cache value later, such during post-send of an HTTP request
1212  $func = $this->asyncHandler;
1213  $func( function () use ( $key, $ttl, $callback, $opts, $asOf ) {
1214  $opts['minAsOf'] = INF; // force a refresh
1215  $this->doGetWithSetCallback( $key, $ttl, $callback, $opts, $asOf );
1216  } );
1217  $this->stats->increment( "wanobjectcache.$kClass.hit.refresh" );
1218 
1219  return $value;
1220  }
1221  }
1222 
1223  // A deleted key with a negative TTL left must be tombstoned
1224  $isTombstone = ( $curTTL !== null && $value === false );
1225  if ( $isTombstone && $lockTSE <= 0 ) {
1226  // Use the INTERIM value for tombstoned keys to reduce regeneration load
1227  $lockTSE = self::INTERIM_KEY_TTL;
1228  }
1229  // Assume a key is hot if requested soon after invalidation
1230  $isHot = ( $curTTL !== null && $curTTL <= 0 && abs( $curTTL ) <= $lockTSE );
1231  // Use the mutex if there is no value and a busy fallback is given
1232  $checkBusy = ( $busyValue !== null && $value === false );
1233  // Decide whether a single thread should handle regenerations.
1234  // This avoids stampedes when $checkKeys are bumped and when preemptive
1235  // renegerations take too long. It also reduces regenerations while $key
1236  // is tombstoned. This balances cache freshness with avoiding DB load.
1237  $useMutex = ( $isHot || ( $isTombstone && $lockTSE > 0 ) || $checkBusy );
1238 
1239  $lockAcquired = false;
1240  if ( $useMutex ) {
1241  // Acquire a datacenter-local non-blocking lock
1242  if ( $this->cache->add( self::MUTEX_KEY_PREFIX . $key, 1, self::LOCK_TTL ) ) {
1243  // Lock acquired; this thread should update the key
1244  $lockAcquired = true;
1245  } elseif ( $value !== false && $this->isValid( $value, $versioned, $asOf, $minTime ) ) {
1246  $this->stats->increment( "wanobjectcache.$kClass.hit.stale" );
1247  // If it cannot be acquired; then the stale value can be used
1248  return $value;
1249  } else {
1250  // Use the INTERIM value for tombstoned keys to reduce regeneration load.
1251  // For hot keys, either another thread has the lock or the lock failed;
1252  // use the INTERIM value from the last thread that regenerated it.
1253  $value = $this->getInterimValue( $key, $versioned, $minTime, $asOf );
1254  if ( $value !== false ) {
1255  $this->stats->increment( "wanobjectcache.$kClass.hit.volatile" );
1256 
1257  return $value;
1258  }
1259  // Use the busy fallback value if nothing else
1260  if ( $busyValue !== null ) {
1261  $miss = is_infinite( $minTime ) ? 'renew' : 'miss';
1262  $this->stats->increment( "wanobjectcache.$kClass.$miss.busy" );
1263 
1264  return is_callable( $busyValue ) ? $busyValue() : $busyValue;
1265  }
1266  }
1267  }
1268 
1269  if ( !is_callable( $callback ) ) {
1270  throw new InvalidArgumentException( "Invalid cache miss callback provided." );
1271  }
1272 
1273  // Generate the new value from the callback...
1274  $setOpts = [];
1276  try {
1277  $value = call_user_func_array( $callback, [ $cValue, &$ttl, &$setOpts, $asOf ] );
1278  } finally {
1280  }
1281  $valueIsCacheable = ( $value !== false && $ttl >= 0 );
1282 
1283  // When delete() is called, writes are write-holed by the tombstone,
1284  // so use a special INTERIM key to pass the new value around threads.
1285  if ( ( $isTombstone && $lockTSE > 0 ) && $valueIsCacheable ) {
1286  $tempTTL = max( 1, (int)$lockTSE ); // set() expects seconds
1287  $newAsOf = $this->getCurrentTime();
1288  $wrapped = $this->wrap( $value, $tempTTL, $newAsOf );
1289  // Avoid using set() to avoid pointless mcrouter broadcasting
1290  $this->setInterimValue( $key, $wrapped, $tempTTL );
1291  }
1292 
1293  if ( $valueIsCacheable ) {
1294  $setOpts['lockTSE'] = $lockTSE;
1295  $setOpts['staleTTL'] = $staleTTL;
1296  // Use best known "since" timestamp if not provided
1297  $setOpts += [ 'since' => $preCallbackTime ];
1298  // Update the cache; this will fail if the key is tombstoned
1299  $this->set( $key, $value, $ttl, $setOpts );
1300  }
1301 
1302  if ( $lockAcquired ) {
1303  // Avoid using delete() to avoid pointless mcrouter broadcasting
1304  $this->cache->changeTTL( self::MUTEX_KEY_PREFIX . $key, (int)$preCallbackTime - 60 );
1305  }
1306 
1307  $miss = is_infinite( $minTime ) ? 'renew' : 'miss';
1308  $this->stats->increment( "wanobjectcache.$kClass.$miss.compute" );
1309 
1310  return $value;
1311  }
1312 
1320  protected function getInterimValue( $key, $versioned, $minTime, &$asOf ) {
1321  if ( !$this->useInterimHoldOffCaching ) {
1322  return false; // disabled
1323  }
1324 
1325  $wrapped = $this->cache->get( self::INTERIM_KEY_PREFIX . $key );
1326  list( $value ) = $this->unwrap( $wrapped, $this->getCurrentTime() );
1327  if ( $value !== false && $this->isValid( $value, $versioned, $asOf, $minTime ) ) {
1328  $asOf = $wrapped[self::FLD_TIME];
1329 
1330  return $value;
1331  }
1332 
1333  return false;
1334  }
1335 
1341  protected function setInterimValue( $key, $wrapped, $tempTTL ) {
1342  $this->cache->merge(
1343  self::INTERIM_KEY_PREFIX . $key,
1344  function () use ( $wrapped ) {
1345  return $wrapped;
1346  },
1347  $tempTTL,
1348  1
1349  );
1350  }
1351 
1418  final public function getMultiWithSetCallback(
1419  ArrayIterator $keyedIds, $ttl, callable $callback, array $opts = []
1420  ) {
1421  $valueKeys = array_keys( $keyedIds->getArrayCopy() );
1422  $checkKeys = $opts['checkKeys'] ?? [];
1423  $pcTTL = $opts['pcTTL'] ?? self::TTL_UNCACHEABLE;
1424 
1425  // Load required keys into process cache in one go
1426  $this->warmupCache = $this->getRawKeysForWarmup(
1427  $this->getNonProcessCachedKeys( $valueKeys, $opts, $pcTTL ),
1428  $checkKeys
1429  );
1430  $this->warmupKeyMisses = 0;
1431 
1432  // Wrap $callback to match the getWithSetCallback() format while passing $id to $callback
1433  $id = null; // current entity ID
1434  $func = function ( $oldValue, &$ttl, &$setOpts, $oldAsOf ) use ( $callback, &$id ) {
1435  return $callback( $id, $oldValue, $ttl, $setOpts, $oldAsOf );
1436  };
1437 
1438  $values = [];
1439  foreach ( $keyedIds as $key => $id ) { // preserve order
1440  $values[$key] = $this->getWithSetCallback( $key, $ttl, $func, $opts );
1441  }
1442 
1443  $this->warmupCache = [];
1444 
1445  return $values;
1446  }
1447 
1513  final public function getMultiWithUnionSetCallback(
1514  ArrayIterator $keyedIds, $ttl, callable $callback, array $opts = []
1515  ) {
1516  $idsByValueKey = $keyedIds->getArrayCopy();
1517  $valueKeys = array_keys( $idsByValueKey );
1518  $checkKeys = $opts['checkKeys'] ?? [];
1519  $pcTTL = $opts['pcTTL'] ?? self::TTL_UNCACHEABLE;
1520  unset( $opts['lockTSE'] ); // incompatible
1521  unset( $opts['busyValue'] ); // incompatible
1522 
1523  // Load required keys into process cache in one go
1524  $keysGet = $this->getNonProcessCachedKeys( $valueKeys, $opts, $pcTTL );
1525  $this->warmupCache = $this->getRawKeysForWarmup( $keysGet, $checkKeys );
1526  $this->warmupKeyMisses = 0;
1527 
1528  // IDs of entities known to be in need of regeneration
1529  $idsRegen = [];
1530 
1531  // Find out which keys are missing/deleted/stale
1532  $curTTLs = [];
1533  $asOfs = [];
1534  $curByKey = $this->getMulti( $keysGet, $curTTLs, $checkKeys, $asOfs );
1535  foreach ( $keysGet as $key ) {
1536  if ( !array_key_exists( $key, $curByKey ) || $curTTLs[$key] < 0 ) {
1537  $idsRegen[] = $idsByValueKey[$key];
1538  }
1539  }
1540 
1541  // Run the callback to populate the regeneration value map for all required IDs
1542  $newSetOpts = [];
1543  $newTTLsById = array_fill_keys( $idsRegen, $ttl );
1544  $newValsById = $idsRegen ? $callback( $idsRegen, $newTTLsById, $newSetOpts ) : [];
1545 
1546  // Wrap $callback to match the getWithSetCallback() format while passing $id to $callback
1547  $id = null; // current entity ID
1548  $func = function ( $oldValue, &$ttl, &$setOpts, $oldAsOf )
1549  use ( $callback, &$id, $newValsById, $newTTLsById, $newSetOpts )
1550  {
1551  if ( array_key_exists( $id, $newValsById ) ) {
1552  // Value was already regerated as expected, so use the value in $newValsById
1553  $newValue = $newValsById[$id];
1554  $ttl = $newTTLsById[$id];
1555  $setOpts = $newSetOpts;
1556  } else {
1557  // Pre-emptive/popularity refresh and version mismatch cases are not detected
1558  // above and thus $newValsById has no entry. Run $callback on this single entity.
1559  $ttls = [ $id => $ttl ];
1560  $newValue = $callback( [ $id ], $ttls, $setOpts )[$id];
1561  $ttl = $ttls[$id];
1562  }
1563 
1564  return $newValue;
1565  };
1566 
1567  // Run the cache-aside logic using warmupCache instead of persistent cache queries
1568  $values = [];
1569  foreach ( $idsByValueKey as $key => $id ) { // preserve order
1570  $values[$key] = $this->getWithSetCallback( $key, $ttl, $func, $opts );
1571  }
1572 
1573  $this->warmupCache = [];
1574 
1575  return $values;
1576  }
1577 
1590  final public function reap( $key, $purgeTimestamp, &$isStale = false ) {
1591  $minAsOf = $purgeTimestamp + self::HOLDOFF_TTL;
1592  $wrapped = $this->cache->get( self::VALUE_KEY_PREFIX . $key );
1593  if ( is_array( $wrapped ) && $wrapped[self::FLD_TIME] < $minAsOf ) {
1594  $isStale = true;
1595  $this->logger->warning( "Reaping stale value key '$key'." );
1596  $ttlReap = self::HOLDOFF_TTL; // avoids races with tombstone creation
1597  $ok = $this->cache->changeTTL( self::VALUE_KEY_PREFIX . $key, $ttlReap );
1598  if ( !$ok ) {
1599  $this->logger->error( "Could not complete reap of key '$key'." );
1600  }
1601 
1602  return $ok;
1603  }
1604 
1605  $isStale = false;
1606 
1607  return true;
1608  }
1609 
1619  final public function reapCheckKey( $key, $purgeTimestamp, &$isStale = false ) {
1620  $purge = $this->parsePurgeValue( $this->cache->get( self::TIME_KEY_PREFIX . $key ) );
1621  if ( $purge && $purge[self::FLD_TIME] < $purgeTimestamp ) {
1622  $isStale = true;
1623  $this->logger->warning( "Reaping stale check key '$key'." );
1624  $ok = $this->cache->changeTTL( self::TIME_KEY_PREFIX . $key, self::TTL_SECOND );
1625  if ( !$ok ) {
1626  $this->logger->error( "Could not complete reap of check key '$key'." );
1627  }
1628 
1629  return $ok;
1630  }
1631 
1632  $isStale = false;
1633 
1634  return false;
1635  }
1636 
1644  public function makeKey( $class, $component = null ) {
1645  return $this->cache->makeKey( ...func_get_args() );
1646  }
1647 
1655  public function makeGlobalKey( $class, $component = null ) {
1656  return $this->cache->makeGlobalKey( ...func_get_args() );
1657  }
1658 
1665  final public function makeMultiKeys( array $entities, callable $keyFunc ) {
1666  $map = [];
1667  foreach ( $entities as $entity ) {
1668  $map[$keyFunc( $entity, $this )] = $entity;
1669  }
1670 
1671  return new ArrayIterator( $map );
1672  }
1673 
1678  final public function getLastError() {
1679  if ( $this->lastRelayError ) {
1680  // If the cache and the relayer failed, focus on the latter.
1681  // An update not making it to the relayer means it won't show up
1682  // in other DCs (nor will consistent re-hashing see up-to-date values).
1683  // On the other hand, if just the cache update failed, then it should
1684  // eventually be applied by the relayer.
1685  return $this->lastRelayError;
1686  }
1687 
1688  $code = $this->cache->getLastError();
1689  switch ( $code ) {
1690  case BagOStuff::ERR_NONE:
1691  return self::ERR_NONE;
1693  return self::ERR_NO_RESPONSE;
1695  return self::ERR_UNREACHABLE;
1696  default:
1697  return self::ERR_UNEXPECTED;
1698  }
1699  }
1700 
1704  final public function clearLastError() {
1705  $this->cache->clearLastError();
1706  $this->lastRelayError = self::ERR_NONE;
1707  }
1708 
1714  public function clearProcessCache() {
1715  $this->processCaches = [];
1716  }
1717 
1738  final public function useInterimHoldOffCaching( $enabled ) {
1739  $this->useInterimHoldOffCaching = $enabled;
1740  }
1741 
1747  public function getQoS( $flag ) {
1748  return $this->cache->getQoS( $flag );
1749  }
1750 
1814  public function adaptiveTTL( $mtime, $maxTTL, $minTTL = 30, $factor = 0.2 ) {
1815  if ( is_float( $mtime ) || ctype_digit( $mtime ) ) {
1816  $mtime = (int)$mtime; // handle fractional seconds and string integers
1817  }
1818 
1819  if ( !is_int( $mtime ) || $mtime <= 0 ) {
1820  return $minTTL; // no last-modified time provided
1821  }
1822 
1823  $age = $this->getCurrentTime() - $mtime;
1824 
1825  return (int)min( $maxTTL, max( $minTTL, $factor * $age ) );
1826  }
1827 
1832  final public function getWarmupKeyMisses() {
1833  return $this->warmupKeyMisses;
1834  }
1835 
1846  protected function relayPurge( $key, $ttl, $holdoff ) {
1847  if ( $this->mcrouterAware ) {
1848  // See https://github.com/facebook/mcrouter/wiki/Multi-cluster-broadcast-setup
1849  // Wildcards select all matching routes, e.g. the WAN cluster on all DCs
1850  $ok = $this->cache->set(
1851  "/*/{$this->cluster}/{$key}",
1852  $this->makePurgeValue( $this->getCurrentTime(), self::HOLDOFF_NONE ),
1853  $ttl
1854  );
1855  } elseif ( $this->purgeRelayer instanceof EventRelayerNull ) {
1856  // This handles the mcrouter and the single-DC case
1857  $ok = $this->cache->set(
1858  $key,
1859  $this->makePurgeValue( $this->getCurrentTime(), self::HOLDOFF_NONE ),
1860  $ttl
1861  );
1862  } else {
1863  $event = $this->cache->modifySimpleRelayEvent( [
1864  'cmd' => 'set',
1865  'key' => $key,
1866  'val' => 'PURGED:$UNIXTIME$:' . (int)$holdoff,
1867  'ttl' => max( $ttl, self::TTL_SECOND ),
1868  'sbt' => true, // substitute $UNIXTIME$ with actual microtime
1869  ] );
1870 
1871  $ok = $this->purgeRelayer->notify( $this->purgeChannel, $event );
1872  if ( !$ok ) {
1873  $this->lastRelayError = self::ERR_RELAY;
1874  }
1875  }
1876 
1877  return $ok;
1878  }
1879 
1886  protected function relayDelete( $key ) {
1887  if ( $this->mcrouterAware ) {
1888  // See https://github.com/facebook/mcrouter/wiki/Multi-cluster-broadcast-setup
1889  // Wildcards select all matching routes, e.g. the WAN cluster on all DCs
1890  $ok = $this->cache->delete( "/*/{$this->cluster}/{$key}" );
1891  } elseif ( $this->purgeRelayer instanceof EventRelayerNull ) {
1892  // Some other proxy handles broadcasting or there is only one datacenter
1893  $ok = $this->cache->delete( $key );
1894  } else {
1895  $event = $this->cache->modifySimpleRelayEvent( [
1896  'cmd' => 'delete',
1897  'key' => $key,
1898  ] );
1899 
1900  $ok = $this->purgeRelayer->notify( $this->purgeChannel, $event );
1901  if ( !$ok ) {
1902  $this->lastRelayError = self::ERR_RELAY;
1903  }
1904  }
1905 
1906  return $ok;
1907  }
1908 
1922  protected function isAliveOrInGracePeriod( $curTTL, $graceTTL ) {
1923  if ( $curTTL > 0 ) {
1924  return true;
1925  } elseif ( $graceTTL <= 0 ) {
1926  return false;
1927  }
1928 
1929  $ageStale = abs( $curTTL ); // seconds of staleness
1930  $curGTTL = ( $graceTTL - $ageStale ); // current grace-time-to-live
1931  if ( $curGTTL <= 0 ) {
1932  return false; // already out of grace period
1933  }
1934 
1935  // Chance of using a stale value is the complement of the chance of refreshing it
1936  return !$this->worthRefreshExpiring( $curGTTL, $graceTTL );
1937  }
1938 
1952  protected function worthRefreshExpiring( $curTTL, $lowTTL ) {
1953  if ( $lowTTL <= 0 ) {
1954  return false;
1955  } elseif ( $curTTL >= $lowTTL ) {
1956  return false;
1957  } elseif ( $curTTL <= 0 ) {
1958  return false;
1959  }
1960 
1961  $chance = ( 1 - $curTTL / $lowTTL );
1962 
1963  return mt_rand( 1, 1e9 ) <= 1e9 * $chance;
1964  }
1965 
1981  protected function worthRefreshPopular( $asOf, $ageNew, $timeTillRefresh, $now ) {
1982  if ( $ageNew < 0 || $timeTillRefresh <= 0 ) {
1983  return false;
1984  }
1985 
1986  $age = $now - $asOf;
1987  $timeOld = $age - $ageNew;
1988  if ( $timeOld <= 0 ) {
1989  return false;
1990  }
1991 
1992  // Lifecycle is: new, ramp-up refresh chance, full refresh chance.
1993  // Note that the "expected # of refreshes" for the ramp-up time range is half of what it
1994  // would be if P(refresh) was at its full value during that time range.
1995  $refreshWindowSec = max( $timeTillRefresh - $ageNew - self::RAMPUP_TTL / 2, 1 );
1996  // P(refresh) * (# hits in $refreshWindowSec) = (expected # of refreshes)
1997  // P(refresh) * ($refreshWindowSec * $popularHitsPerSec) = 1
1998  // P(refresh) = 1/($refreshWindowSec * $popularHitsPerSec)
1999  $chance = 1 / ( self::HIT_RATE_HIGH * $refreshWindowSec );
2000 
2001  // Ramp up $chance from 0 to its nominal value over RAMPUP_TTL seconds to avoid stampedes
2002  $chance *= ( $timeOld <= self::RAMPUP_TTL ) ? $timeOld / self::RAMPUP_TTL : 1;
2003 
2004  return mt_rand( 1, 1e9 ) <= 1e9 * $chance;
2005  }
2006 
2016  protected function isValid( $value, $versioned, $asOf, $minTime ) {
2017  if ( $versioned && !isset( $value[self::VFLD_VERSION] ) ) {
2018  return false;
2019  } elseif ( $minTime > 0 && $asOf < $minTime ) {
2020  return false;
2021  }
2022 
2023  return true;
2024  }
2025 
2034  protected function wrap( $value, $ttl, $now ) {
2035  return [
2036  self::FLD_VERSION => self::VERSION,
2037  self::FLD_VALUE => $value,
2038  self::FLD_TTL => $ttl,
2039  self::FLD_TIME => $now
2040  ];
2041  }
2042 
2050  protected function unwrap( $wrapped, $now ) {
2051  // Check if the value is a tombstone
2052  $purge = $this->parsePurgeValue( $wrapped );
2053  if ( $purge !== false ) {
2054  // Purged values should always have a negative current $ttl
2055  $curTTL = min( $purge[self::FLD_TIME] - $now, self::TINY_NEGATIVE );
2056  return [ false, $curTTL ];
2057  }
2058 
2059  if ( !is_array( $wrapped ) // not found
2060  || !isset( $wrapped[self::FLD_VERSION] ) // wrong format
2061  || $wrapped[self::FLD_VERSION] !== self::VERSION // wrong version
2062  ) {
2063  return [ false, null ];
2064  }
2065 
2066  $flags = $wrapped[self::FLD_FLAGS] ?? 0;
2067  if ( ( $flags & self::FLG_STALE ) == self::FLG_STALE ) {
2068  // Treat as expired, with the cache time as the expiration
2069  $age = $now - $wrapped[self::FLD_TIME];
2070  $curTTL = min( -$age, self::TINY_NEGATIVE );
2071  } elseif ( $wrapped[self::FLD_TTL] > 0 ) {
2072  // Get the approximate time left on the key
2073  $age = $now - $wrapped[self::FLD_TIME];
2074  $curTTL = max( $wrapped[self::FLD_TTL] - $age, 0.0 );
2075  } else {
2076  // Key had no TTL, so the time left is unbounded
2077  $curTTL = INF;
2078  }
2079 
2080  if ( $wrapped[self::FLD_TIME] < $this->epoch ) {
2081  // Values this old are ignored
2082  return [ false, null ];
2083  }
2084 
2085  return [ $wrapped[self::FLD_VALUE], $curTTL ];
2086  }
2087 
2093  protected static function prefixCacheKeys( array $keys, $prefix ) {
2094  $res = [];
2095  foreach ( $keys as $key ) {
2096  $res[] = $prefix . $key;
2097  }
2098 
2099  return $res;
2100  }
2101 
2106  protected function determineKeyClass( $key ) {
2107  $parts = explode( ':', $key );
2108 
2109  return $parts[1] ?? $parts[0]; // sanity
2110  }
2111 
2117  protected function parsePurgeValue( $value ) {
2118  if ( !is_string( $value ) ) {
2119  return false;
2120  }
2121 
2122  $segments = explode( ':', $value, 3 );
2123  if ( !isset( $segments[0] ) || !isset( $segments[1] )
2124  || "{$segments[0]}:" !== self::PURGE_VAL_PREFIX
2125  ) {
2126  return false;
2127  }
2128 
2129  if ( !isset( $segments[2] ) ) {
2130  // Back-compat with old purge values without holdoff
2131  $segments[2] = self::HOLDOFF_TTL;
2132  }
2133 
2134  if ( $segments[1] < $this->epoch ) {
2135  // Values this old are ignored
2136  return false;
2137  }
2138 
2139  return [
2140  self::FLD_TIME => (float)$segments[1],
2141  self::FLD_HOLDOFF => (int)$segments[2],
2142  ];
2143  }
2144 
2150  protected function makePurgeValue( $timestamp, $holdoff ) {
2151  return self::PURGE_VAL_PREFIX . (float)$timestamp . ':' . (int)$holdoff;
2152  }
2153 
2158  protected function getProcessCache( $group ) {
2159  if ( !isset( $this->processCaches[$group] ) ) {
2160  list( , $n ) = explode( ':', $group );
2161  $this->processCaches[$group] = new MapCacheLRU( (int)$n );
2162  }
2163 
2164  return $this->processCaches[$group];
2165  }
2166 
2173  private function getNonProcessCachedKeys( array $keys, array $opts, $pcTTL ) {
2174  $keysFound = [];
2175  if ( isset( $opts['pcTTL'] ) && $opts['pcTTL'] > 0 && $this->callbackDepth == 0 ) {
2176  $pcGroup = $opts['pcGroup'] ?? self::PC_PRIMARY;
2177  $procCache = $this->getProcessCache( $pcGroup );
2178  foreach ( $keys as $key ) {
2179  if ( $procCache->has( $key, $pcTTL ) ) {
2180  $keysFound[] = $key;
2181  }
2182  }
2183  }
2184 
2185  return array_diff( $keys, $keysFound );
2186  }
2187 
2193  private function getRawKeysForWarmup( array $keys, array $checkKeys ) {
2194  if ( !$keys ) {
2195  return [];
2196  }
2197 
2198  $keysWarmUp = [];
2199  // Get all the value keys to fetch...
2200  foreach ( $keys as $key ) {
2201  $keysWarmUp[] = self::VALUE_KEY_PREFIX . $key;
2202  }
2203  // Get all the check keys to fetch...
2204  foreach ( $checkKeys as $i => $checkKeyOrKeys ) {
2205  if ( is_int( $i ) ) {
2206  // Single check key that applies to all value keys
2207  $keysWarmUp[] = self::TIME_KEY_PREFIX . $checkKeyOrKeys;
2208  } else {
2209  // List of check keys that apply to value key $i
2210  $keysWarmUp = array_merge(
2211  $keysWarmUp,
2212  self::prefixCacheKeys( $checkKeyOrKeys, self::TIME_KEY_PREFIX )
2213  );
2214  }
2215  }
2216 
2217  $warmupCache = $this->cache->getMulti( $keysWarmUp );
2218  $warmupCache += array_fill_keys( $keysWarmUp, false );
2219 
2220  return $warmupCache;
2221  }
2222 
2227  protected function getCurrentTime() {
2228  return $this->wallClockOverride ?: microtime( true );
2229  }
2230 
2235  public function setMockTime( &$time ) {
2236  $this->wallClockOverride =& $time;
2237  $this->cache->setMockTime( $time );
2238  }
2239 }
WANObjectCache\ERR_UNREACHABLE
const ERR_UNREACHABLE
Definition: WANObjectCache.php:217
WANObjectCache\getQoS
getQoS( $flag)
Definition: WANObjectCache.php:1747
WANObjectCache\relayDelete
relayDelete( $key)
Do the actual async bus delete of a key.
Definition: WANObjectCache.php:1886
WANObjectCache\VALUE_KEY_PREFIX
const VALUE_KEY_PREFIX
Definition: WANObjectCache.php:221
WANObjectCache\TTL_UNCACHEABLE
const TTL_UNCACHEABLE
Idiom for getWithSetCallback() callbacks to avoid calling set()
Definition: WANObjectCache.php:184
WANObjectCache\setMockTime
setMockTime(&$time)
Definition: WANObjectCache.php:2235
WANObjectCache\makePurgeValue
makePurgeValue( $timestamp, $holdoff)
Definition: WANObjectCache.php:2150
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
WANObjectCache\$warmupCache
mixed[] $warmupCache
Temporary warm-up cache.
Definition: WANObjectCache.php:150
BagOStuff\ERR_UNREACHABLE
const ERR_UNREACHABLE
Definition: BagOStuff.php:93
EmptyBagOStuff
A BagOStuff object with no objects in it.
Definition: EmptyBagOStuff.php:29
WANObjectCache\$cache
BagOStuff $cache
The local datacenter cache.
Definition: WANObjectCache.php:120
WANObjectCache\determineKeyClass
determineKeyClass( $key)
Definition: WANObjectCache.php:2106
WANObjectCache\MAX_READ_LAG
const MAX_READ_LAG
Max replication+snapshot lag before applying TTL_LAGGED or disallowing set()
Definition: WANObjectCache.php:160
captcha-old.count
count
Definition: captcha-old.py:249
WANObjectCache\$purgeChannel
string $purgeChannel
Purge channel name.
Definition: WANObjectCache.php:124
WANObjectCache\FLD_VERSION
const FLD_VERSION
Definition: WANObjectCache.php:205
WANObjectCache\FLD_VALUE
const FLD_VALUE
Definition: WANObjectCache.php:206
WANObjectCache\MAX_COMMIT_DELAY
const MAX_COMMIT_DELAY
Max time expected to pass between delete() and DB commit finishing.
Definition: WANObjectCache.php:158
WANObjectCache\isValid
isValid( $value, $versioned, $asOf, $minTime)
Check whether $value is appropriately versioned and not older than $minTime (if set)
Definition: WANObjectCache.php:2016
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUnknownUser':When a user doesn 't exist locally, this hook is called to give extensions an opportunity to auto-create it. If the auto-creation is successful, return false. $name:User name 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetMagic':DEPRECATED since 1.16! Use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language & $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetSpecialPageAliases':DEPRECATED! Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language & $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Array with elements of the form "language:title" in the order that they will be output. & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED since 1.28! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:2034
WANObjectCache\getInterimValue
getInterimValue( $key, $versioned, $minTime, &$asOf)
Definition: WANObjectCache.php:1320
EventRelayerNull
No-op class for publishing messages into a PubSub system.
Definition: EventRelayerNull.php:24
WANObjectCache\makeMultiKeys
makeMultiKeys(array $entities, callable $keyFunc)
Definition: WANObjectCache.php:1665
NullStatsdDataFactory
Definition: NullStatsdDataFactory.php:10
WANObjectCache\getMultiWithSetCallback
getMultiWithSetCallback(ArrayIterator $keyedIds, $ttl, callable $callback, array $opts=[])
Method to fetch multiple cache keys at once with regeneration.
Definition: WANObjectCache.php:1418
$params
$params
Definition: styleTest.css.php:44
BagOStuff
Class representing a cache/ephemeral data store.
Definition: BagOStuff.php:58
WANObjectCache\getMultiWithUnionSetCallback
getMultiWithUnionSetCallback(ArrayIterator $keyedIds, $ttl, callable $callback, array $opts=[])
Method to fetch/regenerate multiple cache keys at once.
Definition: WANObjectCache.php:1513
$res
$res
Definition: database.txt:21
WANObjectCache\VFLD_VERSION
const VFLD_VERSION
Definition: WANObjectCache.php:229
cache
you have access to all of the normal MediaWiki so you can get a DB use the cache
Definition: maintenance.txt:52
WANObjectCache\TTL_LAGGED
const TTL_LAGGED
Max TTL to store keys when a data sourced is lagged.
Definition: WANObjectCache.php:188
BagOStuff\ERR_NONE
const ERR_NONE
Possible values for getLastError()
Definition: BagOStuff.php:91
WANObjectCache\reapCheckKey
reapCheckKey( $key, $purgeTimestamp, &$isStale=false)
Set a "check" key to soon expire in the local cluster if it pre-dates $purgeTimestamp.
Definition: WANObjectCache.php:1619
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
WANObjectCache\getCheckKeyTime
getCheckKeyTime( $key)
Fetch the value of a timestamp "check" key.
Definition: WANObjectCache.php:676
WANObjectCache\$logger
LoggerInterface $logger
Definition: WANObjectCache.php:134
WANObjectCache\VFLD_DATA
const VFLD_DATA
Definition: WANObjectCache.php:228
WANObjectCache\$purgeRelayer
EventRelayer $purgeRelayer
Bus that handles purge broadcasts.
Definition: WANObjectCache.php:126
WANObjectCache\$warmupKeyMisses
int $warmupKeyMisses
Key fetched.
Definition: WANObjectCache.php:152
WANObjectCache\newEmpty
static newEmpty()
Get an instance that wraps EmptyBagOStuff.
Definition: WANObjectCache.php:287
WANObjectCache\ERR_RELAY
const ERR_RELAY
Definition: WANObjectCache.php:219
WANObjectCache\TIME_KEY_PREFIX
const TIME_KEY_PREFIX
Definition: WANObjectCache.php:223
WANObjectCache\LOW_TTL
const LOW_TTL
Default remaining TTL at which to consider pre-emptive regeneration.
Definition: WANObjectCache.php:172
WANObjectCache\FLD_TIME
const FLD_TIME
Definition: WANObjectCache.php:208
IExpiringStore
Generic base class for storage interfaces.
Definition: IExpiringStore.php:31
WANObjectCache\clearProcessCache
clearProcessCache()
Clear the in-process caches; useful for testing.
Definition: WANObjectCache.php:1714
WANObjectCache\clearLastError
clearLastError()
Clear the "last error" registry.
Definition: WANObjectCache.php:1704
WANObjectCache\getCurrentTime
getCurrentTime()
Definition: WANObjectCache.php:2227
WANObjectCache\getWithSetCallback
getWithSetCallback( $key, $ttl, $callback, array $opts=[])
Method to fetch/regenerate cache keys.
Definition: WANObjectCache.php:1090
WANObjectCache\INTERIM_KEY_TTL
const INTERIM_KEY_TTL
Seconds to keep interim value keys for tombstoned keys around.
Definition: WANObjectCache.php:167
WANObjectCache\prefixCacheKeys
static prefixCacheKeys(array $keys, $prefix)
Definition: WANObjectCache.php:2093
WANObjectCache\unwrap
unwrap( $wrapped, $now)
Do not use this method outside WANObjectCache.
Definition: WANObjectCache.php:2050
WANObjectCache\MIN_TIMESTAMP_NONE
const MIN_TIMESTAMP_NONE
Idiom for getWithSetCallback() for "no minimum required as-of timestamp".
Definition: WANObjectCache.php:197
WANObjectCache\getLastError
getLastError()
Get the "last error" registered; clearLastError() should be called manually.
Definition: WANObjectCache.php:1678
WANObjectCache\INTERIM_KEY_PREFIX
const INTERIM_KEY_PREFIX
Definition: WANObjectCache.php:222
WANObjectCache\__construct
__construct(array $params)
Definition: WANObjectCache.php:261
WANObjectCache\useInterimHoldOffCaching
useInterimHoldOffCaching( $enabled)
Enable or disable the use of brief caching for tombstoned keys.
Definition: WANObjectCache.php:1738
WANObjectCache\reap
reap( $key, $purgeTimestamp, &$isStale=false)
Set a key to soon expire in the local cluster if it pre-dates $purgeTimestamp.
Definition: WANObjectCache.php:1590
WANObjectCache\touchCheckKey
touchCheckKey( $key, $holdoff=self::HOLDOFF_TTL)
Purge a "check" key from all datacenters, invalidating keys that use it.
Definition: WANObjectCache.php:806
WANObjectCache\DEFAULT_PURGE_CHANNEL
const DEFAULT_PURGE_CHANNEL
Definition: WANObjectCache.php:233
MapCacheLRU
Handles a simple LRU key/value map with a maximum number of entries.
Definition: MapCacheLRU.php:37
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
$code
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable & $code
Definition: hooks.txt:813
WANObjectCache\ERR_NONE
const ERR_NONE
Definition: WANObjectCache.php:215
$time
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1841
WANObjectCache\adaptiveTTL
adaptiveTTL( $mtime, $maxTTL, $minTTL=30, $factor=0.2)
Get a TTL that is higher for objects that have not changed recently.
Definition: WANObjectCache.php:1814
WANObjectCache\$epoch
float $epoch
Unix timestamp of the oldest possible valid values.
Definition: WANObjectCache.php:142
WANObjectCache\relayPurge
relayPurge( $key, $ttl, $holdoff)
Do the actual async bus purge of a key.
Definition: WANObjectCache.php:1846
array
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
WANObjectCache\$wallClockOverride
float null $wallClockOverride
Definition: WANObjectCache.php:155
WANObjectCache\HOLDOFF_TTL
const HOLDOFF_TTL
Seconds to tombstone keys on delete()
Definition: WANObjectCache.php:162
string
This code would result in ircNotify being run twice when an article is and once for brion Hooks can return three possible true was required This is the default since MediaWiki *some string
Definition: hooks.txt:175
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
WANObjectCache\getMultiCheckKeyTime
getMultiCheckKeyTime(array $keys)
Fetch the values of each timestamp "check" key.
Definition: WANObjectCache.php:741
WANObjectCache\worthRefreshPopular
worthRefreshPopular( $asOf, $ageNew, $timeTillRefresh, $now)
Check if a key is due for randomized regeneration due to its popularity.
Definition: WANObjectCache.php:1981
WANObjectCache\isAliveOrInGracePeriod
isAliveOrInGracePeriod( $curTTL, $graceTTL)
Check if a key is fresh or in the grace window and thus due for randomized reuse.
Definition: WANObjectCache.php:1922
WANObjectCache\ERR_NO_RESPONSE
const ERR_NO_RESPONSE
Definition: WANObjectCache.php:216
WANObjectCache\RAMPUP_TTL
const RAMPUP_TTL
Seconds to ramp up to the "popularity" refresh chance after a key is no longer new.
Definition: WANObjectCache.php:181
WANObjectCache\wrap
wrap( $value, $ttl, $now)
Do not use this method outside WANObjectCache.
Definition: WANObjectCache.php:2034
WANObjectCache\$useInterimHoldOffCaching
bool $useInterimHoldOffCaching
Whether to use "interim" caching while keys are tombstoned.
Definition: WANObjectCache.php:138
$value
$value
Definition: styleTest.css.php:49
WANObjectCache\MUTEX_KEY_PREFIX
const MUTEX_KEY_PREFIX
Definition: WANObjectCache.php:224
WANObjectCache\PC_PRIMARY
const PC_PRIMARY
Definition: WANObjectCache.php:231
WANObjectCache\makeGlobalKey
makeGlobalKey( $class, $component=null)
Definition: WANObjectCache.php:1655
WANObjectCache\$cluster
string $cluster
Cache cluster name for mcrouter use.
Definition: WANObjectCache.php:132
WANObjectCache\worthRefreshExpiring
worthRefreshExpiring( $curTTL, $lowTTL)
Check if a key is nearing expiration and thus due for randomized regeneration.
Definition: WANObjectCache.php:1952
WANObjectCache
Multi-datacenter aware caching interface.
Definition: WANObjectCache.php:118
WANObjectCache\VERSION
const VERSION
Cache format version number.
Definition: WANObjectCache.php:203
WANObjectCache\getProcessCache
getProcessCache( $group)
Definition: WANObjectCache.php:2158
WANObjectCache\makeKey
makeKey( $class, $component=null)
Definition: WANObjectCache.php:1644
WANObjectCache\getRawKeysForWarmup
getRawKeysForWarmup(array $keys, array $checkKeys)
Definition: WANObjectCache.php:2193
WANObjectCache\getMulti
getMulti(array $keys, &$curTTLs=[], array $checkKeys=[], array &$asOfs=[])
Fetch the value of several keys from cache.
Definition: WANObjectCache.php:356
WANObjectCache\doGetWithSetCallback
doGetWithSetCallback( $key, $ttl, $callback, array $opts, &$asOf=null)
Do the actual I/O for getWithSetCallback() when needed.
Definition: WANObjectCache.php:1175
WANObjectCache\resetCheckKey
resetCheckKey( $key)
Delete a "check" key from all datacenters, invalidating keys that use it.
Definition: WANObjectCache.php:838
WANObjectCache\TSE_NONE
const TSE_NONE
Idiom for getWithSetCallback() callbacks to 'lockTSE' logic.
Definition: WANObjectCache.php:186
WANObjectCache\parsePurgeValue
parsePurgeValue( $value)
Definition: WANObjectCache.php:2117
WANObjectCache\HOT_TTR
const HOT_TTR
The time length of the "popularity" refresh window for hot keys.
Definition: WANObjectCache.php:177
WANObjectCache\LOCK_TTL
const LOCK_TTL
Seconds to keep lock keys around.
Definition: WANObjectCache.php:170
WANObjectCache\getNonProcessCachedKeys
getNonProcessCachedKeys(array $keys, array $opts, $pcTTL)
Definition: WANObjectCache.php:2173
WANObjectCache\PURGE_VAL_PREFIX
const PURGE_VAL_PREFIX
Definition: WANObjectCache.php:226
WANObjectCache\$asyncHandler
callable null $asyncHandler
Function that takes a WAN cache callback and runs it later.
Definition: WANObjectCache.php:140
WANObjectCache\getWarmupKeyMisses
getWarmupKeyMisses()
Definition: WANObjectCache.php:1832
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
WANObjectCache\$mcrouterAware
$mcrouterAware
@bar bool Whether to use mcrouter key prefixing for routing
Definition: WANObjectCache.php:128
IExpiringStore\TTL_YEAR
const TTL_YEAR
Definition: IExpiringStore.php:39
$keys
$keys
Definition: testCompression.php:67
WANObjectCache\$lastRelayError
int $lastRelayError
ERR_* constant for the "last error" registry.
Definition: WANObjectCache.php:145
WANObjectCache\HOLDOFF_NONE
const HOLDOFF_NONE
Idiom for delete() for "no hold-off".
Definition: WANObjectCache.php:190
WANObjectCache\$callbackDepth
int $callbackDepth
Callback stack depth for getWithSetCallback()
Definition: WANObjectCache.php:148
WANObjectCache\FLD_FLAGS
const FLD_FLAGS
Definition: WANObjectCache.php:209
WANObjectCache\AGE_NEW
const AGE_NEW
Never consider performing "popularity" refreshes until a key reaches this age.
Definition: WANObjectCache.php:175
WANObjectCache\setLogger
setLogger(LoggerInterface $logger)
Definition: WANObjectCache.php:278
WANObjectCache\setInterimValue
setInterimValue( $key, $wrapped, $tempTTL)
Definition: WANObjectCache.php:1341
WANObjectCache\TINY_NEGATIVE
const TINY_NEGATIVE
Tiny negative float to use when CTL comes up >= 0 due to clock skew.
Definition: WANObjectCache.php:200
WANObjectCache\CHECK_KEY_TTL
const CHECK_KEY_TTL
Seconds to keep dependency purge keys around.
Definition: WANObjectCache.php:165
WANObjectCache\GRACE_TTL_NONE
const GRACE_TTL_NONE
Idiom for set()/getWithSetCallback() for "no post-expired grace period".
Definition: WANObjectCache.php:194
EventRelayer
Base class for reliable event relays.
Definition: EventRelayer.php:27
WANObjectCache\FLD_HOLDOFF
const FLD_HOLDOFF
Definition: WANObjectCache.php:210
BagOStuff\ERR_NO_RESPONSE
const ERR_NO_RESPONSE
Definition: BagOStuff.php:92
WANObjectCache\ERR_UNEXPECTED
const ERR_UNEXPECTED
Definition: WANObjectCache.php:218
WANObjectCache\FLD_TTL
const FLD_TTL
Definition: WANObjectCache.php:207
WANObjectCache\HIT_RATE_HIGH
const HIT_RATE_HIGH
Hits/second for a refresh to be expected within the "popularity" window.
Definition: WANObjectCache.php:179
WANObjectCache\STALE_TTL_NONE
const STALE_TTL_NONE
Idiom for set()/getWithSetCallback() for "do not augment the storage medium TTL".
Definition: WANObjectCache.php:192
WANObjectCache\$stats
StatsdDataFactoryInterface $stats
Definition: WANObjectCache.php:136
WANObjectCache\$processCaches
MapCacheLRU[] $processCaches
Map of group PHP instance caches.
Definition: WANObjectCache.php:122
WANObjectCache\processCheckKeys
processCheckKeys(array $timeKeys, array $wrappedValues, $now)
Definition: WANObjectCache.php:446
WANObjectCache\$region
string $region
Physical region for mcrouter use.
Definition: WANObjectCache.php:130