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  private const SEGMENT_COMPONENT = 'segment';
63 
65  protected const PASS_BY_REF = -1;
66 
86  public function __construct( array $params = [] ) {
87  parent::__construct( $params );
88 
89  if ( isset( $params['keyspace'] ) ) {
90  $this->keyspace = $params['keyspace'];
91  }
92 
93  if ( !empty( $params['reportDupes'] ) && is_callable( $this->asyncHandler ) ) {
94  $this->reportDupes = true;
95  }
96 
97  $this->syncTimeout = $params['syncTimeout'] ?? 3;
98  $this->segmentationSize = $params['segmentationSize'] ?? 8388608; // 8MiB
99  $this->segmentedValueMaxSize = $params['segmentedValueMaxSize'] ?? 67108864; // 64MiB
100  }
101 
115  public function get( $key, $flags = 0 ) {
116  $this->trackDuplicateKeys( $key );
117 
118  return $this->resolveSegments( $key, $this->doGet( $key, $flags ) );
119  }
120 
125  private function trackDuplicateKeys( $key ) {
126  if ( !$this->reportDupes ) {
127  return;
128  }
129 
130  if ( !isset( $this->duplicateKeyLookups[$key] ) ) {
131  // Track that we have seen this key. This N-1 counting style allows
132  // easy filtering with array_filter() later.
133  $this->duplicateKeyLookups[$key] = 0;
134  } else {
135  $this->duplicateKeyLookups[$key] += 1;
136 
137  if ( $this->dupeTrackScheduled === false ) {
138  $this->dupeTrackScheduled = true;
139  // Schedule a callback that logs keys processed more than once by get().
140  call_user_func( $this->asyncHandler, function () {
141  $dups = array_filter( $this->duplicateKeyLookups );
142  foreach ( $dups as $key => $count ) {
143  $this->logger->warning(
144  'Duplicate get(): "{key}" fetched {count} times',
145  // Count is N-1 of the actual lookup count
146  [ 'key' => $key, 'count' => $count + 1, ]
147  );
148  }
149  } );
150  }
151  }
152  }
153 
160  abstract protected function doGet( $key, $flags = 0, &$casToken = null );
161 
171  public function set( $key, $value, $exptime = 0, $flags = 0 ) {
172  list( $entry, $usable ) = $this->makeValueOrSegmentList( $key, $value, $exptime, $flags );
173  // Only when all segments (if any) are stored should the main key be changed
174  return $usable ? $this->doSet( $key, $entry, $exptime, $flags ) : false;
175  }
176 
186  abstract protected function doSet( $key, $value, $exptime = 0, $flags = 0 );
187 
199  public function delete( $key, $flags = 0 ) {
200  if ( !$this->fieldHasFlags( $flags, self::WRITE_PRUNE_SEGMENTS ) ) {
201  return $this->doDelete( $key, $flags );
202  }
203 
204  $mainValue = $this->doGet( $key, self::READ_LATEST );
205  if ( !$this->doDelete( $key, $flags ) ) {
206  return false;
207  }
208 
209  if ( !SerializedValueContainer::isSegmented( $mainValue ) ) {
210  return true; // no segments to delete
211  }
212 
213  $orderedKeys = array_map(
214  function ( $segmentHash ) use ( $key ) {
215  return $this->makeGlobalKey( self::SEGMENT_COMPONENT, $key, $segmentHash );
216  },
218  );
219 
220  return $this->deleteMulti( $orderedKeys, $flags & ~self::WRITE_PRUNE_SEGMENTS );
221  }
222 
230  abstract protected function doDelete( $key, $flags = 0 );
231 
232  public function add( $key, $value, $exptime = 0, $flags = 0 ) {
233  list( $entry, $usable ) = $this->makeValueOrSegmentList( $key, $value, $exptime, $flags );
234  // Only when all segments (if any) are stored should the main key be changed
235  return $usable ? $this->doAdd( $key, $entry, $exptime, $flags ) : false;
236  }
237 
247  abstract protected function doAdd( $key, $value, $exptime = 0, $flags = 0 );
248 
265  public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
266  return $this->mergeViaCas( $key, $callback, $exptime, $attempts, $flags );
267  }
268 
278  final protected function mergeViaCas( $key, callable $callback, $exptime, $attempts, $flags ) {
279  $attemptsLeft = $attempts;
280  do {
281  $token = self::PASS_BY_REF; // passed by reference
282  // Get the old value and CAS token from cache
283  $this->clearLastError();
284  $currentValue = $this->resolveSegments(
285  $key,
286  $this->doGet( $key, $flags, $token )
287  );
288  if ( $this->getLastError() ) {
289  // Don't spam slow retries due to network problems (retry only on races)
290  $this->logger->warning(
291  __METHOD__ . ' failed due to read I/O error on get() for {key}.',
292  [ 'key' => $key ]
293  );
294  $success = false;
295  break;
296  }
297 
298  // Derive the new value from the old value
299  $value = $callback( $this, $key, $currentValue, $exptime );
300  $keyWasNonexistant = ( $currentValue === false );
301  $valueMatchesOldValue = ( $value === $currentValue );
302  unset( $currentValue ); // free RAM in case the value is large
303 
304  $this->clearLastError();
305  if ( $value === false || $exptime < 0 ) {
306  $success = true; // do nothing
307  } elseif ( $valueMatchesOldValue && $attemptsLeft !== $attempts ) {
308  $success = true; // recently set by another thread to the same value
309  } elseif ( $keyWasNonexistant ) {
310  // Try to create the key, failing if it gets created in the meantime
311  $success = $this->add( $key, $value, $exptime, $flags );
312  } else {
313  // Try to update the key, failing if it gets changed in the meantime
314  $success = $this->cas( $token, $key, $value, $exptime, $flags );
315  }
316  if ( $this->getLastError() ) {
317  // Don't spam slow retries due to network problems (retry only on races)
318  $this->logger->warning(
319  __METHOD__ . ' failed due to write I/O error for {key}.',
320  [ 'key' => $key ]
321  );
322  $success = false;
323  break;
324  }
325 
326  } while ( !$success && --$attemptsLeft );
327 
328  return $success;
329  }
330 
341  protected function cas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
342  if ( $casToken === null ) {
343  $this->logger->warning(
344  __METHOD__ . ' got empty CAS token for {key}.',
345  [ 'key' => $key ]
346  );
347 
348  return false; // caller may have meant to use add()?
349  }
350 
351  list( $entry, $usable ) = $this->makeValueOrSegmentList( $key, $value, $exptime, $flags );
352  // Only when all segments (if any) are stored should the main key be changed
353  return $usable ? $this->doCas( $casToken, $key, $entry, $exptime, $flags ) : false;
354  }
355 
366  protected function doCas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
367  // @TODO: the lock() call assumes that all other relavent sets() use one
368  if ( !$this->lock( $key, 0 ) ) {
369  return false; // non-blocking
370  }
371 
372  $curCasToken = self::PASS_BY_REF; // passed by reference
373  $this->clearLastError();
374  $this->doGet( $key, self::READ_LATEST, $curCasToken );
375  if ( is_object( $curCasToken ) ) {
376  // Using === does not work with objects since it checks for instance identity
377  throw new UnexpectedValueException( "CAS token cannot be an object" );
378  }
379  if ( $this->getLastError() ) {
380  // Fail if the old CAS token could not be read
381  $success = false;
382  $this->logger->warning(
383  __METHOD__ . ' failed due to write I/O error for {key}.',
384  [ 'key' => $key ]
385  );
386  } elseif ( $casToken === $curCasToken ) {
387  $success = $this->doSet( $key, $value, $exptime, $flags );
388  } else {
389  $success = false; // mismatched or failed
390  $this->logger->info(
391  __METHOD__ . ' failed due to race condition for {key}.',
392  [ 'key' => $key ]
393  );
394  }
395 
396  $this->unlock( $key );
397 
398  return $success;
399  }
400 
418  public function changeTTL( $key, $exptime = 0, $flags = 0 ) {
419  return $this->doChangeTTL( $key, $exptime, $flags );
420  }
421 
428  protected function doChangeTTL( $key, $exptime, $flags ) {
429  if ( !$this->lock( $key, 0 ) ) {
430  return false;
431  }
432 
433  $expiry = $this->getExpirationAsTimestamp( $exptime );
434  $delete = ( $expiry != self::TTL_INDEFINITE && $expiry < $this->getCurrentTime() );
435 
436  // Use doGet() to avoid having to trigger resolveSegments()
437  $blob = $this->doGet( $key, self::READ_LATEST );
438  if ( $blob ) {
439  if ( $delete ) {
440  $ok = $this->doDelete( $key, $flags );
441  } else {
442  $ok = $this->doSet( $key, $blob, $exptime, $flags );
443  }
444  } else {
445  $ok = false;
446  }
447 
448  $this->unlock( $key );
449 
450  return $ok;
451  }
452 
464  public function lock( $key, $timeout = 6, $expiry = 6, $rclass = '' ) {
465  // Avoid deadlocks and allow lock reentry if specified
466  if ( isset( $this->locks[$key] ) ) {
467  if ( $rclass != '' && $this->locks[$key]['class'] === $rclass ) {
468  ++$this->locks[$key]['depth'];
469  return true;
470  } else {
471  return false;
472  }
473  }
474 
475  $fname = __METHOD__;
476  $expiry = min( $expiry ?: INF, self::TTL_DAY );
477  $loop = new WaitConditionLoop(
478  function () use ( $key, $expiry, $fname ) {
479  $this->clearLastError();
480  if ( $this->add( "{$key}:lock", 1, $expiry ) ) {
481  return WaitConditionLoop::CONDITION_REACHED; // locked!
482  } elseif ( $this->getLastError() ) {
483  $this->logger->warning(
484  $fname . ' failed due to I/O error for {key}.',
485  [ 'key' => $key ]
486  );
487 
488  return WaitConditionLoop::CONDITION_ABORTED; // network partition?
489  }
490 
491  return WaitConditionLoop::CONDITION_CONTINUE;
492  },
493  $timeout
494  );
495 
496  $code = $loop->invoke();
497  $locked = ( $code === $loop::CONDITION_REACHED );
498  if ( $locked ) {
499  $this->locks[$key] = [ 'class' => $rclass, 'depth' => 1 ];
500  } elseif ( $code === $loop::CONDITION_TIMED_OUT ) {
501  $this->logger->warning(
502  "$fname failed due to timeout for {key}.",
503  [ 'key' => $key, 'timeout' => $timeout ]
504  );
505  }
506 
507  return $locked;
508  }
509 
516  public function unlock( $key ) {
517  if ( !isset( $this->locks[$key] ) ) {
518  return false;
519  }
520 
521  if ( --$this->locks[$key]['depth'] <= 0 ) {
522  unset( $this->locks[$key] );
523 
524  $ok = $this->doDelete( "{$key}:lock" );
525  if ( !$ok ) {
526  $this->logger->warning(
527  __METHOD__ . ' failed to release lock for {key}.',
528  [ 'key' => $key ]
529  );
530  }
531 
532  return $ok;
533  }
534 
535  return true;
536  }
537 
549  $timestamp,
550  callable $progress = null,
551  $limit = INF
552  ) {
553  return false;
554  }
555 
562  public function getMulti( array $keys, $flags = 0 ) {
563  $foundByKey = $this->doGetMulti( $keys, $flags );
564 
565  $res = [];
566  foreach ( $keys as $key ) {
567  // Resolve one blob at a time (avoids too much I/O at once)
568  if ( array_key_exists( $key, $foundByKey ) ) {
569  // A value should not appear in the key if a segment is missing
570  $value = $this->resolveSegments( $key, $foundByKey[$key] );
571  if ( $value !== false ) {
572  $res[$key] = $value;
573  }
574  }
575  }
576 
577  return $res;
578  }
579 
586  protected function doGetMulti( array $keys, $flags = 0 ) {
587  $res = [];
588  foreach ( $keys as $key ) {
589  $val = $this->doGet( $key, $flags );
590  if ( $val !== false ) {
591  $res[$key] = $val;
592  }
593  }
594 
595  return $res;
596  }
597 
609  public function setMulti( array $data, $exptime = 0, $flags = 0 ) {
610  if ( $this->fieldHasFlags( $flags, self::WRITE_ALLOW_SEGMENTS ) ) {
611  throw new InvalidArgumentException( __METHOD__ . ' got WRITE_ALLOW_SEGMENTS' );
612  }
613 
614  return $this->doSetMulti( $data, $exptime, $flags );
615  }
616 
623  protected function doSetMulti( array $data, $exptime = 0, $flags = 0 ) {
624  $res = true;
625  foreach ( $data as $key => $value ) {
626  $res = $this->doSet( $key, $value, $exptime, $flags ) && $res;
627  }
628 
629  return $res;
630  }
631 
642  public function deleteMulti( array $keys, $flags = 0 ) {
643  if ( $this->fieldHasFlags( $flags, self::WRITE_PRUNE_SEGMENTS ) ) {
644  throw new InvalidArgumentException( __METHOD__ . ' got WRITE_PRUNE_SEGMENTS' );
645  }
646 
647  return $this->doDeleteMulti( $keys, $flags );
648  }
649 
655  protected function doDeleteMulti( array $keys, $flags = 0 ) {
656  $res = true;
657  foreach ( $keys as $key ) {
658  $res = $this->doDelete( $key, $flags ) && $res;
659  }
660  return $res;
661  }
662 
674  public function changeTTLMulti( array $keys, $exptime, $flags = 0 ) {
675  $res = true;
676  foreach ( $keys as $key ) {
677  $res = $this->doChangeTTL( $key, $exptime, $flags ) && $res;
678  }
679 
680  return $res;
681  }
682 
683  public function incrWithInit( $key, $exptime, $value = 1, $init = null, $flags = 0 ) {
684  $init = is_int( $init ) ? $init : $value;
685  $this->clearLastError();
686  $newValue = $this->incr( $key, $value, $flags );
687  if ( $newValue === false && !$this->getLastError() ) {
688  // No key set; initialize
689  $newValue = $this->add( $key, (int)$init, $exptime, $flags ) ? $init : false;
690  if ( $newValue === false && !$this->getLastError() ) {
691  // Raced out initializing; increment
692  $newValue = $this->incr( $key, $value, $flags );
693  }
694  }
695 
696  return $newValue;
697  }
698 
706  final protected function resolveSegments( $key, $mainValue ) {
707  if ( SerializedValueContainer::isUnified( $mainValue ) ) {
708  return $this->unserialize( $mainValue->{SerializedValueContainer::UNIFIED_DATA} );
709  }
710 
711  if ( SerializedValueContainer::isSegmented( $mainValue ) ) {
712  $orderedKeys = array_map(
713  function ( $segmentHash ) use ( $key ) {
714  return $this->makeGlobalKey( self::SEGMENT_COMPONENT, $key, $segmentHash );
715  },
717  );
718 
719  $segmentsByKey = $this->doGetMulti( $orderedKeys );
720 
721  $parts = [];
722  foreach ( $orderedKeys as $segmentKey ) {
723  if ( isset( $segmentsByKey[$segmentKey] ) ) {
724  $parts[] = $segmentsByKey[$segmentKey];
725  } else {
726  return false; // missing segment
727  }
728  }
729 
730  return $this->unserialize( implode( '', $parts ) );
731  }
732 
733  return $mainValue;
734  }
735 
741  public function getLastError() {
742  return $this->lastError;
743  }
744 
749  public function clearLastError() {
750  $this->lastError = self::ERR_NONE;
751  }
752 
758  protected function setLastError( $err ) {
759  $this->lastError = $err;
760  }
761 
762  final public function addBusyCallback( callable $workCallback ) {
763  $this->busyCallbacks[] = $workCallback;
764  }
765 
776  final protected function makeValueOrSegmentList( $key, $value, $exptime, $flags ) {
777  $entry = $value;
778  $usable = true;
779 
780  if (
781  $this->fieldHasFlags( $flags, self::WRITE_ALLOW_SEGMENTS ) &&
782  !is_int( $value ) && // avoid breaking incr()/decr()
783  is_finite( $this->segmentationSize )
784  ) {
785  $segmentSize = $this->segmentationSize;
786  $maxTotalSize = $this->segmentedValueMaxSize;
787 
788  $serialized = $this->getSerialized( $value, $key );
789  $size = strlen( $serialized );
790  if ( $size > $maxTotalSize ) {
791  $this->logger->warning(
792  "Value for {key} exceeds $maxTotalSize bytes; cannot segment.",
793  [ 'key' => $key ]
794  );
795  } elseif ( $size <= $segmentSize ) {
796  // The serialized value was already computed, so just use it inline
798  } else {
799  // Split the serialized value into chunks and store them at different keys
800  $chunksByKey = [];
801  $segmentHashes = [];
802  $count = intdiv( $size, $segmentSize ) + ( ( $size % $segmentSize ) ? 1 : 0 );
803  for ( $i = 0; $i < $count; ++$i ) {
804  $segment = substr( $serialized, $i * $segmentSize, $segmentSize );
805  $hash = sha1( $segment );
806  $chunkKey = $this->makeGlobalKey( self::SEGMENT_COMPONENT, $key, $hash );
807  $chunksByKey[$chunkKey] = $segment;
808  $segmentHashes[] = $hash;
809  }
810  $flags &= ~self::WRITE_ALLOW_SEGMENTS; // sanity
811  $usable = $this->setMulti( $chunksByKey, $exptime, $flags );
812  $entry = SerializedValueContainer::newSegmented( $segmentHashes );
813  }
814  }
815 
816  return [ $entry, $usable ];
817  }
818 
824  final protected function isRelativeExpiration( $exptime ) {
825  return ( $exptime !== self::TTL_INDEFINITE && $exptime < ( 10 * self::TTL_YEAR ) );
826  }
827 
841  final protected function getExpirationAsTimestamp( $exptime ) {
842  if ( $exptime == self::TTL_INDEFINITE ) {
843  return $exptime;
844  }
845 
846  return $this->isRelativeExpiration( $exptime )
847  ? intval( $this->getCurrentTime() + $exptime )
848  : $exptime;
849  }
850 
865  final protected function getExpirationAsTTL( $exptime ) {
866  if ( $exptime == self::TTL_INDEFINITE ) {
867  return $exptime;
868  }
869 
870  return $this->isRelativeExpiration( $exptime )
871  ? $exptime
872  : (int)max( $exptime - $this->getCurrentTime(), 1 );
873  }
874 
881  final protected function isInteger( $value ) {
882  if ( is_int( $value ) ) {
883  return true;
884  } elseif ( !is_string( $value ) ) {
885  return false;
886  }
887 
888  $integer = (int)$value;
889 
890  return ( $value === (string)$integer );
891  }
892 
893  public function makeKeyInternal( $keyspace, $args ) {
894  $key = $keyspace;
895  foreach ( $args as $arg ) {
896  $key .= ':' . str_replace( ':', '%3A', $arg );
897  }
898  return strtr( $key, ' ', '_' );
899  }
900 
909  public function makeGlobalKey( $class, ...$components ) {
910  return $this->makeKeyInternal( 'global', func_get_args() );
911  }
912 
921  public function makeKey( $class, ...$components ) {
922  return $this->makeKeyInternal( $this->keyspace, func_get_args() );
923  }
924 
930  public function getQoS( $flag ) {
931  return $this->attrMap[$flag] ?? self::QOS_UNKNOWN;
932  }
933 
934  public function getSegmentationSize() {
935  return $this->segmentationSize;
936  }
937 
938  public function getSegmentedValueMaxSize() {
939  return $this->segmentedValueMaxSize;
940  }
941 
942  public function setNewPreparedValues( array $valueByKey ) {
943  $this->preparedValues = [];
944 
945  $sizes = [];
946  foreach ( $valueByKey as $key => $value ) {
947  if ( $value === false ) {
948  $sizes[] = null; // not storable, don't bother
949  continue;
950  }
951 
952  $serialized = $this->serialize( $value );
953  $sizes[] = ( $serialized !== false ) ? strlen( $serialized ) : null;
954 
955  $this->preparedValues[$key] = [ $value, $serialized ];
956  }
957 
958  return $sizes;
959  }
960 
971  protected function getSerialized( $value, $key ) {
972  // Reuse any available prepared (serialized) value
973  if ( array_key_exists( $key, $this->preparedValues ) ) {
974  list( $prepValue, $prepSerialized ) = $this->preparedValues[$key];
975  // Normally, this comparison should only take a few microseconds to confirm a match.
976  // Using "===" on variables of different types is always fast. It is also fast for
977  // variables of matching type int, float, bool, null, and object. Lastly, it is fast
978  // for comparing arrays/strings if they are copy-on-write references, which should be
979  // the case at this point, assuming prepareValues() was called correctly.
980  if ( $prepValue === $value ) {
981  unset( $this->preparedValues[$key] );
982 
983  return $prepSerialized;
984  }
985  }
986 
987  return $this->serialize( $value );
988  }
989 
999  protected function guessSerialValueSize( $value, $depth = 0, &$loops = 0 ) {
1000  // Include serialization format overhead estimates roughly based on serialize(),
1001  // without counting . Also, int/float variables use the largest case
1002  // byte size for numbers of that type; this avoids CPU overhead for large arrays.
1003  switch ( gettype( $value ) ) {
1004  case 'string':
1005  // E.g. "<type><delim1><quote><value><quote><delim2>"
1006  return strlen( $value ) + 5;
1007  case 'integer':
1008  // E.g. "<type><delim1><sign><2^63><delim2>";
1009  // ceil(log10 (2^63)) = 19
1010  return 23;
1011  case 'double':
1012  // E.g. "<type><delim1><sign><2^52><esign><2^10><delim2>"
1013  // ceil(log10 (2^52)) = 16 and ceil(log10 (2^10)) = 4
1014  return 25;
1015  case 'boolean':
1016  // E.g. "true" becomes "1" and "false" is not storable
1017  return $value ? 1 : null;
1018  case 'NULL':
1019  return 1; // "\0"
1020  case 'array':
1021  case 'object':
1022  // Give up and guess if there is too much depth
1023  if ( $depth >= 5 && $loops >= 256 ) {
1024  return 1024;
1025  }
1026 
1027  ++$loops;
1028  // E.g. "<type><delim1><brace><<Kn><Vn> for all n><brace><delim2>"
1029  $size = 5;
1030  // Note that casting to an array includes private object members
1031  foreach ( (array)$value as $k => $v ) {
1032  // Inline the recursive result here for performance
1033  $size += is_string( $k ) ? ( strlen( $k ) + 5 ) : 23;
1034  $size += $this->guessSerialValueSize( $v, $depth + 1, $loops );
1035  }
1036 
1037  return $size;
1038  default:
1039  return null; // invalid
1040  }
1041  }
1042 
1048  protected function serialize( $value ) {
1049  return is_int( $value ) ? $value : serialize( $value );
1050  }
1051 
1057  protected function unserialize( $value ) {
1058  return $this->isInteger( $value ) ? (int)$value : unserialize( $value );
1059  }
1060 
1064  protected function debug( $text ) {
1065  if ( $this->debugMode ) {
1066  $this->logger->debug( "{class} debug: $text", [ 'class' => static::class ] );
1067  }
1068  }
1069 }
MediumSpecificBagOStuff\mergeViaCas
mergeViaCas( $key, callable $callback, $exptime, $attempts, $flags)
Definition: MediumSpecificBagOStuff.php:278
MediumSpecificBagOStuff\setLastError
setLastError( $err)
Set the "last error" registry.
Definition: MediumSpecificBagOStuff.php:758
MediumSpecificBagOStuff\doCas
doCas( $casToken, $key, $value, $exptime=0, $flags=0)
Check and set an item.
Definition: MediumSpecificBagOStuff.php:366
MediumSpecificBagOStuff\isInteger
isInteger( $value)
Check if a value is an integer.
Definition: MediumSpecificBagOStuff.php:881
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:999
MediumSpecificBagOStuff\$keyspace
string $keyspace
Definition: MediumSpecificBagOStuff.php:40
MediumSpecificBagOStuff\debug
debug( $text)
Definition: MediumSpecificBagOStuff.php:1064
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:683
MediumSpecificBagOStuff\doAdd
doAdd( $key, $value, $exptime=0, $flags=0)
Insert an item if it does not already exist.
MediumSpecificBagOStuff\cas
cas( $casToken, $key, $value, $exptime=0, $flags=0)
Check and set an item.
Definition: MediumSpecificBagOStuff.php:341
$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:125
BagOStuff
Class representing a cache/ephemeral data store.
Definition: BagOStuff.php:71
MediumSpecificBagOStuff\$duplicateKeyLookups
array $duplicateKeyLookups
Definition: MediumSpecificBagOStuff.php:49
MediumSpecificBagOStuff\unlock
unlock( $key)
Release an advisory lock on a key string.
Definition: MediumSpecificBagOStuff.php:516
$success
$success
Definition: NoLocalSettings.php:42
MediumSpecificBagOStuff\serialize
serialize( $value)
Definition: MediumSpecificBagOStuff.php:1048
$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:562
MediumSpecificBagOStuff\changeTTL
changeTTL( $key, $exptime=0, $flags=0)
Change the expiration on a key if it exists.
Definition: MediumSpecificBagOStuff.php:418
MediumSpecificBagOStuff\makeGlobalKey
makeGlobalKey( $class,... $components)
Make a global cache key.
Definition: MediumSpecificBagOStuff.php:909
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:893
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:706
MediumSpecificBagOStuff\deleteObjectsExpiringBefore
deleteObjectsExpiringBefore( $timestamp, callable $progress=null, $limit=INF)
Delete all objects expiring before a certain date.
Definition: MediumSpecificBagOStuff.php:548
MediumSpecificBagOStuff\getExpirationAsTimestamp
getExpirationAsTimestamp( $exptime)
Convert an optionally relative timestamp to an absolute time.
Definition: MediumSpecificBagOStuff.php:841
MediumSpecificBagOStuff\changeTTLMulti
changeTTLMulti(array $keys, $exptime, $flags=0)
Change the expiration of multiple keys that exist.
Definition: MediumSpecificBagOStuff.php:674
MediumSpecificBagOStuff\doSetMulti
doSetMulti(array $data, $exptime=0, $flags=0)
Definition: MediumSpecificBagOStuff.php:623
$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:642
MediumSpecificBagOStuff\addBusyCallback
addBusyCallback(callable $workCallback)
Let a callback be run to avoid wasting time on special blocking calls.
Definition: MediumSpecificBagOStuff.php:762
$args
if( $line===false) $args
Definition: mcc.php:124
MediumSpecificBagOStuff\doGet
doGet( $key, $flags=0, &$casToken=null)
MediumSpecificBagOStuff\getQoS
getQoS( $flag)
Definition: MediumSpecificBagOStuff.php:930
MediumSpecificBagOStuff\getExpirationAsTTL
getExpirationAsTTL( $exptime)
Convert an optionally absolute expiry time to a relative time.
Definition: MediumSpecificBagOStuff.php:865
MediumSpecificBagOStuff\makeKey
makeKey( $class,... $components)
Make a cache key, scoped to this instance's keyspace.
Definition: MediumSpecificBagOStuff.php:921
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:776
MediumSpecificBagOStuff\__construct
__construct(array $params=[])
Definition: MediumSpecificBagOStuff.php:86
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:586
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:609
MediumSpecificBagOStuff\getSegmentationSize
getSegmentationSize()
Definition: MediumSpecificBagOStuff.php:934
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:971
SerializedValueContainer\newUnified
static newUnified( $serialized)
Definition: SerializedValueContainer.php:20
MediumSpecificBagOStuff\isRelativeExpiration
isRelativeExpiration( $exptime)
Definition: MediumSpecificBagOStuff.php:824
unserialize
unserialize( $serialized)
Definition: ApiMessageTrait.php:146
MediumSpecificBagOStuff\setNewPreparedValues
setNewPreparedValues(array $valueByKey)
Prepare values for storage and get their serialized sizes, or, estimate those sizes.
Definition: MediumSpecificBagOStuff.php:942
MediumSpecificBagOStuff\unserialize
unserialize( $value)
Definition: MediumSpecificBagOStuff.php:1057
SerializedValueContainer\SEGMENTED_HASHES
const SEGMENTED_HASHES
Definition: SerializedValueContainer.php:14
BagOStuff\fieldHasFlags
fieldHasFlags( $field, $flags)
Definition: BagOStuff.php:509
MediumSpecificBagOStuff\add
add( $key, $value, $exptime=0, $flags=0)
Insert an item if it does not already exist.
Definition: MediumSpecificBagOStuff.php:232
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:938
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:265
MediumSpecificBagOStuff\lock
lock( $key, $timeout=6, $expiry=6, $rclass='')
Acquire an advisory lock on a key string.
Definition: MediumSpecificBagOStuff.php:464
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:749
MediumSpecificBagOStuff\doDeleteMulti
doDeleteMulti(array $keys, $flags=0)
Definition: MediumSpecificBagOStuff.php:655
MediumSpecificBagOStuff\doChangeTTL
doChangeTTL( $key, $exptime, $flags)
Definition: MediumSpecificBagOStuff.php:428
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:741