* @ingroup Cache
*/
class SqlBagOStuff extends BagOStuff {
- /** @var LoadBalancer */
- protected $lb;
-
/** @var array */
protected $serverInfos;
-
/** @var array */
protected $serverNames;
-
/** @var int */
protected $numServers;
-
- /** @var array */
- protected $conns;
-
/** @var int */
protected $lastExpireAll = 0;
-
/** @var int */
protected $purgePeriod = 100;
-
/** @var int */
protected $shards = 1;
-
/** @var string */
protected $tableName = 'objectcache';
-
/** @var bool */
protected $slaveOnly = false;
+ /** @var int */
+ protected $syncTimeout = 3;
+ /** @var array */
+ protected $conns;
/** @var array UNIX timestamps */
protected $connFailureTimes = array();
-
/** @var array Exceptions */
protected $connFailureErrors = array();
* garbage collection logic of expired items. This only
* makes sense if the primary DB is used and only if get()
* calls will be used. This is used by ReplicatedBagOStuff.
+ * - syncTimeout: Max seconds to wait for slaves to catch up for WRITE_SYNC.
*
* @param array $params
*/
if ( isset( $params['shards'] ) ) {
$this->shards = intval( $params['shards'] );
}
+ if ( isset( $params['syncTimeout'] ) ) {
+ $this->syncTimeout = $params['syncTimeout'];
+ }
$this->slaveOnly = !empty( $params['slaveOnly'] );
}
$db = DatabaseBase::factory( $type, $info );
$db->clearFlag( DBO_TRX );
} else {
- /*
- * We must keep a separate connection to MySQL in order to avoid deadlocks
- * However, SQLite has an opposite behavior. And PostgreSQL needs to know
- * if we are in transaction or no
- */
+ // We must keep a separate connection to MySQL in order to avoid deadlocks
+ // However, SQLite has an opposite behavior. And PostgreSQL needs to know
+ // if we are in transaction or not (@TODO: find some work-around).
$index = $this->slaveOnly ? DB_SLAVE : DB_MASTER;
if ( wfGetDB( $index )->getType() == 'mysql' ) {
- $this->lb = wfGetLBFactory()->newMainLB();
- $db = $this->lb->getConnection( $index );
+ $lb = wfGetLBFactory()->newMainLB();
+ $db = $lb->getConnection( $index );
$db->clearFlag( DBO_TRX ); // auto-commit mode
} else {
$db = wfGetDB( $index );
}
}
- public function get( $key, &$casToken = null, $flags = 0 ) {
+ protected function doGet( $key, $flags = 0 ) {
+ $casToken = null;
+
+ return $this->getWithToken( $key, $casToken, $flags );
+ }
+
+ protected function getWithToken( $key, &$casToken, $flags = 0 ) {
$values = $this->getMulti( array( $key ) );
if ( array_key_exists( $key, $values ) ) {
$casToken = $values[$key];
return $values;
}
- /**
- * @param array $data
- * @param int $expiry
- * @return bool
- */
public function setMulti( array $data, $expiry = 0 ) {
$keysByTable = array();
foreach ( $data as $key => $value ) {
return $result;
}
- /**
- * @param string $key
- * @param mixed $value
- * @param int $exptime
- * @return bool
- */
- public function set( $key, $value, $exptime = 0 ) {
- return $this->setMulti( array( $key => $value ), $exptime );
+ public function set( $key, $value, $exptime = 0, $flags = 0 ) {
+ $ok = $this->setMulti( array( $key => $value ), $exptime );
+ if ( ( $flags & self::WRITE_SYNC ) == self::WRITE_SYNC ) {
+ $ok = $ok && $this->waitForSlaves();
+ }
+
+ return $ok;
}
- /**
- * @param mixed $casToken
- * @param string $key
- * @param mixed $value
- * @param int $exptime
- * @return bool
- */
protected function cas( $casToken, $key, $value, $exptime = 0 ) {
list( $serverIndex, $tableName ) = $this->getTableByKey( $key );
try {
return (bool)$db->affectedRows();
}
- /**
- * @param string $key
- * @return bool
- */
public function delete( $key ) {
list( $serverIndex, $tableName ) = $this->getTableByKey( $key );
try {
return true;
}
- /**
- * @param string $key
- * @param int $step
- * @return int|null
- */
public function incr( $key, $step = 1 ) {
list( $serverIndex, $tableName ) = $this->getTableByKey( $key );
try {
return $newValue;
}
- public function merge( $key, $callback, $exptime = 0, $attempts = 10 ) {
+ public function merge( $key, $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
if ( !is_callable( $callback ) ) {
throw new Exception( "Got invalid callback." );
}
- return $this->mergeViaCas( $key, $callback, $exptime, $attempts );
+ $ok = $this->mergeViaCas( $key, $callback, $exptime, $attempts );
+ if ( ( $flags & self::WRITE_SYNC ) == self::WRITE_SYNC ) {
+ $ok = $ok && $this->waitForSlaves();
+ }
+
+ return $ok;
}
/**
}
}
}
+
+ protected function waitForSlaves() {
+ if ( !$this->serverInfos ) {
+ // Main LB is used; wait for any slaves to catch up
+ return wfWaitForSlaves( null, false, false, $this->syncTimeout );
+ } else {
+ // Custom DB server list; probably doesn't use replication
+ return true;
+ }
+ }
}