/** @var int Key fetched */
private $warmupKeyMisses = 0;
+ /** @var float|null */
+ private $wallClockOverride;
+
/** Max time expected to pass between delete() and DB commit finishing */
const MAX_COMMIT_DELAY = 3;
/** Max replication+snapshot lag before applying TTL_LAGGED or disallowing set() */
const LOCK_TTL = 10;
/** Default remaining TTL at which to consider pre-emptive regeneration */
const LOW_TTL = 30;
- /** Default time-since-expiry on a miss that makes a key "hot" */
- const LOCK_TSE = 1;
/** Never consider performing "popularity" refreshes until a key reaches this age */
const AGE_NEW = 60;
* (e.g. the default REPEATABLE-READ in innoDB). Even for mutable data, that
* isolation can largely be maintained by doing the following:
* - a) Calling delete() on entity change *and* creation, before DB commit
- * - b) Keeping transaction duration shorter than delete() hold-off TTL
+ * - b) Keeping transaction duration shorter than the delete() hold-off TTL
+ * - c) Disabling interim key caching via useInterimHoldOffCaching() before get() calls
*
* However, pre-snapshot values might still be seen if an update was made
* in a remote datacenter but the purge from delete() didn't relay yet.
// Case B: any long-running transaction; ignore this set()
} elseif ( $age > self::MAX_READ_LAG ) {
$this->logger->info( 'Rejected set() for {cachekey} due to snapshot lag.',
- [ 'cachekey' => $key ] );
+ [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] );
return true; // no-op the write for being unsafe
// Case C: high replication lag; lower TTL instead of ignoring all set()s
} elseif ( $lag === false || $lag > self::MAX_READ_LAG ) {
$ttl = $ttl ? min( $ttl, self::TTL_LAGGED ) : self::TTL_LAGGED;
$this->logger->warning( 'Lowered set() TTL for {cachekey} due to replication lag.',
- [ 'cachekey' => $key ] );
+ [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] );
// Case D: medium length request with medium replication lag; ignore this set()
} else {
$this->logger->info( 'Rejected set() for {cachekey} due to high read lag.',
- [ 'cachekey' => $key ] );
+ [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] );
return true; // no-op the write for being unsafe
}
}
/**
- * Locally set a key to expire soon if it is stale based on $purgeTimestamp
+ * Set a key to soon expire in the local cluster if it pre-dates $purgeTimestamp
*
* This sets stale keys' time-to-live at HOLDOFF_TTL seconds, which both avoids
* broadcasting in mcrouter setups and also avoids races with new tombstones.
}
/**
- * Locally set a "check" key to expire soon if it is stale based on $purgeTimestamp
+ * Set a "check" key to soon expire in the local cluster if it pre-dates $purgeTimestamp
*
* @param string $key Cache key
* @param int $purgeTimestamp UNIX timestamp of purge
if ( $purge && $purge[self::FLD_TIME] < $purgeTimestamp ) {
$isStale = true;
$this->logger->warning( "Reaping stale check key '$key'." );
- $ok = $this->cache->changeTTL( self::TIME_KEY_PREFIX . $key, 1 );
+ $ok = $this->cache->changeTTL( self::TIME_KEY_PREFIX . $key, self::TTL_SECOND );
if ( !$ok ) {
$this->logger->error( "Could not complete reap of check key '$key'." );
}
'cmd' => 'set',
'key' => $key,
'val' => 'PURGED:$UNIXTIME$:' . (int)$holdoff,
- 'ttl' => max( $ttl, 1 ),
+ 'ttl' => max( $ttl, self::TTL_SECOND ),
'sbt' => true, // substitute $UNIXTIME$ with actual microtime
] );
return isset( $parts[1] ) ? $parts[1] : $parts[0]; // sanity
}
- /**
- * @return float UNIX timestamp
- * @codeCoverageIgnore
- */
- protected function getCurrentTime() {
- return microtime( true );
- }
-
/**
* @param string $value Wrapped value like "PURGED:<timestamp>:<holdoff>"
* @return array|bool Array containing a UNIX timestamp (float) and holdoff period (integer),
return $warmupCache;
}
+
+ /**
+ * @return float UNIX timestamp
+ * @codeCoverageIgnore
+ */
+ protected function getCurrentTime() {
+ return $this->wallClockOverride ?: microtime( true );
+ }
+
+ /**
+ * @param float|null &$time Mock UNIX timestamp for testing
+ * @codeCoverageIgnore
+ */
+ public function setMockTime( &$time ) {
+ $this->wallClockOverride =& $time;
+ $this->cache->setMockTime( $time );
+ }
}