27use InvalidArgumentException;
33use Psr\Log\LoggerInterface;
95 LoggerInterface $logger,
99 $this->cache = $cache;
100 $this->cacheExpiry = $cacheExpiry;
101 $this->cacheEpoch = $cacheEpoch;
102 $this->jsonCodec = $jsonCodec;
103 $this->stats = $stats;
104 $this->logger = $logger;
105 $this->globalIdGenerator = $globalIdGenerator;
112 private function incrementStats(
string $status,
string $reason =
null ) {
113 $metricSuffix = $reason ?
"{$status}_{$reason}" : $status;
115 $this->stats->getCounter(
'RevisionOutputCache_operation_total' )
116 ->setLabel(
'name', $this->name )
117 ->setLabel(
'status', $status )
118 ->setLabel(
'reason', $reason ?:
'n/a' )
119 ->copyToStatsdAt(
"RevisionOutputCache.{$this->name}.{$metricSuffix}" )
144 array $usedOptions =
null
148 $revId = $revision->
getId();
151 throw new InvalidArgumentException(
"Revision must have an id number" );
154 return $this->cache->makeKey( $this->name, $revId, $hash );
177 array $usedOptions =
null
182 $revId = (string)$revision->
getId();
184 return $this->cache->makeKey( $this->name, $revId, $hash );
197 if ( $this->cacheExpiry <= 0 ) {
202 if ( !$parserOptions->isSafeToCache() ) {
203 $this->incrementStats(
'miss',
'unsafe' );
207 $cacheKey = $this->makeParserOutputKey( $revision, $parserOptions );
208 $json = $this->cache->get( $cacheKey );
210 if ( $json ===
false ) {
211 $this->incrementStats(
'miss',
'absent' );
215 $output = $this->restoreFromJson( $json, $cacheKey, ParserOutput::class );
216 if ( $output ===
null ) {
217 $this->incrementStats(
'miss',
'unserialize' );
221 $cacheTime = (int)MWTimestamp::convert( TS_UNIX, $output->getCacheTime() );
222 $expiryTime = (int)MWTimestamp::convert( TS_UNIX, $this->cacheEpoch );
223 $expiryTime = max( $expiryTime, (
int)MWTimestamp::now( TS_UNIX ) - $this->cacheExpiry );
225 if ( $cacheTime < $expiryTime ) {
226 $this->incrementStats(
'miss',
'expired' );
230 $this->logger->debug(
'old-revision cache hit' );
231 $this->incrementStats(
'hit' );
245 string $cacheTime =
null
248 throw new InvalidArgumentException(
'Attempt to cache a ParserOutput with no text set!' );
251 if ( $this->cacheExpiry <= 0 ) {
256 $cacheKey = $this->makeParserOutputKey( $revision, $parserOptions );
271 $output->
setRenderId( $this->globalIdGenerator->newUUIDv1() );
277 $msg =
"Saved in RevisionOutputCache with key $cacheKey" .
278 " and timestamp $cacheTime" .
279 " and revision id {$revision->getId()}.";
287 if ( $expiry <= 0 ) {
288 $this->incrementStats(
'save',
'uncacheable' );
293 $this->incrementStats(
'save',
'unsafe' );
297 $json = $this->encodeAsJson( $output, $cacheKey );
298 if ( $json ===
null ) {
299 $this->incrementStats(
'save',
'nonserializable' );
303 $this->cache->set( $cacheKey, $json, $expiry );
304 $this->incrementStats(
'save',
'success' );
313 private function restoreFromJson(
string $jsonData,
string $key,
string $expectedClass ) {
316 $obj = $this->jsonCodec->unserialize( $jsonData, $expectedClass );
318 }
catch ( JsonException $e ) {
319 $this->logger->error(
'Unable to unserialize JSON', [
320 'name' => $this->name,
322 'message' => $e->getMessage()
333 private function encodeAsJson(
CacheTime $obj,
string $key ) {
335 return $this->jsonCodec->serialize( $obj );
336 }
catch ( JsonException $e ) {
337 $this->logger->error(
'Unable to serialize JSON', [
338 'name' => $this->name,
340 'message' => $e->getMessage(),
Parser cache specific expiry check.
updateCacheExpiry( $seconds)
Sets the number of seconds after which this object should expire.
setCacheTime( $t)
setCacheTime() sets the timestamp expressing when the page has been rendered.
getCacheExpiry()
Returns the number of seconds after which this object should expire.
Set options of the Parser.
isSafeToCache(array $usedOptions=null)
Test whether these options are safe to cache.
optionsHash( $forOptions, $title=null)
Generate a hash string with the values set on these ParserOptions for the keys given in the array.
Multi-datacenter aware caching interface.