MediaWiki  master
MediumSpecificBagOStuff.php
Go to the documentation of this file.
1 <?php
24 use Wikimedia\WaitConditionLoop;
25 
34 abstract class MediumSpecificBagOStuff extends BagOStuff {
36  protected $locks = [];
38  protected $lastError = self::ERR_NONE;
40  protected $keyspace = 'local';
42  protected $syncTimeout;
44  protected $segmentationSize;
47 
49  private $duplicateKeyLookups = [];
51  private $reportDupes = false;
53  private $dupeTrackScheduled = false;
54 
56  protected $busyCallbacks = [];
57 
59  protected $preparedValues = [];
60 
62  const SEGMENT_COMPONENT = 'segment';
63 
83  public function __construct( array $params = [] ) {
84  parent::__construct( $params );
85 
86  if ( isset( $params['keyspace'] ) ) {
87  $this->keyspace = $params['keyspace'];
88  }
89 
90  if ( !empty( $params['reportDupes'] ) && is_callable( $this->asyncHandler ) ) {
91  $this->reportDupes = true;
92  }
93 
94  $this->syncTimeout = $params['syncTimeout'] ?? 3;
95  $this->segmentationSize = $params['segmentationSize'] ?? 8388608; // 8MiB
96  $this->segmentedValueMaxSize = $params['segmentedValueMaxSize'] ?? 67108864; // 64MiB
97  }
98 
112  public function get( $key, $flags = 0 ) {
113  $this->trackDuplicateKeys( $key );
114 
115  return $this->resolveSegments( $key, $this->doGet( $key, $flags ) );
116  }
117 
122  private function trackDuplicateKeys( $key ) {
123  if ( !$this->reportDupes ) {
124  return;
125  }
126 
127  if ( !isset( $this->duplicateKeyLookups[$key] ) ) {
128  // Track that we have seen this key. This N-1 counting style allows
129  // easy filtering with array_filter() later.
130  $this->duplicateKeyLookups[$key] = 0;
131  } else {
132  $this->duplicateKeyLookups[$key] += 1;
133 
134  if ( $this->dupeTrackScheduled === false ) {
135  $this->dupeTrackScheduled = true;
136  // Schedule a callback that logs keys processed more than once by get().
137  call_user_func( $this->asyncHandler, function () {
138  $dups = array_filter( $this->duplicateKeyLookups );
139  foreach ( $dups as $key => $count ) {
140  $this->logger->warning(
141  'Duplicate get(): "{key}" fetched {count} times',
142  // Count is N-1 of the actual lookup count
143  [ 'key' => $key, 'count' => $count + 1, ]
144  );
145  }
146  } );
147  }
148  }
149  }
150 
157  abstract protected function doGet( $key, $flags = 0, &$casToken = null );
158 
168  public function set( $key, $value, $exptime = 0, $flags = 0 ) {
169  list( $entry, $usable ) = $this->makeValueOrSegmentList( $key, $value, $exptime, $flags );
170  // Only when all segments (if any) are stored should the main key be changed
171  return $usable ? $this->doSet( $key, $entry, $exptime, $flags ) : false;
172  }
173 
183  abstract protected function doSet( $key, $value, $exptime = 0, $flags = 0 );
184 
196  public function delete( $key, $flags = 0 ) {
197  if ( !$this->fieldHasFlags( $flags, self::WRITE_PRUNE_SEGMENTS ) ) {
198  return $this->doDelete( $key, $flags );
199  }
200 
201  $mainValue = $this->doGet( $key, self::READ_LATEST );
202  if ( !$this->doDelete( $key, $flags ) ) {
203  return false;
204  }
205 
206  if ( !SerializedValueContainer::isSegmented( $mainValue ) ) {
207  return true; // no segments to delete
208  }
209 
210  $orderedKeys = array_map(
211  function ( $segmentHash ) use ( $key ) {
212  return $this->makeGlobalKey( self::SEGMENT_COMPONENT, $key, $segmentHash );
213  },
215  );
216 
217  return $this->deleteMulti( $orderedKeys, $flags & ~self::WRITE_PRUNE_SEGMENTS );
218  }
219 
227  abstract protected function doDelete( $key, $flags = 0 );
228 
229  public function add( $key, $value, $exptime = 0, $flags = 0 ) {
230  list( $entry, $usable ) = $this->makeValueOrSegmentList( $key, $value, $exptime, $flags );
231  // Only when all segments (if any) are stored should the main key be changed
232  return $usable ? $this->doAdd( $key, $entry, $exptime, $flags ) : false;
233  }
234 
244  abstract protected function doAdd( $key, $value, $exptime = 0, $flags = 0 );
245 
262  public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
263  return $this->mergeViaCas( $key, $callback, $exptime, $attempts, $flags );
264  }
265 
275  final protected function mergeViaCas( $key, callable $callback, $exptime, $attempts, $flags ) {
276  $attemptsLeft = $attempts;
277  do {
278  $token = null; // passed by reference
279  // Get the old value and CAS token from cache
280  $this->clearLastError();
281  $currentValue = $this->resolveSegments(
282  $key,
283  $this->doGet( $key, $flags, $token )
284  );
285  if ( $this->getLastError() ) {
286  // Don't spam slow retries due to network problems (retry only on races)
287  $this->logger->warning(
288  __METHOD__ . ' failed due to read I/O error on get() for {key}.',
289  [ 'key' => $key ]
290  );
291  $success = false;
292  break;
293  }
294 
295  // Derive the new value from the old value
296  $value = call_user_func( $callback, $this, $key, $currentValue, $exptime );
297  $keyWasNonexistant = ( $currentValue === false );
298  $valueMatchesOldValue = ( $value === $currentValue );
299  unset( $currentValue ); // free RAM in case the value is large
300 
301  $this->clearLastError();
302  if ( $value === false || $exptime < 0 ) {
303  $success = true; // do nothing
304  } elseif ( $valueMatchesOldValue && $attemptsLeft !== $attempts ) {
305  $success = true; // recently set by another thread to the same value
306  } elseif ( $keyWasNonexistant ) {
307  // Try to create the key, failing if it gets created in the meantime
308  $success = $this->add( $key, $value, $exptime, $flags );
309  } else {
310  // Try to update the key, failing if it gets changed in the meantime
311  $success = $this->cas( $token, $key, $value, $exptime, $flags );
312  }
313  if ( $this->getLastError() ) {
314  // Don't spam slow retries due to network problems (retry only on races)
315  $this->logger->warning(
316  __METHOD__ . ' failed due to write I/O error for {key}.',
317  [ 'key' => $key ]
318  );
319  $success = false;
320  break;
321  }
322 
323  } while ( !$success && --$attemptsLeft );
324 
325  return $success;
326  }
327 
338  protected function cas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
339  if ( $casToken === null ) {
340  $this->logger->warning(
341  __METHOD__ . ' got empty CAS token for {key}.',
342  [ 'key' => $key ]
343  );
344 
345  return false; // caller may have meant to use add()?
346  }
347 
348  list( $entry, $usable ) = $this->makeValueOrSegmentList( $key, $value, $exptime, $flags );
349  // Only when all segments (if any) are stored should the main key be changed
350  return $usable ? $this->doCas( $casToken, $key, $entry, $exptime, $flags ) : false;
351  }
352 
363  protected function doCas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
364  // @TODO: the lock() call assumes that all other relavent sets() use one
365  if ( !$this->lock( $key, 0 ) ) {
366  return false; // non-blocking
367  }
368 
369  $curCasToken = null; // passed by reference
370  $this->clearLastError();
371  $this->doGet( $key, self::READ_LATEST, $curCasToken );
372  if ( is_object( $curCasToken ) ) {
373  // Using === does not work with objects since it checks for instance identity
374  throw new UnexpectedValueException( "CAS token cannot be an object" );
375  }
376  if ( $this->getLastError() ) {
377  // Fail if the old CAS token could not be read
378  $success = false;
379  $this->logger->warning(
380  __METHOD__ . ' failed due to write I/O error for {key}.',
381  [ 'key' => $key ]
382  );
383  } elseif ( $casToken === $curCasToken ) {
384  $success = $this->doSet( $key, $value, $exptime, $flags );
385  } else {
386  $success = false; // mismatched or failed
387  $this->logger->info(
388  __METHOD__ . ' failed due to race condition for {key}.',
389  [ 'key' => $key ]
390  );
391  }
392 
393  $this->unlock( $key );
394 
395  return $success;
396  }
397 
415  public function changeTTL( $key, $exptime = 0, $flags = 0 ) {
416  return $this->doChangeTTL( $key, $exptime, $flags );
417  }
418 
425  protected function doChangeTTL( $key, $exptime, $flags ) {
426  if ( !$this->lock( $key, 0 ) ) {
427  return false;
428  }
429 
430  $expiry = $this->getExpirationAsTimestamp( $exptime );
431  $delete = ( $expiry != self::TTL_INDEFINITE && $expiry < $this->getCurrentTime() );
432 
433  // Use doGet() to avoid having to trigger resolveSegments()
434  $blob = $this->doGet( $key, self::READ_LATEST );
435  if ( $blob ) {
436  if ( $delete ) {
437  $ok = $this->doDelete( $key, $flags );
438  } else {
439  $ok = $this->doSet( $key, $blob, $exptime, $flags );
440  }
441  } else {
442  $ok = false;
443  }
444 
445  $this->unlock( $key );
446 
447  return $ok;
448  }
449 
461  public function lock( $key, $timeout = 6, $expiry = 6, $rclass = '' ) {
462  // Avoid deadlocks and allow lock reentry if specified
463  if ( isset( $this->locks[$key] ) ) {
464  if ( $rclass != '' && $this->locks[$key]['class'] === $rclass ) {
465  ++$this->locks[$key]['depth'];
466  return true;
467  } else {
468  return false;
469  }
470  }
471 
472  $fname = __METHOD__;
473  $expiry = min( $expiry ?: INF, self::TTL_DAY );
474  $loop = new WaitConditionLoop(
475  function () use ( $key, $expiry, $fname ) {
476  $this->clearLastError();
477  if ( $this->add( "{$key}:lock", 1, $expiry ) ) {
478  return WaitConditionLoop::CONDITION_REACHED; // locked!
479  } elseif ( $this->getLastError() ) {
480  $this->logger->warning(
481  $fname . ' failed due to I/O error for {key}.',
482  [ 'key' => $key ]
483  );
484 
485  return WaitConditionLoop::CONDITION_ABORTED; // network partition?
486  }
487 
488  return WaitConditionLoop::CONDITION_CONTINUE;
489  },
490  $timeout
491  );
492 
493  $code = $loop->invoke();
494  $locked = ( $code === $loop::CONDITION_REACHED );
495  if ( $locked ) {
496  $this->locks[$key] = [ 'class' => $rclass, 'depth' => 1 ];
497  } elseif ( $code === $loop::CONDITION_TIMED_OUT ) {
498  $this->logger->warning(
499  "$fname failed due to timeout for {key}.",
500  [ 'key' => $key, 'timeout' => $timeout ]
501  );
502  }
503 
504  return $locked;
505  }
506 
513  public function unlock( $key ) {
514  if ( !isset( $this->locks[$key] ) ) {
515  return false;
516  }
517 
518  if ( --$this->locks[$key]['depth'] <= 0 ) {
519  unset( $this->locks[$key] );
520 
521  $ok = $this->doDelete( "{$key}:lock" );
522  if ( !$ok ) {
523  $this->logger->warning(
524  __METHOD__ . ' failed to release lock for {key}.',
525  [ 'key' => $key ]
526  );
527  }
528 
529  return $ok;
530  }
531 
532  return true;
533  }
534 
546  $timestamp,
547  callable $progress = null,
548  $limit = INF
549  ) {
550  return false;
551  }
552 
559  public function getMulti( array $keys, $flags = 0 ) {
560  $foundByKey = $this->doGetMulti( $keys, $flags );
561 
562  $res = [];
563  foreach ( $keys as $key ) {
564  // Resolve one blob at a time (avoids too much I/O at once)
565  if ( array_key_exists( $key, $foundByKey ) ) {
566  // A value should not appear in the key if a segment is missing
567  $value = $this->resolveSegments( $key, $foundByKey[$key] );
568  if ( $value !== false ) {
569  $res[$key] = $value;
570  }
571  }
572  }
573 
574  return $res;
575  }
576 
583  protected function doGetMulti( array $keys, $flags = 0 ) {
584  $res = [];
585  foreach ( $keys as $key ) {
586  $val = $this->doGet( $key, $flags );
587  if ( $val !== false ) {
588  $res[$key] = $val;
589  }
590  }
591 
592  return $res;
593  }
594 
606  public function setMulti( array $data, $exptime = 0, $flags = 0 ) {
607  if ( $this->fieldHasFlags( $flags, self::WRITE_ALLOW_SEGMENTS ) ) {
608  throw new InvalidArgumentException( __METHOD__ . ' got WRITE_ALLOW_SEGMENTS' );
609  }
610 
611  return $this->doSetMulti( $data, $exptime, $flags );
612  }
613 
620  protected function doSetMulti( array $data, $exptime = 0, $flags = 0 ) {
621  $res = true;
622  foreach ( $data as $key => $value ) {
623  $res = $this->doSet( $key, $value, $exptime, $flags ) && $res;
624  }
625 
626  return $res;
627  }
628 
639  public function deleteMulti( array $keys, $flags = 0 ) {
640  if ( $this->fieldHasFlags( $flags, self::WRITE_PRUNE_SEGMENTS ) ) {
641  throw new InvalidArgumentException( __METHOD__ . ' got WRITE_PRUNE_SEGMENTS' );
642  }
643 
644  return $this->doDeleteMulti( $keys, $flags );
645  }
646 
652  protected function doDeleteMulti( array $keys, $flags = 0 ) {
653  $res = true;
654  foreach ( $keys as $key ) {
655  $res = $this->doDelete( $key, $flags ) && $res;
656  }
657  return $res;
658  }
659 
671  public function changeTTLMulti( array $keys, $exptime, $flags = 0 ) {
672  $res = true;
673  foreach ( $keys as $key ) {
674  $res = $this->doChangeTTL( $key, $exptime, $flags ) && $res;
675  }
676 
677  return $res;
678  }
679 
680  public function incrWithInit( $key, $exptime, $value = 1, $init = null, $flags = 0 ) {
681  $init = is_int( $init ) ? $init : $value;
682  $this->clearLastError();
683  $newValue = $this->incr( $key, $value, $flags );
684  if ( $newValue === false && !$this->getLastError() ) {
685  // No key set; initialize
686  $newValue = $this->add( $key, (int)$init, $exptime, $flags ) ? $init : false;
687  if ( $newValue === false && !$this->getLastError() ) {
688  // Raced out initializing; increment
689  $newValue = $this->incr( $key, $value, $flags );
690  }
691  }
692 
693  return $newValue;
694  }
695 
703  final protected function resolveSegments( $key, $mainValue ) {
704  if ( SerializedValueContainer::isUnified( $mainValue ) ) {
705  return $this->unserialize( $mainValue->{SerializedValueContainer::UNIFIED_DATA} );
706  }
707 
708  if ( SerializedValueContainer::isSegmented( $mainValue ) ) {
709  $orderedKeys = array_map(
710  function ( $segmentHash ) use ( $key ) {
711  return $this->makeGlobalKey( self::SEGMENT_COMPONENT, $key, $segmentHash );
712  },
714  );
715 
716  $segmentsByKey = $this->doGetMulti( $orderedKeys );
717 
718  $parts = [];
719  foreach ( $orderedKeys as $segmentKey ) {
720  if ( isset( $segmentsByKey[$segmentKey] ) ) {
721  $parts[] = $segmentsByKey[$segmentKey];
722  } else {
723  return false; // missing segment
724  }
725  }
726 
727  return $this->unserialize( implode( '', $parts ) );
728  }
729 
730  return $mainValue;
731  }
732 
738  public function getLastError() {
739  return $this->lastError;
740  }
741 
746  public function clearLastError() {
747  $this->lastError = self::ERR_NONE;
748  }
749 
755  protected function setLastError( $err ) {
756  $this->lastError = $err;
757  }
758 
759  final public function addBusyCallback( callable $workCallback ) {
760  $this->busyCallbacks[] = $workCallback;
761  }
762 
773  final protected function makeValueOrSegmentList( $key, $value, $exptime, $flags ) {
774  $entry = $value;
775  $usable = true;
776 
777  if (
778  $this->fieldHasFlags( $flags, self::WRITE_ALLOW_SEGMENTS ) &&
779  !is_int( $value ) && // avoid breaking incr()/decr()
780  is_finite( $this->segmentationSize )
781  ) {
782  $segmentSize = $this->segmentationSize;
783  $maxTotalSize = $this->segmentedValueMaxSize;
784 
785  $serialized = $this->getSerialized( $value, $key );
786  $size = strlen( $serialized );
787  if ( $size > $maxTotalSize ) {
788  $this->logger->warning(
789  "Value for {key} exceeds $maxTotalSize bytes; cannot segment.",
790  [ 'key' => $key ]
791  );
792  } elseif ( $size <= $segmentSize ) {
793  // The serialized value was already computed, so just use it inline
795  } else {
796  // Split the serialized value into chunks and store them at different keys
797  $chunksByKey = [];
798  $segmentHashes = [];
799  $count = intdiv( $size, $segmentSize ) + ( ( $size % $segmentSize ) ? 1 : 0 );
800  for ( $i = 0; $i < $count; ++$i ) {
801  $segment = substr( $serialized, $i * $segmentSize, $segmentSize );
802  $hash = sha1( $segment );
803  $chunkKey = $this->makeGlobalKey( self::SEGMENT_COMPONENT, $key, $hash );
804  $chunksByKey[$chunkKey] = $segment;
805  $segmentHashes[] = $hash;
806  }
807  $flags &= ~self::WRITE_ALLOW_SEGMENTS; // sanity
808  $usable = $this->setMulti( $chunksByKey, $exptime, $flags );
809  $entry = SerializedValueContainer::newSegmented( $segmentHashes );
810  }
811  }
812 
813  return [ $entry, $usable ];
814  }
815 
821  final protected function isRelativeExpiration( $exptime ) {
822  return ( $exptime !== self::TTL_INDEFINITE && $exptime < ( 10 * self::TTL_YEAR ) );
823  }
824 
838  final protected function getExpirationAsTimestamp( $exptime ) {
839  if ( $exptime == self::TTL_INDEFINITE ) {
840  return $exptime;
841  }
842 
843  return $this->isRelativeExpiration( $exptime )
844  ? intval( $this->getCurrentTime() + $exptime )
845  : $exptime;
846  }
847 
862  final protected function getExpirationAsTTL( $exptime ) {
863  if ( $exptime == self::TTL_INDEFINITE ) {
864  return $exptime;
865  }
866 
867  return $this->isRelativeExpiration( $exptime )
868  ? $exptime
869  : (int)max( $exptime - $this->getCurrentTime(), 1 );
870  }
871 
878  final protected function isInteger( $value ) {
879  if ( is_int( $value ) ) {
880  return true;
881  } elseif ( !is_string( $value ) ) {
882  return false;
883  }
884 
885  $integer = (int)$value;
886 
887  return ( $value === (string)$integer );
888  }
889 
890  public function makeKeyInternal( $keyspace, $args ) {
891  $key = $keyspace;
892  foreach ( $args as $arg ) {
893  $key .= ':' . str_replace( ':', '%3A', $arg );
894  }
895  return strtr( $key, ' ', '_' );
896  }
897 
906  public function makeGlobalKey( $class, ...$components ) {
907  return $this->makeKeyInternal( 'global', func_get_args() );
908  }
909 
918  public function makeKey( $class, ...$components ) {
919  return $this->makeKeyInternal( $this->keyspace, func_get_args() );
920  }
921 
927  public function getQoS( $flag ) {
928  return $this->attrMap[$flag] ?? self::QOS_UNKNOWN;
929  }
930 
931  public function getSegmentationSize() {
932  return $this->segmentationSize;
933  }
934 
935  public function getSegmentedValueMaxSize() {
936  return $this->segmentedValueMaxSize;
937  }
938 
939  public function setNewPreparedValues( array $valuesByKey ) {
940  $this->preparedValues = [];
941 
942  $sizes = [];
943  foreach ( $valuesByKey as $key => $value ) {
944  if ( $value === false ) {
945  $sizes[] = null; // not storable, don't bother
946  continue;
947  }
948 
949  $serialized = $this->serialize( $value );
950  $sizes[] = ( $serialized !== false ) ? strlen( $serialized ) : null;
951 
952  $this->preparedValues[$key] = [ $value, $serialized ];
953  }
954 
955  return $sizes;
956  }
957 
968  protected function getSerialized( $value, $key ) {
969  // Reuse any available prepared (serialized) value
970  if ( array_key_exists( $key, $this->preparedValues ) ) {
971  list( $prepValue, $prepSerialized ) = $this->preparedValues[$key];
972  // Normally, this comparison should only take a few microseconds to confirm a match.
973  // Using "===" on variables of different types is always fast. It is also fast for
974  // variables of matching type int, float, bool, null, and object. Lastly, it is fast
975  // for comparing arrays/strings if they are copy-on-write references, which should be
976  // the case at this point, assuming prepareValues() was called correctly.
977  if ( $prepValue === $value ) {
978  unset( $this->preparedValues[$key] );
979 
980  return $prepSerialized;
981  }
982  }
983 
984  return $this->serialize( $value );
985  }
986 
996  protected function guessSerialValueSize( $value, $depth = 0, &$loops = 0 ) {
997  // Include serialization format overhead estimates roughly based on serialize(),
998  // without counting . Also, int/float variables use the largest case
999  // byte size for numbers of that type; this avoids CPU overhead for large arrays.
1000  switch ( gettype( $value ) ) {
1001  case 'string':
1002  // E.g. "<type><delim1><quote><value><quote><delim2>"
1003  return strlen( $value ) + 5;
1004  case 'integer':
1005  // E.g. "<type><delim1><sign><2^63><delim2>";
1006  // ceil(log10 (2^63)) = 19
1007  return 23;
1008  case 'double':
1009  // E.g. "<type><delim1><sign><2^52><esign><2^10><delim2>"
1010  // ceil(log10 (2^52)) = 16 and ceil(log10 (2^10)) = 4
1011  return 25;
1012  case 'boolean':
1013  // E.g. "true" becomes "1" and "false" is not storable
1014  return $value ? 1 : null;
1015  case 'NULL':
1016  return 1; // "\0"
1017  case 'array':
1018  case 'object':
1019  // Give up up and guess if there is too much depth
1020  if ( $depth >= 5 && $loops >= 256 ) {
1021  return 1024;
1022  }
1023 
1024  ++$loops;
1025  // E.g. "<type><delim1><brace><<Kn><Vn> for all n><brace><delim2>"
1026  $size = 5;
1027  // Note that casting to an array includes private object members
1028  foreach ( (array)$value as $k => $v ) {
1029  // Inline the recursive result here for performance
1030  $size += is_string( $k ) ? ( strlen( $k ) + 5 ) : 23;
1031  $size += $this->guessSerialValueSize( $v, $depth + 1, $loops );
1032  }
1033 
1034  return $size;
1035  default:
1036  return null; // invalid
1037  }
1038  }
1039 
1045  protected function serialize( $value ) {
1046  return is_int( $value ) ? $value : serialize( $value );
1047  }
1048 
1054  protected function unserialize( $value ) {
1055  return $this->isInteger( $value ) ? (int)$value : unserialize( $value );
1056  }
1057 
1061  protected function debug( $text ) {
1062  if ( $this->debugMode ) {
1063  $this->logger->debug( "{class} debug: $text", [ 'class' => static::class ] );
1064  }
1065  }
1066 }
MediumSpecificBagOStuff\mergeViaCas
mergeViaCas( $key, callable $callback, $exptime, $attempts, $flags)
Definition: MediumSpecificBagOStuff.php:275
MediumSpecificBagOStuff\setLastError
setLastError( $err)
Set the "last error" registry.
Definition: MediumSpecificBagOStuff.php:755
MediumSpecificBagOStuff\doCas
doCas( $casToken, $key, $value, $exptime=0, $flags=0)
Check and set an item.
Definition: MediumSpecificBagOStuff.php:363
MediumSpecificBagOStuff\isInteger
isInteger( $value)
Check if a value is an integer.
Definition: MediumSpecificBagOStuff.php:878
MediumSpecificBagOStuff\$reportDupes
bool $reportDupes
Definition: MediumSpecificBagOStuff.php:51
MediumSpecificBagOStuff\guessSerialValueSize
guessSerialValueSize( $value, $depth=0, &$loops=0)
Estimate the size of a variable once serialized.
Definition: MediumSpecificBagOStuff.php:996
MediumSpecificBagOStuff\$keyspace
string $keyspace
Definition: MediumSpecificBagOStuff.php:40
MediumSpecificBagOStuff\debug
debug( $text)
Definition: MediumSpecificBagOStuff.php:1061
MediumSpecificBagOStuff\incrWithInit
incrWithInit( $key, $exptime, $value=1, $init=null, $flags=0)
Increase the value of the given key (no TTL change) if it exists or create it otherwise.
Definition: MediumSpecificBagOStuff.php:680
MediumSpecificBagOStuff\cas
cas( $casToken, $key, $value, $exptime=0, $flags=0)
Check and set an item.
Definition: MediumSpecificBagOStuff.php:338
$serialized
foreach( $res as $row) $serialized
Definition: testCompression.php:88
MediumSpecificBagOStuff\trackDuplicateKeys
trackDuplicateKeys( $key)
Track the number of times that a given key has been used.
Definition: MediumSpecificBagOStuff.php:122
BagOStuff
Class representing a cache/ephemeral data store.
Definition: BagOStuff.php:65
MediumSpecificBagOStuff\$duplicateKeyLookups
array $duplicateKeyLookups
Definition: MediumSpecificBagOStuff.php:49
MediumSpecificBagOStuff\unlock
unlock( $key)
Release an advisory lock on a key string.
Definition: MediumSpecificBagOStuff.php:513
$success
$success
Definition: NoLocalSettings.php:42
MediumSpecificBagOStuff\serialize
serialize( $value)
Definition: MediumSpecificBagOStuff.php:1045
$res
$res
Definition: testCompression.php:57
serialize
serialize()
Definition: ApiMessageTrait.php:138
MediumSpecificBagOStuff\getMulti
getMulti(array $keys, $flags=0)
Get an associative array containing the item for each of the keys that have items.
Definition: MediumSpecificBagOStuff.php:559
MediumSpecificBagOStuff\changeTTL
changeTTL( $key, $exptime=0, $flags=0)
Change the expiration on a key if it exists.
Definition: MediumSpecificBagOStuff.php:415
MediumSpecificBagOStuff\makeGlobalKey
makeGlobalKey( $class,... $components)
Make a global cache key.
Definition: MediumSpecificBagOStuff.php:906
MediumSpecificBagOStuff\$syncTimeout
int $syncTimeout
Seconds.
Definition: MediumSpecificBagOStuff.php:42
SerializedValueContainer\isSegmented
static isSegmented( $value)
Definition: SerializedValueContainer.php:50
MediumSpecificBagOStuff\makeKeyInternal
makeKeyInternal( $keyspace, $args)
Construct a cache key.
Definition: MediumSpecificBagOStuff.php:890
MediumSpecificBagOStuff\$segmentedValueMaxSize
int $segmentedValueMaxSize
Bytes; maximum total size of a segmented cache value.
Definition: MediumSpecificBagOStuff.php:46
MediumSpecificBagOStuff\resolveSegments
resolveSegments( $key, $mainValue)
Get and reassemble the chunks of blob at the given key.
Definition: MediumSpecificBagOStuff.php:703
MediumSpecificBagOStuff\deleteObjectsExpiringBefore
deleteObjectsExpiringBefore( $timestamp, callable $progress=null, $limit=INF)
Delete all objects expiring before a certain date.
Definition: MediumSpecificBagOStuff.php:545
MediumSpecificBagOStuff\getExpirationAsTimestamp
getExpirationAsTimestamp( $exptime)
Convert an optionally relative timestamp to an absolute time.
Definition: MediumSpecificBagOStuff.php:838
MediumSpecificBagOStuff\changeTTLMulti
changeTTLMulti(array $keys, $exptime, $flags=0)
Change the expiration of multiple keys that exist.
Definition: MediumSpecificBagOStuff.php:671
MediumSpecificBagOStuff\doSetMulti
doSetMulti(array $data, $exptime=0, $flags=0)
Definition: MediumSpecificBagOStuff.php:620
$blob
$blob
Definition: testCompression.php:70
MediumSpecificBagOStuff\doSet
doSet( $key, $value, $exptime=0, $flags=0)
Set an item.
MediumSpecificBagOStuff\deleteMulti
deleteMulti(array $keys, $flags=0)
Batch deletion.
Definition: MediumSpecificBagOStuff.php:639
MediumSpecificBagOStuff\addBusyCallback
addBusyCallback(callable $workCallback)
Let a callback be run to avoid wasting time on special blocking calls.
Definition: MediumSpecificBagOStuff.php:759
$args
if( $line===false) $args
Definition: mcc.php:124
MediumSpecificBagOStuff\doGet
doGet( $key, $flags=0, &$casToken=null)
MediumSpecificBagOStuff\getQoS
getQoS( $flag)
Definition: MediumSpecificBagOStuff.php:927
MediumSpecificBagOStuff\getExpirationAsTTL
getExpirationAsTTL( $exptime)
Convert an optionally absolute expiry time to a relative time.
Definition: MediumSpecificBagOStuff.php:862
MediumSpecificBagOStuff\setNewPreparedValues
setNewPreparedValues(array $valuesByKey)
Prepare values for storage and get their serialized sizes, or, estimate those sizes.
Definition: MediumSpecificBagOStuff.php:939
MediumSpecificBagOStuff\makeKey
makeKey( $class,... $components)
Make a cache key, scoped to this instance's keyspace.
Definition: MediumSpecificBagOStuff.php:918
MediumSpecificBagOStuff
Storage medium specific cache for storing items (e.g.
Definition: MediumSpecificBagOStuff.php:34
MediumSpecificBagOStuff\$dupeTrackScheduled
bool $dupeTrackScheduled
Definition: MediumSpecificBagOStuff.php:53
MediumSpecificBagOStuff\doDelete
doDelete( $key, $flags=0)
Delete an item.
MediumSpecificBagOStuff\makeValueOrSegmentList
makeValueOrSegmentList( $key, $value, $exptime, $flags)
Determine the entry (inline or segment list) to store under a key to save the value.
Definition: MediumSpecificBagOStuff.php:773
MediumSpecificBagOStuff\__construct
__construct(array $params=[])
Definition: MediumSpecificBagOStuff.php:83
MediumSpecificBagOStuff\$segmentationSize
int $segmentationSize
Bytes; chunk size of segmented cache values.
Definition: MediumSpecificBagOStuff.php:44
MediumSpecificBagOStuff\doGetMulti
doGetMulti(array $keys, $flags=0)
Get an associative array containing the item for each of the keys that have items.
Definition: MediumSpecificBagOStuff.php:583
SerializedValueContainer\UNIFIED_DATA
const UNIFIED_DATA
Definition: SerializedValueContainer.php:13
MediumSpecificBagOStuff\setMulti
setMulti(array $data, $exptime=0, $flags=0)
Batch insertion/replace.
Definition: MediumSpecificBagOStuff.php:606
MediumSpecificBagOStuff\getSegmentationSize
getSegmentationSize()
Definition: MediumSpecificBagOStuff.php:931
MediumSpecificBagOStuff\$busyCallbacks
callable[] $busyCallbacks
Definition: MediumSpecificBagOStuff.php:56
MediumSpecificBagOStuff\getSerialized
getSerialized( $value, $key)
Get the serialized form a value, using any applicable prepared value.
Definition: MediumSpecificBagOStuff.php:968
SerializedValueContainer\newUnified
static newUnified( $serialized)
Definition: SerializedValueContainer.php:20
MediumSpecificBagOStuff\isRelativeExpiration
isRelativeExpiration( $exptime)
Definition: MediumSpecificBagOStuff.php:821
unserialize
unserialize( $serialized)
Definition: ApiMessageTrait.php:146
MediumSpecificBagOStuff\unserialize
unserialize( $value)
Definition: MediumSpecificBagOStuff.php:1054
SerializedValueContainer\SEGMENTED_HASHES
const SEGMENTED_HASHES
Definition: SerializedValueContainer.php:14
BagOStuff\fieldHasFlags
fieldHasFlags( $field, $flags)
Definition: BagOStuff.php:508
MediumSpecificBagOStuff\add
add( $key, $value, $exptime=0, $flags=0)
Insert an item if it does not already exist.
Definition: MediumSpecificBagOStuff.php:229
MediumSpecificBagOStuff\$preparedValues
array[] $preparedValues
Map of (key => (PHP variable value, serialized value))
Definition: MediumSpecificBagOStuff.php:59
$keys
$keys
Definition: testCompression.php:72
MediumSpecificBagOStuff\getSegmentedValueMaxSize
getSegmentedValueMaxSize()
Definition: MediumSpecificBagOStuff.php:935
SerializedValueContainer\isUnified
static isUnified( $value)
Definition: SerializedValueContainer.php:42
MediumSpecificBagOStuff\merge
merge( $key, callable $callback, $exptime=0, $attempts=10, $flags=0)
Merge changes into the existing cache value (possibly creating a new one)
Definition: MediumSpecificBagOStuff.php:262
MediumSpecificBagOStuff\lock
lock( $key, $timeout=6, $expiry=6, $rclass='')
Acquire an advisory lock on a key string.
Definition: MediumSpecificBagOStuff.php:461
MediumSpecificBagOStuff\$locks
array[] $locks
Lock tracking.
Definition: MediumSpecificBagOStuff.php:36
MediumSpecificBagOStuff\$lastError
int $lastError
ERR_* class constant.
Definition: MediumSpecificBagOStuff.php:38
MediumSpecificBagOStuff\clearLastError
clearLastError()
Clear the "last error" registry.
Definition: MediumSpecificBagOStuff.php:746
MediumSpecificBagOStuff\doDeleteMulti
doDeleteMulti(array $keys, $flags=0)
Definition: MediumSpecificBagOStuff.php:652
MediumSpecificBagOStuff\doChangeTTL
doChangeTTL( $key, $exptime, $flags)
Definition: MediumSpecificBagOStuff.php:425
SerializedValueContainer\newSegmented
static newSegmented(array $segmentHashList)
Definition: SerializedValueContainer.php:31
MediumSpecificBagOStuff\getLastError
getLastError()
Get the "last error" registered; clearLastError() should be called manually.
Definition: MediumSpecificBagOStuff.php:738