MediaWiki  1.31.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 
87 class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
89  protected $cache;
91  protected $processCaches = [];
93  protected $purgeChannel;
95  protected $purgeRelayer;
97  protected $mcrouterAware;
99  protected $region;
101  protected $cluster;
103  protected $logger;
105  protected $stats;
107  protected $useInterimHoldOffCaching = true;
109  protected $asyncHandler;
110 
113 
115  private $callbackDepth = 0;
117  private $warmupCache = [];
119  private $warmupKeyMisses = 0;
120 
122  const MAX_COMMIT_DELAY = 3;
124  const MAX_READ_LAG = 7;
126  const HOLDOFF_TTL = 11; // MAX_COMMIT_DELAY + MAX_READ_LAG + 1
127 
131  const INTERIM_KEY_TTL = 1;
132 
134  const LOCK_TTL = 10;
136  const LOW_TTL = 30;
137 
139  const AGE_NEW = 60;
141  const HOT_TTR = 900;
143  const HIT_RATE_HIGH = 1;
145  const RAMPUP_TTL = 30;
146 
148  const TTL_UNCACHEABLE = -1;
150  const TSE_NONE = -1;
152  const TTL_LAGGED = 30;
154  const HOLDOFF_NONE = 0;
156  const STALE_TTL_NONE = 0;
158  const GRACE_TTL_NONE = 0;
159 
161  const MIN_TIMESTAMP_NONE = 0.0;
162 
164  const TINY_NEGATIVE = -0.000001;
165 
167  const VERSION = 1;
168 
169  const FLD_VERSION = 0; // key to cache version number
170  const FLD_VALUE = 1; // key to the cached value
171  const FLD_TTL = 2; // key to the original TTL
172  const FLD_TIME = 3; // key to the cache time
173  const FLD_FLAGS = 4; // key to the flags bitfield
174  const FLD_HOLDOFF = 5; // key to any hold-off TTL
175 
177  const FLG_STALE = 1;
178 
179  const ERR_NONE = 0; // no error
180  const ERR_NO_RESPONSE = 1; // no response
181  const ERR_UNREACHABLE = 2; // can't connect
182  const ERR_UNEXPECTED = 3; // response gave some error
183  const ERR_RELAY = 4; // relay broadcast failed
184 
185  const VALUE_KEY_PREFIX = 'WANCache:v:';
186  const INTERIM_KEY_PREFIX = 'WANCache:i:';
187  const TIME_KEY_PREFIX = 'WANCache:t:';
188  const MUTEX_KEY_PREFIX = 'WANCache:m:';
189 
190  const PURGE_VAL_PREFIX = 'PURGED:';
191 
192  const VFLD_DATA = 'WOC:d'; // key to the value of versioned data
193  const VFLD_VERSION = 'WOC:v'; // key to the version of the value present
194 
195  const PC_PRIMARY = 'primary:1000'; // process cache name and max key count
196 
197  const DEFAULT_PURGE_CHANNEL = 'wancache-purge';
198 
224  public function __construct( array $params ) {
225  $this->cache = $params['cache'];
226  $this->purgeChannel = isset( $params['channels']['purge'] )
227  ? $params['channels']['purge']
229  $this->purgeRelayer = isset( $params['relayers']['purge'] )
230  ? $params['relayers']['purge']
231  : new EventRelayerNull( [] );
232  $this->region = isset( $params['region'] ) ? $params['region'] : 'main';
233  $this->cluster = isset( $params['cluster'] ) ? $params['cluster'] : 'wan-main';
234  $this->mcrouterAware = !empty( $params['mcrouterAware'] );
235 
236  $this->setLogger( isset( $params['logger'] ) ? $params['logger'] : new NullLogger() );
237  $this->stats = isset( $params['stats'] ) ? $params['stats'] : new NullStatsdDataFactory();
238  $this->asyncHandler = isset( $params['asyncHandler'] ) ? $params['asyncHandler'] : null;
239  }
240 
244  public function setLogger( LoggerInterface $logger ) {
245  $this->logger = $logger;
246  }
247 
253  public static function newEmpty() {
254  return new static( [
255  'cache' => new EmptyBagOStuff()
256  ] );
257  }
258 
300  final public function get( $key, &$curTTL = null, array $checkKeys = [], &$asOf = null ) {
301  $curTTLs = [];
302  $asOfs = [];
303  $values = $this->getMulti( [ $key ], $curTTLs, $checkKeys, $asOfs );
304  $curTTL = isset( $curTTLs[$key] ) ? $curTTLs[$key] : null;
305  $asOf = isset( $asOfs[$key] ) ? $asOfs[$key] : null;
306 
307  return isset( $values[$key] ) ? $values[$key] : false;
308  }
309 
322  final public function getMulti(
323  array $keys, &$curTTLs = [], array $checkKeys = [], array &$asOfs = []
324  ) {
325  $result = [];
326  $curTTLs = [];
327  $asOfs = [];
328 
329  $vPrefixLen = strlen( self::VALUE_KEY_PREFIX );
330  $valueKeys = self::prefixCacheKeys( $keys, self::VALUE_KEY_PREFIX );
331 
332  $checkKeysForAll = [];
333  $checkKeysByKey = [];
334  $checkKeysFlat = [];
335  foreach ( $checkKeys as $i => $checkKeyGroup ) {
336  $prefixed = self::prefixCacheKeys( (array)$checkKeyGroup, self::TIME_KEY_PREFIX );
337  $checkKeysFlat = array_merge( $checkKeysFlat, $prefixed );
338  // Is this check keys for a specific cache key, or for all keys being fetched?
339  if ( is_int( $i ) ) {
340  $checkKeysForAll = array_merge( $checkKeysForAll, $prefixed );
341  } else {
342  $checkKeysByKey[$i] = isset( $checkKeysByKey[$i] )
343  ? array_merge( $checkKeysByKey[$i], $prefixed )
344  : $prefixed;
345  }
346  }
347 
348  // Fetch all of the raw values
349  $keysGet = array_merge( $valueKeys, $checkKeysFlat );
350  if ( $this->warmupCache ) {
351  $wrappedValues = array_intersect_key( $this->warmupCache, array_flip( $keysGet ) );
352  $keysGet = array_diff( $keysGet, array_keys( $wrappedValues ) ); // keys left to fetch
353  $this->warmupKeyMisses += count( $keysGet );
354  } else {
355  $wrappedValues = [];
356  }
357  if ( $keysGet ) {
358  $wrappedValues += $this->cache->getMulti( $keysGet );
359  }
360  // Time used to compare/init "check" keys (derived after getMulti() to be pessimistic)
361  $now = $this->getCurrentTime();
362 
363  // Collect timestamps from all "check" keys
364  $purgeValuesForAll = $this->processCheckKeys( $checkKeysForAll, $wrappedValues, $now );
365  $purgeValuesByKey = [];
366  foreach ( $checkKeysByKey as $cacheKey => $checks ) {
367  $purgeValuesByKey[$cacheKey] =
368  $this->processCheckKeys( $checks, $wrappedValues, $now );
369  }
370 
371  // Get the main cache value for each key and validate them
372  foreach ( $valueKeys as $vKey ) {
373  if ( !isset( $wrappedValues[$vKey] ) ) {
374  continue; // not found
375  }
376 
377  $key = substr( $vKey, $vPrefixLen ); // unprefix
378 
379  list( $value, $curTTL ) = $this->unwrap( $wrappedValues[$vKey], $now );
380  if ( $value !== false ) {
381  $result[$key] = $value;
382 
383  // Force dependant keys to be invalid for a while after purging
384  // to reduce race conditions involving stale data getting cached
385  $purgeValues = $purgeValuesForAll;
386  if ( isset( $purgeValuesByKey[$key] ) ) {
387  $purgeValues = array_merge( $purgeValues, $purgeValuesByKey[$key] );
388  }
389  foreach ( $purgeValues as $purge ) {
390  $safeTimestamp = $purge[self::FLD_TIME] + $purge[self::FLD_HOLDOFF];
391  if ( $safeTimestamp >= $wrappedValues[$vKey][self::FLD_TIME] ) {
392  // How long ago this value was expired by *this* check key
393  $ago = min( $purge[self::FLD_TIME] - $now, self::TINY_NEGATIVE );
394  // How long ago this value was expired by *any* known check key
395  $curTTL = min( $curTTL, $ago );
396  }
397  }
398  }
399  $curTTLs[$key] = $curTTL;
400  $asOfs[$key] = ( $value !== false ) ? $wrappedValues[$vKey][self::FLD_TIME] : null;
401  }
402 
403  return $result;
404  }
405 
413  private function processCheckKeys( array $timeKeys, array $wrappedValues, $now ) {
414  $purgeValues = [];
415  foreach ( $timeKeys as $timeKey ) {
416  $purge = isset( $wrappedValues[$timeKey] )
417  ? $this->parsePurgeValue( $wrappedValues[$timeKey] )
418  : false;
419  if ( $purge === false ) {
420  // Key is not set or invalid; regenerate
421  $newVal = $this->makePurgeValue( $now, self::HOLDOFF_TTL );
422  $this->cache->add( $timeKey, $newVal, self::CHECK_KEY_TTL );
423  $purge = $this->parsePurgeValue( $newVal );
424  }
425  $purgeValues[] = $purge;
426  }
427  return $purgeValues;
428  }
429 
495  final public function set( $key, $value, $ttl = 0, array $opts = [] ) {
496  $now = $this->getCurrentTime();
497  $lockTSE = isset( $opts['lockTSE'] ) ? $opts['lockTSE'] : self::TSE_NONE;
498  $staleTTL = isset( $opts['staleTTL'] ) ? $opts['staleTTL'] : self::STALE_TTL_NONE;
499  $age = isset( $opts['since'] ) ? max( 0, $now - $opts['since'] ) : 0;
500  $lag = isset( $opts['lag'] ) ? $opts['lag'] : 0;
501 
502  // Do not cache potentially uncommitted data as it might get rolled back
503  if ( !empty( $opts['pending'] ) ) {
504  $this->logger->info( 'Rejected set() for {cachekey} due to pending writes.',
505  [ 'cachekey' => $key ] );
506 
507  return true; // no-op the write for being unsafe
508  }
509 
510  $wrapExtra = []; // additional wrapped value fields
511  // Check if there's a risk of writing stale data after the purge tombstone expired
512  if ( $lag === false || ( $lag + $age ) > self::MAX_READ_LAG ) {
513  // Case A: read lag with "lockTSE"; save but record value as stale
514  if ( $lockTSE >= 0 ) {
515  $ttl = max( 1, (int)$lockTSE ); // set() expects seconds
516  $wrapExtra[self::FLD_FLAGS] = self::FLG_STALE; // mark as stale
517  // Case B: any long-running transaction; ignore this set()
518  } elseif ( $age > self::MAX_READ_LAG ) {
519  $this->logger->info( 'Rejected set() for {cachekey} due to snapshot lag.',
520  [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] );
521 
522  return true; // no-op the write for being unsafe
523  // Case C: high replication lag; lower TTL instead of ignoring all set()s
524  } elseif ( $lag === false || $lag > self::MAX_READ_LAG ) {
525  $ttl = $ttl ? min( $ttl, self::TTL_LAGGED ) : self::TTL_LAGGED;
526  $this->logger->warning( 'Lowered set() TTL for {cachekey} due to replication lag.',
527  [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] );
528  // Case D: medium length request with medium replication lag; ignore this set()
529  } else {
530  $this->logger->info( 'Rejected set() for {cachekey} due to high read lag.',
531  [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] );
532 
533  return true; // no-op the write for being unsafe
534  }
535  }
536 
537  // Wrap that value with time/TTL/version metadata
538  $wrapped = $this->wrap( $value, $ttl, $now ) + $wrapExtra;
539 
540  $func = function ( $cache, $key, $cWrapped ) use ( $wrapped ) {
541  return ( is_string( $cWrapped ) )
542  ? false // key is tombstoned; do nothing
543  : $wrapped;
544  };
545 
546  return $this->cache->merge( self::VALUE_KEY_PREFIX . $key, $func, $ttl + $staleTTL, 1 );
547  }
548 
610  final public function delete( $key, $ttl = self::HOLDOFF_TTL ) {
611  $key = self::VALUE_KEY_PREFIX . $key;
612 
613  if ( $ttl <= 0 ) {
614  // Publish the purge to all datacenters
615  $ok = $this->relayDelete( $key );
616  } else {
617  // Publish the purge to all datacenters
618  $ok = $this->relayPurge( $key, $ttl, self::HOLDOFF_NONE );
619  }
620 
621  return $ok;
622  }
623 
643  final public function getCheckKeyTime( $key ) {
644  return $this->getMultiCheckKeyTime( [ $key ] )[$key];
645  }
646 
708  final public function getMultiCheckKeyTime( array $keys ) {
709  $rawKeys = [];
710  foreach ( $keys as $key ) {
711  $rawKeys[$key] = self::TIME_KEY_PREFIX . $key;
712  }
713 
714  $rawValues = $this->cache->getMulti( $rawKeys );
715  $rawValues += array_fill_keys( $rawKeys, false );
716 
717  $times = [];
718  foreach ( $rawKeys as $key => $rawKey ) {
719  $purge = $this->parsePurgeValue( $rawValues[$rawKey] );
720  if ( $purge !== false ) {
721  $time = $purge[self::FLD_TIME];
722  } else {
723  // Casting assures identical floats for the next getCheckKeyTime() calls
724  $now = (string)$this->getCurrentTime();
725  $this->cache->add(
726  $rawKey,
727  $this->makePurgeValue( $now, self::HOLDOFF_TTL ),
728  self::CHECK_KEY_TTL
729  );
730  $time = (float)$now;
731  }
732 
733  $times[$key] = $time;
734  }
735 
736  return $times;
737  }
738 
773  final public function touchCheckKey( $key, $holdoff = self::HOLDOFF_TTL ) {
774  // Publish the purge to all datacenters
775  return $this->relayPurge( self::TIME_KEY_PREFIX . $key, self::CHECK_KEY_TTL, $holdoff );
776  }
777 
805  final public function resetCheckKey( $key ) {
806  // Publish the purge to all datacenters
807  return $this->relayDelete( self::TIME_KEY_PREFIX . $key );
808  }
809 
1054  final public function getWithSetCallback( $key, $ttl, $callback, array $opts = [] ) {
1055  $pcTTL = isset( $opts['pcTTL'] ) ? $opts['pcTTL'] : self::TTL_UNCACHEABLE;
1056 
1057  // Try the process cache if enabled and the cache callback is not within a cache callback.
1058  // Process cache use in nested callbacks is not lag-safe with regard to HOLDOFF_TTL since
1059  // the in-memory value is further lagged than the shared one since it uses a blind TTL.
1060  if ( $pcTTL >= 0 && $this->callbackDepth == 0 ) {
1061  $group = isset( $opts['pcGroup'] ) ? $opts['pcGroup'] : self::PC_PRIMARY;
1062  $procCache = $this->getProcessCache( $group );
1063  $value = $procCache->get( $key );
1064  } else {
1065  $procCache = false;
1066  $value = false;
1067  }
1068 
1069  if ( $value === false ) {
1070  // Fetch the value over the network
1071  if ( isset( $opts['version'] ) ) {
1072  $version = $opts['version'];
1073  $asOf = null;
1074  $cur = $this->doGetWithSetCallback(
1075  $key,
1076  $ttl,
1077  function ( $oldValue, &$ttl, &$setOpts, $oldAsOf )
1078  use ( $callback, $version ) {
1079  if ( is_array( $oldValue )
1080  && array_key_exists( self::VFLD_DATA, $oldValue )
1081  && array_key_exists( self::VFLD_VERSION, $oldValue )
1082  && $oldValue[self::VFLD_VERSION] === $version
1083  ) {
1084  $oldData = $oldValue[self::VFLD_DATA];
1085  } else {
1086  // VFLD_DATA is not set if an old, unversioned, key is present
1087  $oldData = false;
1088  $oldAsOf = null;
1089  }
1090 
1091  return [
1092  self::VFLD_DATA => $callback( $oldData, $ttl, $setOpts, $oldAsOf ),
1093  self::VFLD_VERSION => $version
1094  ];
1095  },
1096  $opts,
1097  $asOf
1098  );
1099  if ( $cur[self::VFLD_VERSION] === $version ) {
1100  // Value created or existed before with version; use it
1101  $value = $cur[self::VFLD_DATA];
1102  } else {
1103  // Value existed before with a different version; use variant key.
1104  // Reflect purges to $key by requiring that this key value be newer.
1105  $value = $this->doGetWithSetCallback(
1106  $this->makeGlobalKey( 'WANCache-key-variant', md5( $key ), $version ),
1107  $ttl,
1108  $callback,
1109  // Regenerate value if not newer than $key
1110  [ 'version' => null, 'minAsOf' => $asOf ] + $opts
1111  );
1112  }
1113  } else {
1114  $value = $this->doGetWithSetCallback( $key, $ttl, $callback, $opts );
1115  }
1116 
1117  // Update the process cache if enabled
1118  if ( $procCache && $value !== false ) {
1119  $procCache->set( $key, $value, $pcTTL );
1120  }
1121  }
1122 
1123  return $value;
1124  }
1125 
1139  protected function doGetWithSetCallback( $key, $ttl, $callback, array $opts, &$asOf = null ) {
1140  $lowTTL = isset( $opts['lowTTL'] ) ? $opts['lowTTL'] : min( self::LOW_TTL, $ttl );
1141  $lockTSE = isset( $opts['lockTSE'] ) ? $opts['lockTSE'] : self::TSE_NONE;
1142  $staleTTL = isset( $opts['staleTTL'] ) ? $opts['staleTTL'] : self::STALE_TTL_NONE;
1143  $graceTTL = isset( $opts['graceTTL'] ) ? $opts['graceTTL'] : self::GRACE_TTL_NONE;
1144  $checkKeys = isset( $opts['checkKeys'] ) ? $opts['checkKeys'] : [];
1145  $busyValue = isset( $opts['busyValue'] ) ? $opts['busyValue'] : null;
1146  $popWindow = isset( $opts['hotTTR'] ) ? $opts['hotTTR'] : self::HOT_TTR;
1147  $ageNew = isset( $opts['ageNew'] ) ? $opts['ageNew'] : self::AGE_NEW;
1148  $minTime = isset( $opts['minAsOf'] ) ? $opts['minAsOf'] : self::MIN_TIMESTAMP_NONE;
1149  $versioned = isset( $opts['version'] );
1150 
1151  // Get a collection name to describe this class of key
1152  $kClass = $this->determineKeyClass( $key );
1153 
1154  // Get the current key value
1155  $curTTL = null;
1156  $cValue = $this->get( $key, $curTTL, $checkKeys, $asOf ); // current value
1157  $value = $cValue; // return value
1158 
1159  $preCallbackTime = $this->getCurrentTime();
1160  // Determine if a cached value regeneration is needed or desired
1161  if ( $value !== false
1162  && $this->isAliveOrInGracePeriod( $curTTL, $graceTTL )
1163  && $this->isValid( $value, $versioned, $asOf, $minTime )
1164  ) {
1165  $preemptiveRefresh = (
1166  $this->worthRefreshExpiring( $curTTL, $lowTTL ) ||
1167  $this->worthRefreshPopular( $asOf, $ageNew, $popWindow, $preCallbackTime )
1168  );
1169 
1170  if ( !$preemptiveRefresh ) {
1171  $this->stats->increment( "wanobjectcache.$kClass.hit.good" );
1172 
1173  return $value;
1174  } elseif ( $this->asyncHandler ) {
1175  // Update the cache value later, such during post-send of an HTTP request
1176  $func = $this->asyncHandler;
1177  $func( function () use ( $key, $ttl, $callback, $opts, $asOf ) {
1178  $opts['minAsOf'] = INF; // force a refresh
1179  $this->doGetWithSetCallback( $key, $ttl, $callback, $opts, $asOf );
1180  } );
1181  $this->stats->increment( "wanobjectcache.$kClass.hit.refresh" );
1182 
1183  return $value;
1184  }
1185  }
1186 
1187  // A deleted key with a negative TTL left must be tombstoned
1188  $isTombstone = ( $curTTL !== null && $value === false );
1189  if ( $isTombstone && $lockTSE <= 0 ) {
1190  // Use the INTERIM value for tombstoned keys to reduce regeneration load
1191  $lockTSE = self::INTERIM_KEY_TTL;
1192  }
1193  // Assume a key is hot if requested soon after invalidation
1194  $isHot = ( $curTTL !== null && $curTTL <= 0 && abs( $curTTL ) <= $lockTSE );
1195  // Use the mutex if there is no value and a busy fallback is given
1196  $checkBusy = ( $busyValue !== null && $value === false );
1197  // Decide whether a single thread should handle regenerations.
1198  // This avoids stampedes when $checkKeys are bumped and when preemptive
1199  // renegerations take too long. It also reduces regenerations while $key
1200  // is tombstoned. This balances cache freshness with avoiding DB load.
1201  $useMutex = ( $isHot || ( $isTombstone && $lockTSE > 0 ) || $checkBusy );
1202 
1203  $lockAcquired = false;
1204  if ( $useMutex ) {
1205  // Acquire a datacenter-local non-blocking lock
1206  if ( $this->cache->add( self::MUTEX_KEY_PREFIX . $key, 1, self::LOCK_TTL ) ) {
1207  // Lock acquired; this thread should update the key
1208  $lockAcquired = true;
1209  } elseif ( $value !== false && $this->isValid( $value, $versioned, $asOf, $minTime ) ) {
1210  $this->stats->increment( "wanobjectcache.$kClass.hit.stale" );
1211  // If it cannot be acquired; then the stale value can be used
1212  return $value;
1213  } else {
1214  // Use the INTERIM value for tombstoned keys to reduce regeneration load.
1215  // For hot keys, either another thread has the lock or the lock failed;
1216  // use the INTERIM value from the last thread that regenerated it.
1217  $value = $this->getInterimValue( $key, $versioned, $minTime, $asOf );
1218  if ( $value !== false ) {
1219  $this->stats->increment( "wanobjectcache.$kClass.hit.volatile" );
1220 
1221  return $value;
1222  }
1223  // Use the busy fallback value if nothing else
1224  if ( $busyValue !== null ) {
1225  $this->stats->increment( "wanobjectcache.$kClass.miss.busy" );
1226 
1227  return is_callable( $busyValue ) ? $busyValue() : $busyValue;
1228  }
1229  }
1230  }
1231 
1232  if ( !is_callable( $callback ) ) {
1233  throw new InvalidArgumentException( "Invalid cache miss callback provided." );
1234  }
1235 
1236  // Generate the new value from the callback...
1237  $setOpts = [];
1239  try {
1240  $value = call_user_func_array( $callback, [ $cValue, &$ttl, &$setOpts, $asOf ] );
1241  } finally {
1243  }
1244  $valueIsCacheable = ( $value !== false && $ttl >= 0 );
1245 
1246  // When delete() is called, writes are write-holed by the tombstone,
1247  // so use a special INTERIM key to pass the new value around threads.
1248  if ( ( $isTombstone && $lockTSE > 0 ) && $valueIsCacheable ) {
1249  $tempTTL = max( 1, (int)$lockTSE ); // set() expects seconds
1250  $newAsOf = $this->getCurrentTime();
1251  $wrapped = $this->wrap( $value, $tempTTL, $newAsOf );
1252  // Avoid using set() to avoid pointless mcrouter broadcasting
1253  $this->setInterimValue( $key, $wrapped, $tempTTL );
1254  }
1255 
1256  if ( $valueIsCacheable ) {
1257  $setOpts['lockTSE'] = $lockTSE;
1258  $setOpts['staleTTL'] = $staleTTL;
1259  // Use best known "since" timestamp if not provided
1260  $setOpts += [ 'since' => $preCallbackTime ];
1261  // Update the cache; this will fail if the key is tombstoned
1262  $this->set( $key, $value, $ttl, $setOpts );
1263  }
1264 
1265  if ( $lockAcquired ) {
1266  // Avoid using delete() to avoid pointless mcrouter broadcasting
1267  $this->cache->changeTTL( self::MUTEX_KEY_PREFIX . $key, (int)$preCallbackTime - 60 );
1268  }
1269 
1270  $this->stats->increment( "wanobjectcache.$kClass.miss.compute" );
1271 
1272  return $value;
1273  }
1274 
1282  protected function getInterimValue( $key, $versioned, $minTime, &$asOf ) {
1283  if ( !$this->useInterimHoldOffCaching ) {
1284  return false; // disabled
1285  }
1286 
1287  $wrapped = $this->cache->get( self::INTERIM_KEY_PREFIX . $key );
1288  list( $value ) = $this->unwrap( $wrapped, $this->getCurrentTime() );
1289  if ( $value !== false && $this->isValid( $value, $versioned, $asOf, $minTime ) ) {
1290  $asOf = $wrapped[self::FLD_TIME];
1291 
1292  return $value;
1293  }
1294 
1295  return false;
1296  }
1297 
1303  protected function setInterimValue( $key, $wrapped, $tempTTL ) {
1304  $this->cache->merge(
1305  self::INTERIM_KEY_PREFIX . $key,
1306  function () use ( $wrapped ) {
1307  return $wrapped;
1308  },
1309  $tempTTL,
1310  1
1311  );
1312  }
1313 
1380  final public function getMultiWithSetCallback(
1381  ArrayIterator $keyedIds, $ttl, callable $callback, array $opts = []
1382  ) {
1383  $valueKeys = array_keys( $keyedIds->getArrayCopy() );
1384  $checkKeys = isset( $opts['checkKeys'] ) ? $opts['checkKeys'] : [];
1385 
1386  // Load required keys into process cache in one go
1387  $this->warmupCache = $this->getRawKeysForWarmup(
1388  $this->getNonProcessCachedKeys( $valueKeys, $opts ),
1389  $checkKeys
1390  );
1391  $this->warmupKeyMisses = 0;
1392 
1393  // Wrap $callback to match the getWithSetCallback() format while passing $id to $callback
1394  $id = null; // current entity ID
1395  $func = function ( $oldValue, &$ttl, &$setOpts, $oldAsOf ) use ( $callback, &$id ) {
1396  return $callback( $id, $oldValue, $ttl, $setOpts, $oldAsOf );
1397  };
1398 
1399  $values = [];
1400  foreach ( $keyedIds as $key => $id ) { // preserve order
1401  $values[$key] = $this->getWithSetCallback( $key, $ttl, $func, $opts );
1402  }
1403 
1404  $this->warmupCache = [];
1405 
1406  return $values;
1407  }
1408 
1474  final public function getMultiWithUnionSetCallback(
1475  ArrayIterator $keyedIds, $ttl, callable $callback, array $opts = []
1476  ) {
1477  $idsByValueKey = $keyedIds->getArrayCopy();
1478  $valueKeys = array_keys( $idsByValueKey );
1479  $checkKeys = isset( $opts['checkKeys'] ) ? $opts['checkKeys'] : [];
1480  unset( $opts['lockTSE'] ); // incompatible
1481  unset( $opts['busyValue'] ); // incompatible
1482 
1483  // Load required keys into process cache in one go
1484  $keysGet = $this->getNonProcessCachedKeys( $valueKeys, $opts );
1485  $this->warmupCache = $this->getRawKeysForWarmup( $keysGet, $checkKeys );
1486  $this->warmupKeyMisses = 0;
1487 
1488  // IDs of entities known to be in need of regeneration
1489  $idsRegen = [];
1490 
1491  // Find out which keys are missing/deleted/stale
1492  $curTTLs = [];
1493  $asOfs = [];
1494  $curByKey = $this->getMulti( $keysGet, $curTTLs, $checkKeys, $asOfs );
1495  foreach ( $keysGet as $key ) {
1496  if ( !array_key_exists( $key, $curByKey ) || $curTTLs[$key] < 0 ) {
1497  $idsRegen[] = $idsByValueKey[$key];
1498  }
1499  }
1500 
1501  // Run the callback to populate the regeneration value map for all required IDs
1502  $newSetOpts = [];
1503  $newTTLsById = array_fill_keys( $idsRegen, $ttl );
1504  $newValsById = $idsRegen ? $callback( $idsRegen, $newTTLsById, $newSetOpts ) : [];
1505 
1506  // Wrap $callback to match the getWithSetCallback() format while passing $id to $callback
1507  $id = null; // current entity ID
1508  $func = function ( $oldValue, &$ttl, &$setOpts, $oldAsOf )
1509  use ( $callback, &$id, $newValsById, $newTTLsById, $newSetOpts )
1510  {
1511  if ( array_key_exists( $id, $newValsById ) ) {
1512  // Value was already regerated as expected, so use the value in $newValsById
1513  $newValue = $newValsById[$id];
1514  $ttl = $newTTLsById[$id];
1515  $setOpts = $newSetOpts;
1516  } else {
1517  // Pre-emptive/popularity refresh and version mismatch cases are not detected
1518  // above and thus $newValsById has no entry. Run $callback on this single entity.
1519  $ttls = [ $id => $ttl ];
1520  $newValue = $callback( [ $id ], $ttls, $setOpts )[$id];
1521  $ttl = $ttls[$id];
1522  }
1523 
1524  return $newValue;
1525  };
1526 
1527  // Run the cache-aside logic using warmupCache instead of persistent cache queries
1528  $values = [];
1529  foreach ( $idsByValueKey as $key => $id ) { // preserve order
1530  $values[$key] = $this->getWithSetCallback( $key, $ttl, $func, $opts );
1531  }
1532 
1533  $this->warmupCache = [];
1534 
1535  return $values;
1536  }
1537 
1550  final public function reap( $key, $purgeTimestamp, &$isStale = false ) {
1551  $minAsOf = $purgeTimestamp + self::HOLDOFF_TTL;
1552  $wrapped = $this->cache->get( self::VALUE_KEY_PREFIX . $key );
1553  if ( is_array( $wrapped ) && $wrapped[self::FLD_TIME] < $minAsOf ) {
1554  $isStale = true;
1555  $this->logger->warning( "Reaping stale value key '$key'." );
1556  $ttlReap = self::HOLDOFF_TTL; // avoids races with tombstone creation
1557  $ok = $this->cache->changeTTL( self::VALUE_KEY_PREFIX . $key, $ttlReap );
1558  if ( !$ok ) {
1559  $this->logger->error( "Could not complete reap of key '$key'." );
1560  }
1561 
1562  return $ok;
1563  }
1564 
1565  $isStale = false;
1566 
1567  return true;
1568  }
1569 
1579  final public function reapCheckKey( $key, $purgeTimestamp, &$isStale = false ) {
1580  $purge = $this->parsePurgeValue( $this->cache->get( self::TIME_KEY_PREFIX . $key ) );
1581  if ( $purge && $purge[self::FLD_TIME] < $purgeTimestamp ) {
1582  $isStale = true;
1583  $this->logger->warning( "Reaping stale check key '$key'." );
1584  $ok = $this->cache->changeTTL( self::TIME_KEY_PREFIX . $key, self::TTL_SECOND );
1585  if ( !$ok ) {
1586  $this->logger->error( "Could not complete reap of check key '$key'." );
1587  }
1588 
1589  return $ok;
1590  }
1591 
1592  $isStale = false;
1593 
1594  return false;
1595  }
1596 
1604  public function makeKey( $class, $component = null ) {
1605  return call_user_func_array( [ $this->cache, __FUNCTION__ ], func_get_args() );
1606  }
1607 
1615  public function makeGlobalKey( $class, $component = null ) {
1616  return call_user_func_array( [ $this->cache, __FUNCTION__ ], func_get_args() );
1617  }
1618 
1625  final public function makeMultiKeys( array $entities, callable $keyFunc ) {
1626  $map = [];
1627  foreach ( $entities as $entity ) {
1628  $map[$keyFunc( $entity, $this )] = $entity;
1629  }
1630 
1631  return new ArrayIterator( $map );
1632  }
1633 
1638  final public function getLastError() {
1639  if ( $this->lastRelayError ) {
1640  // If the cache and the relayer failed, focus on the latter.
1641  // An update not making it to the relayer means it won't show up
1642  // in other DCs (nor will consistent re-hashing see up-to-date values).
1643  // On the other hand, if just the cache update failed, then it should
1644  // eventually be applied by the relayer.
1645  return $this->lastRelayError;
1646  }
1647 
1648  $code = $this->cache->getLastError();
1649  switch ( $code ) {
1650  case BagOStuff::ERR_NONE:
1651  return self::ERR_NONE;
1653  return self::ERR_NO_RESPONSE;
1655  return self::ERR_UNREACHABLE;
1656  default:
1657  return self::ERR_UNEXPECTED;
1658  }
1659  }
1660 
1664  final public function clearLastError() {
1665  $this->cache->clearLastError();
1666  $this->lastRelayError = self::ERR_NONE;
1667  }
1668 
1674  public function clearProcessCache() {
1675  $this->processCaches = [];
1676  }
1677 
1698  final public function useInterimHoldOffCaching( $enabled ) {
1699  $this->useInterimHoldOffCaching = $enabled;
1700  }
1701 
1707  public function getQoS( $flag ) {
1708  return $this->cache->getQoS( $flag );
1709  }
1710 
1774  public function adaptiveTTL( $mtime, $maxTTL, $minTTL = 30, $factor = 0.2 ) {
1775  if ( is_float( $mtime ) || ctype_digit( $mtime ) ) {
1776  $mtime = (int)$mtime; // handle fractional seconds and string integers
1777  }
1778 
1779  if ( !is_int( $mtime ) || $mtime <= 0 ) {
1780  return $minTTL; // no last-modified time provided
1781  }
1782 
1783  $age = $this->getCurrentTime() - $mtime;
1784 
1785  return (int)min( $maxTTL, max( $minTTL, $factor * $age ) );
1786  }
1787 
1792  final public function getWarmupKeyMisses() {
1793  return $this->warmupKeyMisses;
1794  }
1795 
1806  protected function relayPurge( $key, $ttl, $holdoff ) {
1807  if ( $this->mcrouterAware ) {
1808  // See https://github.com/facebook/mcrouter/wiki/Multi-cluster-broadcast-setup
1809  // Wildcards select all matching routes, e.g. the WAN cluster on all DCs
1810  $ok = $this->cache->set(
1811  "/*/{$this->cluster}/{$key}",
1812  $this->makePurgeValue( $this->getCurrentTime(), self::HOLDOFF_NONE ),
1813  $ttl
1814  );
1815  } elseif ( $this->purgeRelayer instanceof EventRelayerNull ) {
1816  // This handles the mcrouter and the single-DC case
1817  $ok = $this->cache->set(
1818  $key,
1819  $this->makePurgeValue( $this->getCurrentTime(), self::HOLDOFF_NONE ),
1820  $ttl
1821  );
1822  } else {
1823  $event = $this->cache->modifySimpleRelayEvent( [
1824  'cmd' => 'set',
1825  'key' => $key,
1826  'val' => 'PURGED:$UNIXTIME$:' . (int)$holdoff,
1827  'ttl' => max( $ttl, self::TTL_SECOND ),
1828  'sbt' => true, // substitute $UNIXTIME$ with actual microtime
1829  ] );
1830 
1831  $ok = $this->purgeRelayer->notify( $this->purgeChannel, $event );
1832  if ( !$ok ) {
1833  $this->lastRelayError = self::ERR_RELAY;
1834  }
1835  }
1836 
1837  return $ok;
1838  }
1839 
1846  protected function relayDelete( $key ) {
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->delete( "/*/{$this->cluster}/{$key}" );
1851  } elseif ( $this->purgeRelayer instanceof EventRelayerNull ) {
1852  // Some other proxy handles broadcasting or there is only one datacenter
1853  $ok = $this->cache->delete( $key );
1854  } else {
1855  $event = $this->cache->modifySimpleRelayEvent( [
1856  'cmd' => 'delete',
1857  'key' => $key,
1858  ] );
1859 
1860  $ok = $this->purgeRelayer->notify( $this->purgeChannel, $event );
1861  if ( !$ok ) {
1862  $this->lastRelayError = self::ERR_RELAY;
1863  }
1864  }
1865 
1866  return $ok;
1867  }
1868 
1882  protected function isAliveOrInGracePeriod( $curTTL, $graceTTL ) {
1883  if ( $curTTL > 0 ) {
1884  return true;
1885  } elseif ( $graceTTL <= 0 ) {
1886  return false;
1887  }
1888 
1889  $ageStale = abs( $curTTL ); // seconds of staleness
1890  $curGTTL = ( $graceTTL - $ageStale ); // current grace-time-to-live
1891  if ( $curGTTL <= 0 ) {
1892  return false; // already out of grace period
1893  }
1894 
1895  // Chance of using a stale value is the complement of the chance of refreshing it
1896  return !$this->worthRefreshExpiring( $curGTTL, $graceTTL );
1897  }
1898 
1912  protected function worthRefreshExpiring( $curTTL, $lowTTL ) {
1913  if ( $lowTTL <= 0 ) {
1914  return false;
1915  } elseif ( $curTTL >= $lowTTL ) {
1916  return false;
1917  } elseif ( $curTTL <= 0 ) {
1918  return false;
1919  }
1920 
1921  $chance = ( 1 - $curTTL / $lowTTL );
1922 
1923  return mt_rand( 1, 1e9 ) <= 1e9 * $chance;
1924  }
1925 
1941  protected function worthRefreshPopular( $asOf, $ageNew, $timeTillRefresh, $now ) {
1942  if ( $ageNew < 0 || $timeTillRefresh <= 0 ) {
1943  return false;
1944  }
1945 
1946  $age = $now - $asOf;
1947  $timeOld = $age - $ageNew;
1948  if ( $timeOld <= 0 ) {
1949  return false;
1950  }
1951 
1952  // Lifecycle is: new, ramp-up refresh chance, full refresh chance.
1953  // Note that the "expected # of refreshes" for the ramp-up time range is half of what it
1954  // would be if P(refresh) was at its full value during that time range.
1955  $refreshWindowSec = max( $timeTillRefresh - $ageNew - self::RAMPUP_TTL / 2, 1 );
1956  // P(refresh) * (# hits in $refreshWindowSec) = (expected # of refreshes)
1957  // P(refresh) * ($refreshWindowSec * $popularHitsPerSec) = 1
1958  // P(refresh) = 1/($refreshWindowSec * $popularHitsPerSec)
1959  $chance = 1 / ( self::HIT_RATE_HIGH * $refreshWindowSec );
1960 
1961  // Ramp up $chance from 0 to its nominal value over RAMPUP_TTL seconds to avoid stampedes
1962  $chance *= ( $timeOld <= self::RAMPUP_TTL ) ? $timeOld / self::RAMPUP_TTL : 1;
1963 
1964  return mt_rand( 1, 1e9 ) <= 1e9 * $chance;
1965  }
1966 
1976  protected function isValid( $value, $versioned, $asOf, $minTime ) {
1977  if ( $versioned && !isset( $value[self::VFLD_VERSION] ) ) {
1978  return false;
1979  } elseif ( $minTime > 0 && $asOf < $minTime ) {
1980  return false;
1981  }
1982 
1983  return true;
1984  }
1985 
1994  protected function wrap( $value, $ttl, $now ) {
1995  return [
1996  self::FLD_VERSION => self::VERSION,
1997  self::FLD_VALUE => $value,
1998  self::FLD_TTL => $ttl,
1999  self::FLD_TIME => $now
2000  ];
2001  }
2002 
2010  protected function unwrap( $wrapped, $now ) {
2011  // Check if the value is a tombstone
2012  $purge = $this->parsePurgeValue( $wrapped );
2013  if ( $purge !== false ) {
2014  // Purged values should always have a negative current $ttl
2015  $curTTL = min( $purge[self::FLD_TIME] - $now, self::TINY_NEGATIVE );
2016  return [ false, $curTTL ];
2017  }
2018 
2019  if ( !is_array( $wrapped ) // not found
2020  || !isset( $wrapped[self::FLD_VERSION] ) // wrong format
2021  || $wrapped[self::FLD_VERSION] !== self::VERSION // wrong version
2022  ) {
2023  return [ false, null ];
2024  }
2025 
2026  $flags = isset( $wrapped[self::FLD_FLAGS] ) ? $wrapped[self::FLD_FLAGS] : 0;
2027  if ( ( $flags & self::FLG_STALE ) == self::FLG_STALE ) {
2028  // Treat as expired, with the cache time as the expiration
2029  $age = $now - $wrapped[self::FLD_TIME];
2030  $curTTL = min( -$age, self::TINY_NEGATIVE );
2031  } elseif ( $wrapped[self::FLD_TTL] > 0 ) {
2032  // Get the approximate time left on the key
2033  $age = $now - $wrapped[self::FLD_TIME];
2034  $curTTL = max( $wrapped[self::FLD_TTL] - $age, 0.0 );
2035  } else {
2036  // Key had no TTL, so the time left is unbounded
2037  $curTTL = INF;
2038  }
2039 
2040  return [ $wrapped[self::FLD_VALUE], $curTTL ];
2041  }
2042 
2048  protected static function prefixCacheKeys( array $keys, $prefix ) {
2049  $res = [];
2050  foreach ( $keys as $key ) {
2051  $res[] = $prefix . $key;
2052  }
2053 
2054  return $res;
2055  }
2056 
2061  protected function determineKeyClass( $key ) {
2062  $parts = explode( ':', $key );
2063 
2064  return isset( $parts[1] ) ? $parts[1] : $parts[0]; // sanity
2065  }
2066 
2071  protected function getCurrentTime() {
2072  return microtime( true );
2073  }
2074 
2080  protected function parsePurgeValue( $value ) {
2081  if ( !is_string( $value ) ) {
2082  return false;
2083  }
2084  $segments = explode( ':', $value, 3 );
2085  if ( !isset( $segments[0] ) || !isset( $segments[1] )
2086  || "{$segments[0]}:" !== self::PURGE_VAL_PREFIX
2087  ) {
2088  return false;
2089  }
2090  if ( !isset( $segments[2] ) ) {
2091  // Back-compat with old purge values without holdoff
2092  $segments[2] = self::HOLDOFF_TTL;
2093  }
2094  return [
2095  self::FLD_TIME => (float)$segments[1],
2096  self::FLD_HOLDOFF => (int)$segments[2],
2097  ];
2098  }
2099 
2105  protected function makePurgeValue( $timestamp, $holdoff ) {
2106  return self::PURGE_VAL_PREFIX . (float)$timestamp . ':' . (int)$holdoff;
2107  }
2108 
2113  protected function getProcessCache( $group ) {
2114  if ( !isset( $this->processCaches[$group] ) ) {
2115  list( , $n ) = explode( ':', $group );
2116  $this->processCaches[$group] = new HashBagOStuff( [ 'maxKeys' => (int)$n ] );
2117  }
2118 
2119  return $this->processCaches[$group];
2120  }
2121 
2127  private function getNonProcessCachedKeys( array $keys, array $opts ) {
2128  $keysFound = [];
2129  if ( isset( $opts['pcTTL'] ) && $opts['pcTTL'] > 0 && $this->callbackDepth == 0 ) {
2130  $pcGroup = isset( $opts['pcGroup'] ) ? $opts['pcGroup'] : self::PC_PRIMARY;
2131  $procCache = $this->getProcessCache( $pcGroup );
2132  foreach ( $keys as $key ) {
2133  if ( $procCache->get( $key ) !== false ) {
2134  $keysFound[] = $key;
2135  }
2136  }
2137  }
2138 
2139  return array_diff( $keys, $keysFound );
2140  }
2141 
2147  private function getRawKeysForWarmup( array $keys, array $checkKeys ) {
2148  if ( !$keys ) {
2149  return [];
2150  }
2151 
2152  $keysWarmUp = [];
2153  // Get all the value keys to fetch...
2154  foreach ( $keys as $key ) {
2155  $keysWarmUp[] = self::VALUE_KEY_PREFIX . $key;
2156  }
2157  // Get all the check keys to fetch...
2158  foreach ( $checkKeys as $i => $checkKeyOrKeys ) {
2159  if ( is_int( $i ) ) {
2160  // Single check key that applies to all value keys
2161  $keysWarmUp[] = self::TIME_KEY_PREFIX . $checkKeyOrKeys;
2162  } else {
2163  // List of check keys that apply to value key $i
2164  $keysWarmUp = array_merge(
2165  $keysWarmUp,
2166  self::prefixCacheKeys( $checkKeyOrKeys, self::TIME_KEY_PREFIX )
2167  );
2168  }
2169  }
2170 
2171  $warmupCache = $this->cache->getMulti( $keysWarmUp );
2172  $warmupCache += array_fill_keys( $keysWarmUp, false );
2173 
2174  return $warmupCache;
2175  }
2176 }
WANObjectCache\ERR_UNREACHABLE
const ERR_UNREACHABLE
Definition: WANObjectCache.php:181
WANObjectCache\getQoS
getQoS( $flag)
Definition: WANObjectCache.php:1707
WANObjectCache\relayDelete
relayDelete( $key)
Do the actual async bus delete of a key.
Definition: WANObjectCache.php:1846
WANObjectCache\getNonProcessCachedKeys
getNonProcessCachedKeys(array $keys, array $opts)
Definition: WANObjectCache.php:2127
WANObjectCache\VALUE_KEY_PREFIX
const VALUE_KEY_PREFIX
Definition: WANObjectCache.php:185
WANObjectCache\TTL_UNCACHEABLE
const TTL_UNCACHEABLE
Idiom for getWithSetCallback() callbacks to avoid calling set()
Definition: WANObjectCache.php:148
WANObjectCache\makePurgeValue
makePurgeValue( $timestamp, $holdoff)
Definition: WANObjectCache.php:2105
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:117
BagOStuff\ERR_UNREACHABLE
const ERR_UNREACHABLE
Definition: BagOStuff.php:79
HashBagOStuff
Simple store for keeping values in an associative array for the current process.
Definition: HashBagOStuff.php:31
EmptyBagOStuff
A BagOStuff object with no objects in it.
Definition: EmptyBagOStuff.php:29
WANObjectCache\$cache
BagOStuff $cache
The local datacenter cache.
Definition: WANObjectCache.php:89
WANObjectCache\determineKeyClass
determineKeyClass( $key)
Definition: WANObjectCache.php:2061
WANObjectCache\MAX_READ_LAG
const MAX_READ_LAG
Max replication+snapshot lag before applying TTL_LAGGED or disallowing set()
Definition: WANObjectCache.php:124
captcha-old.count
count
Definition: captcha-old.py:249
WANObjectCache\$purgeChannel
string $purgeChannel
Purge channel name.
Definition: WANObjectCache.php:93
WANObjectCache\FLD_VERSION
const FLD_VERSION
Definition: WANObjectCache.php:169
WANObjectCache\FLD_VALUE
const FLD_VALUE
Definition: WANObjectCache.php:170
WANObjectCache\MAX_COMMIT_DELAY
const MAX_COMMIT_DELAY
Max time expected to pass between delete() and DB commit finishing.
Definition: WANObjectCache.php:122
WANObjectCache\isValid
isValid( $value, $versioned, $asOf, $minTime)
Check whether $value is appropriately versioned and not older than $minTime (if set)
Definition: WANObjectCache.php:1976
$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! 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! 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:1985
WANObjectCache\getInterimValue
getInterimValue( $key, $versioned, $minTime, &$asOf)
Definition: WANObjectCache.php:1282
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
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:1625
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:1380
$params
$params
Definition: styleTest.css.php:40
BagOStuff
interface is intended to be more or less compatible with the PHP memcached client.
Definition: BagOStuff.php:47
WANObjectCache\getMultiWithUnionSetCallback
getMultiWithUnionSetCallback(ArrayIterator $keyedIds, $ttl, callable $callback, array $opts=[])
Method to fetch/regenerate multiple cache keys at once.
Definition: WANObjectCache.php:1474
WANObjectCache\$processCaches
HashBagOStuff[] $processCaches
Map of group PHP instance caches.
Definition: WANObjectCache.php:91
$res
$res
Definition: database.txt:21
WANObjectCache\VFLD_VERSION
const VFLD_VERSION
Definition: WANObjectCache.php:193
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:152
BagOStuff\ERR_NONE
const ERR_NONE
Possible values for getLastError()
Definition: BagOStuff.php:77
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:1579
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:643
WANObjectCache\$logger
LoggerInterface $logger
Definition: WANObjectCache.php:103
WANObjectCache\VFLD_DATA
const VFLD_DATA
Definition: WANObjectCache.php:192
WANObjectCache\$purgeRelayer
EventRelayer $purgeRelayer
Bus that handles purge broadcasts.
Definition: WANObjectCache.php:95
WANObjectCache\$warmupKeyMisses
int $warmupKeyMisses
Key fetched.
Definition: WANObjectCache.php:119
WANObjectCache\newEmpty
static newEmpty()
Get an instance that wraps EmptyBagOStuff.
Definition: WANObjectCache.php:253
WANObjectCache\ERR_RELAY
const ERR_RELAY
Definition: WANObjectCache.php:183
WANObjectCache\TIME_KEY_PREFIX
const TIME_KEY_PREFIX
Definition: WANObjectCache.php:187
WANObjectCache\LOW_TTL
const LOW_TTL
Default remaining TTL at which to consider pre-emptive regeneration.
Definition: WANObjectCache.php:136
WANObjectCache\FLD_TIME
const FLD_TIME
Definition: WANObjectCache.php:172
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:1674
WANObjectCache\clearLastError
clearLastError()
Clear the "last error" registry.
Definition: WANObjectCache.php:1664
WANObjectCache\getCurrentTime
getCurrentTime()
Definition: WANObjectCache.php:2071
WANObjectCache\getWithSetCallback
getWithSetCallback( $key, $ttl, $callback, array $opts=[])
Method to fetch/regenerate cache keys.
Definition: WANObjectCache.php:1054
WANObjectCache\INTERIM_KEY_TTL
const INTERIM_KEY_TTL
Seconds to keep interim value keys for tombstoned keys around.
Definition: WANObjectCache.php:131
WANObjectCache\prefixCacheKeys
static prefixCacheKeys(array $keys, $prefix)
Definition: WANObjectCache.php:2048
WANObjectCache\unwrap
unwrap( $wrapped, $now)
Do not use this method outside WANObjectCache.
Definition: WANObjectCache.php:2010
WANObjectCache\MIN_TIMESTAMP_NONE
const MIN_TIMESTAMP_NONE
Idiom for getWithSetCallback() for "no minimum required as-of timestamp".
Definition: WANObjectCache.php:161
WANObjectCache\getLastError
getLastError()
Get the "last error" registered; clearLastError() should be called manually.
Definition: WANObjectCache.php:1638
WANObjectCache\INTERIM_KEY_PREFIX
const INTERIM_KEY_PREFIX
Definition: WANObjectCache.php:186
WANObjectCache\__construct
__construct(array $params)
Definition: WANObjectCache.php:224
WANObjectCache\useInterimHoldOffCaching
useInterimHoldOffCaching( $enabled)
Enable or disable the use of brief caching for tombstoned keys.
Definition: WANObjectCache.php:1698
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:1550
WANObjectCache\touchCheckKey
touchCheckKey( $key, $holdoff=self::HOLDOFF_TTL)
Purge a "check" key from all datacenters, invalidating keys that use it.
Definition: WANObjectCache.php:773
WANObjectCache\DEFAULT_PURGE_CHANNEL
const DEFAULT_PURGE_CHANNEL
Definition: WANObjectCache.php:197
WANObjectCache\ERR_NONE
const ERR_NONE
Definition: WANObjectCache.php:179
$time
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1795
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:1774
WANObjectCache\relayPurge
relayPurge( $key, $ttl, $holdoff)
Do the actual async bus purge of a key.
Definition: WANObjectCache.php:1806
WANObjectCache\HOLDOFF_TTL
const HOLDOFF_TTL
Seconds to tombstone keys on delete()
Definition: WANObjectCache.php:126
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:708
WANObjectCache\worthRefreshPopular
worthRefreshPopular( $asOf, $ageNew, $timeTillRefresh, $now)
Check if a key is due for randomized regeneration due to its popularity.
Definition: WANObjectCache.php:1941
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:1882
WANObjectCache\ERR_NO_RESPONSE
const ERR_NO_RESPONSE
Definition: WANObjectCache.php:180
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:145
WANObjectCache\wrap
wrap( $value, $ttl, $now)
Do not use this method outside WANObjectCache.
Definition: WANObjectCache.php:1994
WANObjectCache\$useInterimHoldOffCaching
bool $useInterimHoldOffCaching
Whether to use "interim" caching while keys are tombstoned.
Definition: WANObjectCache.php:107
$value
$value
Definition: styleTest.css.php:45
WANObjectCache\MUTEX_KEY_PREFIX
const MUTEX_KEY_PREFIX
Definition: WANObjectCache.php:188
WANObjectCache\PC_PRIMARY
const PC_PRIMARY
Definition: WANObjectCache.php:195
WANObjectCache\makeGlobalKey
makeGlobalKey( $class, $component=null)
Definition: WANObjectCache.php:1615
WANObjectCache\$cluster
string $cluster
Cache cluster name for mcrouter use.
Definition: WANObjectCache.php:101
WANObjectCache\worthRefreshExpiring
worthRefreshExpiring( $curTTL, $lowTTL)
Check if a key is nearing expiration and thus due for randomized regeneration.
Definition: WANObjectCache.php:1912
WANObjectCache
Multi-datacenter aware caching interface.
Definition: WANObjectCache.php:87
WANObjectCache\VERSION
const VERSION
Cache format version number.
Definition: WANObjectCache.php:167
WANObjectCache\getProcessCache
getProcessCache( $group)
Definition: WANObjectCache.php:2113
WANObjectCache\makeKey
makeKey( $class, $component=null)
Definition: WANObjectCache.php:1604
WANObjectCache\getRawKeysForWarmup
getRawKeysForWarmup(array $keys, array $checkKeys)
Definition: WANObjectCache.php:2147
WANObjectCache\getMulti
getMulti(array $keys, &$curTTLs=[], array $checkKeys=[], array &$asOfs=[])
Fetch the value of several keys from cache.
Definition: WANObjectCache.php:322
WANObjectCache\doGetWithSetCallback
doGetWithSetCallback( $key, $ttl, $callback, array $opts, &$asOf=null)
Do the actual I/O for getWithSetCallback() when needed.
Definition: WANObjectCache.php:1139
WANObjectCache\resetCheckKey
resetCheckKey( $key)
Delete a "check" key from all datacenters, invalidating keys that use it.
Definition: WANObjectCache.php:805
WANObjectCache\TSE_NONE
const TSE_NONE
Idiom for getWithSetCallback() callbacks to 'lockTSE' logic.
Definition: WANObjectCache.php:150
WANObjectCache\parsePurgeValue
parsePurgeValue( $value)
Definition: WANObjectCache.php:2080
WANObjectCache\HOT_TTR
const HOT_TTR
The time length of the "popularity" refresh window for hot keys.
Definition: WANObjectCache.php:141
WANObjectCache\LOCK_TTL
const LOCK_TTL
Seconds to keep lock keys around.
Definition: WANObjectCache.php:134
$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:783
WANObjectCache\PURGE_VAL_PREFIX
const PURGE_VAL_PREFIX
Definition: WANObjectCache.php:190
WANObjectCache\$asyncHandler
callable null $asyncHandler
Function that takes a WAN cache callback and runs it later.
Definition: WANObjectCache.php:109
WANObjectCache\getWarmupKeyMisses
getWarmupKeyMisses()
Definition: WANObjectCache.php:1792
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:97
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:112
WANObjectCache\HOLDOFF_NONE
const HOLDOFF_NONE
Idiom for delete() for "no hold-off".
Definition: WANObjectCache.php:154
WANObjectCache\$callbackDepth
int $callbackDepth
Callback stack depth for getWithSetCallback()
Definition: WANObjectCache.php:115
WANObjectCache\FLD_FLAGS
const FLD_FLAGS
Definition: WANObjectCache.php:173
WANObjectCache\AGE_NEW
const AGE_NEW
Never consider performing "popularity" refreshes until a key reaches this age.
Definition: WANObjectCache.php:139
WANObjectCache\setLogger
setLogger(LoggerInterface $logger)
Definition: WANObjectCache.php:244
WANObjectCache\setInterimValue
setInterimValue( $key, $wrapped, $tempTTL)
Definition: WANObjectCache.php:1303
WANObjectCache\TINY_NEGATIVE
const TINY_NEGATIVE
Tiny negative float to use when CTL comes up >= 0 due to clock skew.
Definition: WANObjectCache.php:164
WANObjectCache\CHECK_KEY_TTL
const CHECK_KEY_TTL
Seconds to keep dependency purge keys around.
Definition: WANObjectCache.php:129
WANObjectCache\GRACE_TTL_NONE
const GRACE_TTL_NONE
Idiom for set()/getWithSetCallback() for "no post-expired grace period".
Definition: WANObjectCache.php:158
EventRelayer
Base class for reliable event relays.
Definition: EventRelayer.php:27
WANObjectCache\FLD_HOLDOFF
const FLD_HOLDOFF
Definition: WANObjectCache.php:174
BagOStuff\ERR_NO_RESPONSE
const ERR_NO_RESPONSE
Definition: BagOStuff.php:78
WANObjectCache\ERR_UNEXPECTED
const ERR_UNEXPECTED
Definition: WANObjectCache.php:182
WANObjectCache\FLD_TTL
const FLD_TTL
Definition: WANObjectCache.php:171
WANObjectCache\HIT_RATE_HIGH
const HIT_RATE_HIGH
Hits/second for a refresh to be expected within the "popularity" window.
Definition: WANObjectCache.php:143
WANObjectCache\STALE_TTL_NONE
const STALE_TTL_NONE
Idiom for set()/getWithSetCallback() for "do not augment the storage medium TTL".
Definition: WANObjectCache.php:156
WANObjectCache\$stats
StatsdDataFactoryInterface $stats
Definition: WANObjectCache.php:105
array
the array() calling protocol came about after MediaWiki 1.4rc1.
WANObjectCache\processCheckKeys
processCheckKeys(array $timeKeys, array $wrappedValues, $now)
Definition: WANObjectCache.php:413
WANObjectCache\$region
string $region
Physical region for mcrouter use.
Definition: WANObjectCache.php:99