*
* @param string $key
* @param int $flags Bitfield of BagOStuff::READ_* constants [optional]
- * @param int|null $oldFlags [unused]
* @return mixed Returns false on failure or if the item does not exist
*/
- public function get( $key, $flags = 0, $oldFlags = null ) {
- // B/C for ( $key, &$casToken = null, $flags = 0 )
- $flags = is_int( $oldFlags ) ? $oldFlags : $flags;
-
+ public function get( $key, $flags = 0 ) {
$this->trackDuplicateKeys( $key );
return $this->doGet( $key, $flags );
/**
* @param string $key
* @param int $flags Bitfield of BagOStuff::READ_* constants [optional]
+ * @param mixed|null &$casToken Token to use for check-and-set comparisons
* @return mixed Returns false on failure or if the item does not exist
*/
- abstract protected function doGet( $key, $flags = 0 );
-
- /**
- * @note This method is only needed if merge() uses mergeViaCas()
- *
- * @param string $key
- * @param mixed &$casToken
- * @param int $flags Bitfield of BagOStuff::READ_* constants [optional]
- * @return mixed Returns false on failure or if the item does not exist
- * @throws Exception
- */
- protected function getWithToken( $key, &$casToken, $flags = 0 ) {
- throw new Exception( __METHOD__ . ' not implemented.' );
- }
+ abstract protected function doGet( $key, $flags = 0, &$casToken = null );
/**
* Set an item
*/
abstract public function delete( $key, $flags = 0 );
+ /**
+ * Insert an item if it does not already exist
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime
+ * @param int $flags Bitfield of BagOStuff::WRITE_* constants (since 1.33)
+ * @return bool Success
+ */
+ abstract public function add( $key, $value, $exptime = 0, $flags = 0 );
+
/**
* Merge changes into the existing cache value (possibly creating a new one)
*
* @throws InvalidArgumentException
*/
public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
- return $this->mergeViaLock( $key, $callback, $exptime, $attempts, $flags );
+ return $this->mergeViaCas( $key, $callback, $exptime, $attempts, $flags );
}
/**
*/
protected function mergeViaCas( $key, $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
do {
- $this->clearLastError();
- $reportDupes = $this->reportDupes;
- $this->reportDupes = false;
$casToken = null; // passed by reference
- $currentValue = $this->getWithToken( $key, $casToken, self::READ_LATEST );
- $this->reportDupes = $reportDupes;
-
+ // Get the old value and CAS token from cache
+ $this->clearLastError();
+ $currentValue = $this->doGet( $key, self::READ_LATEST, $casToken );
if ( $this->getLastError() ) {
$this->logger->warning(
__METHOD__ . ' failed due to I/O error on get() for {key}.',
// Derive the new value from the old value
$value = call_user_func( $callback, $this, $key, $currentValue, $exptime );
+ $hadNoCurrentValue = ( $currentValue === false );
+ unset( $currentValue ); // free RAM in case the value is large
$this->clearLastError();
if ( $value === false ) {
$success = true; // do nothing
- } elseif ( $currentValue === false ) {
+ } elseif ( $hadNoCurrentValue ) {
// Try to create the key, failing if it gets created in the meantime
$success = $this->add( $key, $value, $exptime, $flags );
} else {
}
$curCasToken = null; // passed by reference
- $this->getWithToken( $key, $curCasToken, self::READ_LATEST );
+ $this->doGet( $key, self::READ_LATEST, $curCasToken );
if ( $casToken === $curCasToken ) {
$success = $this->set( $key, $value, $exptime, $flags );
} else {
return $success;
}
- /**
- * @see BagOStuff::merge()
- *
- * @param string $key
- * @param callable $callback Callback method to be executed
- * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
- * @param int $attempts The amount of times to attempt a merge in case of failure
- * @param int $flags Bitfield of BagOStuff::WRITE_* constants
- * @return bool Success
- */
- protected function mergeViaLock( $key, $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
- if ( $attempts <= 1 ) {
- $timeout = 0; // clearly intended to be "non-blocking"
- } else {
- $timeout = 3;
- }
-
- if ( !$this->lock( $key, $timeout ) ) {
- return false;
- }
-
- $this->clearLastError();
- $reportDupes = $this->reportDupes;
- $this->reportDupes = false;
- $currentValue = $this->get( $key, self::READ_LATEST );
- $this->reportDupes = $reportDupes;
-
- if ( $this->getLastError() ) {
- $this->logger->warning(
- __METHOD__ . ' failed due to I/O error on get() for {key}.',
- [ 'key' => $key ]
- );
-
- $success = false;
- } else {
- // Derive the new value from the old value
- $value = call_user_func( $callback, $this, $key, $currentValue, $exptime );
- if ( $value === false ) {
- $success = true; // do nothing
- } else {
- $success = $this->set( $key, $value, $exptime, $flags ); // set the new value
- }
- }
-
- if ( !$this->unlock( $key ) ) {
- // this should never happen
- trigger_error( "Could not release lock for key '$key'." );
- }
-
- return $success;
- }
-
/**
* Change the expiration on a key if it exists
*
return $res;
}
- /**
- * Insertion
- * @param string $key
- * @param mixed $value
- * @param int $exptime
- * @param int $flags Bitfield of BagOStuff::WRITE_* constants (since 1.33)
- * @return bool Success
- */
- abstract public function add( $key, $value, $exptime = 0, $flags = 0 );
-
/**
* Increase stored value of $key by $value while preserving its TTL
* @param string $key Key to increase
}
}
+ /**
+ * @param int $exptime
+ * @return bool
+ */
+ protected function expiryIsRelative( $exptime ) {
+ return ( $exptime != 0 && $exptime < ( 10 * self::TTL_YEAR ) );
+ }
+
/**
* Convert an optionally relative time to an absolute time
* @param int $exptime
* @return int
*/
- protected function convertExpiry( $exptime ) {
- if ( $exptime != 0 && $exptime < ( 10 * self::TTL_YEAR ) ) {
+ protected function convertToExpiry( $exptime ) {
+ if ( $this->expiryIsRelative( $exptime ) ) {
return (int)$this->getCurrentTime() + $exptime;
} else {
return $exptime;