*
* The simplest purge method is delete().
*
- * There are two supported ways to handle broadcasted operations:
+ * There are three supported ways to handle broadcasted operations:
* - a) Configure the 'purge' EventRelayer to point to a valid PubSub endpoint
- * that has subscribed listeners on the cache servers applying the cache updates.
+ * that has subscribed listeners on the cache servers applying the cache updates.
* - b) Ignore the 'purge' EventRelayer configuration (default is NullEventRelayer)
- * and set up mcrouter as the underlying cache backend, using one of the memcached
- * BagOStuff classes as 'cache'. Use OperationSelectorRoute in the mcrouter settings
- * to configure 'set' and 'delete' operations to go to all DCs via AllAsyncRoute and
- * configure other operations to go to the local DC via PoolRoute (for reference,
- * see https://github.com/facebook/mcrouter/wiki/List-of-Route-Handles).
+ * and set up mcrouter as the underlying cache backend, using one of the memcached
+ * BagOStuff classes as 'cache'. Use OperationSelectorRoute in the mcrouter settings
+ * to configure 'set' and 'delete' operations to go to all DCs via AllAsyncRoute and
+ * configure other operations to go to the local DC via PoolRoute (for reference,
+ * see https://github.com/facebook/mcrouter/wiki/List-of-Route-Handles).
+ * - c) Ignore the 'purge' EventRelayer configuration (default is NullEventRelayer)
+ * and set up dynomite as cache middleware between the web servers and either
+ * memcached or redis. This will also broadcast all key setting operations, not just purges,
+ * which can be useful for cache warming. Writes are eventually consistent via the
+ * Dynamo replication model (see https://github.com/Netflix/dynomite).
*
* Broadcasted operations like delete() and touchCheckKey() are done asynchronously
* in all datacenters this way, though the local one should likely be near immediate.
return $values;
}
+ /**
+ * Locally set a key to expire soon if it is stale based on $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.
+ *
+ * @param string $key Cache key
+ * @param int $purgeTimestamp UNIX timestamp of purge
+ * @param bool &$isStale Whether the key is stale
+ * @return bool Success
+ * @since 1.28
+ */
+ public function reap( $key, $purgeTimestamp, &$isStale = false ) {
+ $minAsOf = $purgeTimestamp + self::HOLDOFF_TTL;
+ $wrapped = $this->cache->get( self::VALUE_KEY_PREFIX . $key );
+ if ( is_array( $wrapped ) && $wrapped[self::FLD_TIME] < $minAsOf ) {
+ $isStale = true;
+ $this->logger->warning( "Reaping stale value key '$key'." );
+ $ttlReap = self::HOLDOFF_TTL; // avoids races with tombstone creation
+ $ok = $this->cache->changeTTL( self::VALUE_KEY_PREFIX . $key, $ttlReap );
+ if ( !$ok ) {
+ $this->logger->error( "Could not complete reap of key '$key'." );
+ }
+
+ return $ok;
+ }
+
+ $isStale = false;
+
+ return true;
+ }
+
+ /**
+ * Locally set a "check" key to expire soon if it is stale based on $purgeTimestamp
+ *
+ * @param string $key Cache key
+ * @param int $purgeTimestamp UNIX timestamp of purge
+ * @param bool &$isStale Whether the key is stale
+ * @return bool Success
+ * @since 1.28
+ */
+ public function reapCheckKey( $key, $purgeTimestamp, &$isStale = false ) {
+ $purge = $this->parsePurgeValue( $this->cache->get( self::TIME_KEY_PREFIX . $key ) );
+ 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 );
+ if ( !$ok ) {
+ $this->logger->error( "Could not complete reap of check key '$key'." );
+ }
+
+ return $ok;
+ }
+
+ $isStale = false;
+
+ return false;
+ }
+
/**
* @see BagOStuff::makeKey()
* @param string ... Key component