abstract class BagOStuff {
private $debugMode = false;
+ protected $lastError = self::ERR_NONE;
+
+ /** Possible values for getLastError() */
+ const ERR_NONE = 0; // no error
+ const ERR_NO_RESPONSE = 1; // no response
+ const ERR_UNREACHABLE = 2; // can't connect
+ const ERR_UNEXPECTED = 3; // response gave some error
+
/**
* @param $bool bool
*/
* @return bool success
*/
public function lock( $key, $timeout = 6 ) {
+ $this->clearLastError();
$timestamp = microtime( true ); // starting UNIX timestamp
if ( $this->add( "{$key}:lock", 1, $timeout ) ) {
return true;
+ } elseif ( $this->getLastError() ) {
+ return false;
}
$uRTT = ceil( 1e6 * ( microtime( true ) - $timestamp ) ); // estimate RTT (us)
$sleep *= 2;
}
usleep( $sleep ); // back off
+ $this->clearLastError();
$locked = $this->add( "{$key}:lock", 1, $timeout );
+ if ( $this->getLastError() ) {
+ return false;
+ }
} while ( !$locked );
return $locked;
return $this->incr( $key, - $value );
}
+ /**
+ * Get the "last error" registered; clearLastError() should be called manually
+ * @return integer ERR_* constant for the "last error" registry
+ * @since 1.23
+ */
+ public function getLastError() {
+ return $this->lastError;
+ }
+
+ /**
+ * Clear the "last error" registry
+ * @since 1.23
+ */
+ public function clearLastError() {
+ $this->lastError = self::ERR_NONE;
+ }
+
+ /**
+ * Set the "last error" registry
+ * @param $err integer ERR_* constant
+ * @since 1.23
+ */
+ protected function setLastError( $err ) {
+ $this->lastError = $err;
+ }
+
/**
* @param $text string
*/
return $this->doWrite( 'merge', $key, $callback, $exptime );
}
+ public function getLastError() {
+ return isset( $this->caches[0] ) ? $this->caches[0]->getLastError() : self::ERR_NONE;
+ }
+
+ public function clearLastError() {
+ if ( isset( $this->caches[0] ) ) {
+ $this->caches[0]->clearLastError();
+ }
+ }
+
/**
* @param $method string
* @return bool
return array( $server, $conn );
}
}
+ $this->setLastError( BagOStuff::ERR_UNREACHABLE );
return array( false, false );
}
* object and let it be reopened during the next request.
*/
protected function handleException( RedisConnRef $conn, $e ) {
+ $this->setLastError( BagOStuff::ERR_UNEXPECTED );
$this->redisPool->handleError( $conn, $e );
}
}
wfDebugLog( 'SQLBagOStuff', "DBError: {$exception->getMessage()}" );
if ( $exception instanceof DBConnectionError ) {
+ $this->setLastError( BagOStuff::ERR_UNREACHABLE );
wfDebug( __METHOD__ . ": ignoring connection error\n" );
} else {
+ $this->setLastError( BagOStuff::ERR_UNEXPECTED );
wfDebug( __METHOD__ . ": ignoring query error\n" );
}
}
}
wfDebugLog( 'SQLBagOStuff', "DBError: {$exception->getMessage()}" );
if ( $exception instanceof DBConnectionError ) {
+ $this->setLastError( BagOStuff::ERR_UNREACHABLE );
wfDebug( __METHOD__ . ": ignoring connection error\n" );
} else {
+ $this->setLastError( BagOStuff::ERR_UNEXPECTED );
wfDebug( __METHOD__ . ": ignoring query error\n" );
}
}