/** @var int Seconds to store positions */
const POSITION_TTL = 60;
+ /** @var int Seconds to store position write index cookies (safely less than POSITION_TTL) */
+ const POSITION_COOKIE_TTL = 60;
/** @var int Max time to wait for positions to appear */
const POS_STORE_WAIT_TIMEOUT = 5;
implode( ', ', array_keys( $this->shutdownPositions ) ) . "\n"
);
- // CP-protected writes should overwhemingly go to the master datacenter, so get DC-local
- // lock to merge the values. Use a DC-local get() and a synchronous all-DC set(). This
- // makes it possible for the BagOStuff class to write in parallel to all DCs with one RTT.
+ // CP-protected writes should overwhelmingly go to the master datacenter, so use a
+ // DC-local lock to merge the values. Use a DC-local get() and a synchronous all-DC
+ // set(). This makes it possible for the BagOStuff class to write in parallel to all
+ // DCs with one RTT. The use of WRITE_SYNC avoids needing READ_LATEST for the get().
if ( $store->lock( $this->key, 3 ) ) {
if ( $workCallback ) {
- // Let the store run the work before blocking on a replication sync barrier. By the
- // time it's done with the work, the barrier should be fast if replication caught up.
+ // Let the store run the work before blocking on a replication sync barrier.
+ // If replication caught up while the work finished, the barrier will be fast.
$store->addBusyCallback( $workCallback );
}
$ok = $store->set(
/**
* @see ILBFactory::newMainLB()
* @param bool $domain
- * @return LoadBalancer
+ * @return ILoadBalancer
*/
abstract public function newMainLB( $domain = false );
/**
* @see ILBFactory::getMainLB()
* @param bool $domain
- * @return LoadBalancer
+ * @return ILoadBalancer
*/
abstract public function getMainLB( $domain = false );
/**
* @see ILBFactory::newExternalLB()
* @param string $cluster
- * @return LoadBalancer
+ * @return ILoadBalancer
*/
abstract public function newExternalLB( $cluster );
/**
* @see ILBFactory::getExternalLB()
* @param string $cluster
- * @return LoadBalancer
+ * @return ILoadBalancer
*/
abstract public function getExternalLB( $cluster );
}
/**
- * Base parameters to LoadBalancer::__construct()
+ * Base parameters to ILoadBalancer::__construct()
* @return array
*/
final protected function baseLoadBalancerParams() {
return strpos( $url, '?' ) === false ? "$url?cpPosIndex=$index" : "$url&cpPosIndex=$index";
}
+ /**
+ * @param int $index Write index
+ * @param int $time UNIX timestamp
+ * @return string Timestamp-qualified write index of the form "<index>.<timestamp>"
+ * @since 1.32
+ */
+ public static function makeCookieValueFromCPIndex( $index, $time ) {
+ return $index . '@' . $time;
+ }
+
+ /**
+ * @param string $value String possibly of the form "<index>" or "<index>@<timestamp>"
+ * @param int $minTimestamp Lowest UNIX timestamp of non-expired values (if present)
+ * @return int|null Write index or null if $value is empty or expired
+ * @since 1.32
+ */
+ public static function getCPIndexFromCookieValue( $value, $minTimestamp ) {
+ if ( !preg_match( '/^(\d+)(?:@(\d+))?$/', $value, $m ) ) {
+ return null;
+ }
+
+ $index = (int)$m[1];
+
+ if ( isset( $m[2] ) && $m[2] !== '' && (int)$m[2] < $minTimestamp ) {
+ return null; // expired
+ }
+
+ return ( $index > 0 ) ? $index : null;
+ }
+
public function setRequestInfo( array $info ) {
if ( $this->chronProt ) {
throw new LogicException( 'ChronologyProtector already initialized.' );