Respect new upcoming feature when calculating TTL of page cache (#17)

Relates: #10506
This commit is contained in:
Daniel Siepmann 2023-05-23 11:46:18 +02:00 committed by GitHub
parent 99ef32a37b
commit 76c1e79ea3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 141 additions and 23 deletions

View file

@ -25,10 +25,12 @@ namespace Wrm\Events\Caching;
use DateTime; use DateTime;
use DateTimeImmutable; use DateTimeImmutable;
use InvalidArgumentException;
use TYPO3\CMS\Core\Cache\CacheManager; use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\SingletonInterface;
use Wrm\Events\Domain\Model\Date;
use Wrm\Events\Events\Controller\DateListVariables; use Wrm\Events\Events\Controller\DateListVariables;
/** /**
@ -42,7 +44,7 @@ class PageCacheTimeout implements SingletonInterface
/** /**
* @var null|DateTimeImmutable * @var null|DateTimeImmutable
*/ */
private $endOfEvent = null; private $timeout = null;
/** /**
* @var FrontendInterface * @var FrontendInterface
@ -78,46 +80,74 @@ class PageCacheTimeout implements SingletonInterface
public function trackDates(DateListVariables $event): void public function trackDates(DateListVariables $event): void
{ {
if ($event->getDemand()->shouldShowFromMidnight()) { if ($event->getDemand()->shouldShowFromMidnight()) {
$this->updateTimeout((new DateTimeImmutable('tomorrow midnight'))); $this->updateTimeout(new DateTimeImmutable('tomorrow midnight'));
return; return;
} }
if ($event->getDemand()->shouldShowUpcoming()) {
$this->trackTimeoutByDate($event, static function (Date $date) {
return $date->getStart();
});
}
$this->trackTimeoutByDate($event, static function (Date $date) {
return $date->getEnd();
});
}
/**
* @param callable $callback Receives Date as argument and should return DateTime to use as potential timeout.
*/
private function trackTimeoutByDate(
DateListVariables $event,
callable $callback
): void {
foreach ($event->getDates() as $date) { foreach ($event->getDates() as $date) {
$endDate = $date->getEnd(); $date = $callback($date);
if (!$endDate instanceof DateTime) { if (!$date instanceof DateTime) {
continue; continue;
} }
$this->updateTimeout(DateTimeImmutable::createFromMutable($endDate)); $this->updateTimeout(DateTimeImmutable::createFromMutable($date));
} }
} }
private function updateTimeout(DateTimeImmutable $end): void private function updateTimeout(DateTimeImmutable $newTimeout): void
{ {
$now = new DateTimeImmutable(); $now = $this->getExecution();
if ( if (
$end <= $now $newTimeout <= $now
|| ( || (
$this->endOfEvent instanceof DateTimeImmutable $this->timeout instanceof DateTimeImmutable
&& $this->endOfEvent >= $end && $this->timeout >= $newTimeout
) )
) { ) {
return; return;
} }
$this->runtimeCache->remove('core-tslib_fe-get_cache_timeout'); $this->runtimeCache->remove('core-tslib_fe-get_cache_timeout');
$this->endOfEvent = $end; $this->timeout = $newTimeout;
} }
private function getTimeout(): ?int private function getTimeout(): ?int
{ {
if (!$this->endOfEvent instanceof DateTimeImmutable) { if (!$this->timeout instanceof DateTimeImmutable) {
return null; return null;
} }
$executionTime = $this->context->getPropertyFromAspect('date', 'timestamp'); return ((int) $this->timeout->format('U')) - ((int) $this->getExecution()->format('U'));
return ((int) $this->endOfEvent->format('U')) - $executionTime; }
private function getExecution(): DateTimeImmutable
{
$execution = $this->context->getPropertyFromAspect('date', 'full');
if (!$execution instanceof DateTimeImmutable) {
throw new InvalidArgumentException('Could not fetch DateTimeImmutable from context.', 1684740576);
}
return $execution;
} }
public static function register(): void public static function register(): void

View file

@ -10,8 +10,15 @@ Features
-------- --------
* Adjust TYPO3 page cache timeout based on rendered dates. * Adjust TYPO3 page cache timeout based on rendered dates.
The end time of each rendered date will be used. Will use one of the following:
The lowest end date will be used to calculate the maximum time to life for the page cache.
- end time of each rendered date
- start time of each rendered date (if upcoming is enabled)
- midnight (if midnight is configured)
The lowest date will be used to calculate the maximum time to life for the page cache.
This is compared to the already calculated time to life. This is compared to the already calculated time to life.
The lower value is then used by TYPO3. The lower value is then used by TYPO3.

View file

@ -0,0 +1,13 @@
.. _features:
Features
=========
The extension provides a couple of features.
The following is an incomplete list of the feature set with further info per feature.
.. toctree::
:glob:
:maxdepth: 1
Features/*

View file

@ -0,0 +1,28 @@
.. _caching:
.. index:: single: caching
Caching
=======
The extension provides out of the box support for proper TYPO3 page caching.
Page Cache
----------
TYPO3 can cache the full page with a TTL (=time to live).
The TTL can be adjusted via configuration and code.
The extension uses the code to lower TTL if necessary.
The TTL of the extension is determined in the following way:
#. Upcoming midnight if midnight should be used.
#. Start of each shown date if upcoming should be used.
#. End of each shown date as fallback.
The corresponding code is ``Wrm\Events\Caching\PageCacheTimeout``.
That way the TTL of each page is not longer than the valid period for shown events,
leading to re-rendering of the page once an event might change.

View file

@ -42,6 +42,7 @@ Table of Contents
:maxdepth: 3 :maxdepth: 3
:titlesonly: :titlesonly:
Features
Commands Commands
Settings Settings
Changelog Changelog

View file

@ -67,7 +67,7 @@ class CacheTest extends AbstractTestCase
/** /**
* @test * @test
*/ */
public function setupReturnsSystemDefaults(): void public function returnsSystemDefaults(): void
{ {
$response = $this->executeFrontendRequest($this->getRequestWithSleep()); $response = $this->executeFrontendRequest($this->getRequestWithSleep());
@ -79,7 +79,7 @@ class CacheTest extends AbstractTestCase
/** /**
* @test * @test
*/ */
public function setupReturnsDefaultsIfEventsEndLater(): void public function returnsDefaultsIfEventsEndLater(): void
{ {
(new PhpDataSet())->import([ (new PhpDataSet())->import([
'tx_events_domain_model_event' => [ 'tx_events_domain_model_event' => [
@ -107,7 +107,7 @@ class CacheTest extends AbstractTestCase
/** /**
* @test * @test
*/ */
public function setupReturnsEarlierIfEventsChangeBeforeSystemDefault(): void public function returnsEarlierIfEventsEndEarlier(): void
{ {
$end = (new DateTimeImmutable('tomorrow midnight', new DateTimeZone('UTC')))->modify('+2 hours'); $end = (new DateTimeImmutable('tomorrow midnight', new DateTimeZone('UTC')))->modify('+2 hours');
@ -138,7 +138,46 @@ class CacheTest extends AbstractTestCase
/** /**
* @test * @test
*/ */
public function setupReturnsMidnightIfConfigured(): void public function returnsEarlierIfStartEndEalierAndIsUpcoming(): void
{
$end = (new DateTimeImmutable('now', new DateTimeZone('UTC')))->modify('+2 hours');
(new PhpDataSet())->import([
'tx_events_domain_model_event' => [
[
'uid' => '1',
'pid' => '2',
'title' => 'Test Event 1',
],
],
'tx_events_domain_model_date' => [
[
'pid' => '2',
'event' => '1',
'start' => $end->format('U'),
'end' => '0',
],
],
]);
$response = $this->executeFrontendRequest($this->getRequestWithSleep([
'plugin.' => [
'tx_events.' => [
'settings.' => [
'upcoming' => 1,
],
],
],
]));
self::assertSame(200, $response->getStatusCode());
self::assertCacheHeaders($end, $response);
}
/**
* @test
*/
public function returnsMidnightIfConfigured(): void
{ {
$midnight = (new DateTimeImmutable('tomorrow midnight', new DateTimeZone('UTC'))); $midnight = (new DateTimeImmutable('tomorrow midnight', new DateTimeZone('UTC')));
@ -207,13 +246,13 @@ class CacheTest extends AbstractTestCase
]; ];
} }
private function getRequestWithSleep(): InternalRequest private function getRequestWithSleep(array $typoScript = []): InternalRequest
{ {
$request = new InternalRequest(); $request = new InternalRequest();
$request = $request->withPageId(1); $request = $request->withPageId(1);
$request = $request->withInstructions([ $request = $request->withInstructions([
$this->getTypoScriptInstruction() $this->getTypoScriptInstruction()
->withTypoScript([ ->withTypoScript(array_merge_recursive($typoScript, [
'page.' => [ 'page.' => [
'30.' => [ '30.' => [
'userFunc.' => [ 'userFunc.' => [
@ -221,7 +260,7 @@ class CacheTest extends AbstractTestCase
], ],
], ],
], ],
]) ]))
]); ]);
return $request; return $request;