|
| 1 | +# Migrating from WoltLab Suite 6.1 - Tolerant and Eager Caching |
| 2 | + |
| 3 | +In the upcoming version of WoltLab Suite, the caching system has been reworked to provide a more flexible and efficient way to cache data. |
| 4 | + |
| 5 | +The previous implementation had three distinct shortcomings: |
| 6 | + |
| 7 | +- Resetting a cache causes the next request that needs this cache to trigger a synchronous rebuild. |
| 8 | +- Non-critical caches can sometimes be expensive to generate, causing dips in response times. |
| 9 | +- The same rebuild can take place simultaneously by concurrent requests. |
| 10 | + |
| 11 | +The new caching systems solves this moving the any request for a cache rebuild into the request that triggered the cache invalidation. |
| 12 | + |
| 13 | +This will add the burden to rebuild these caches onto the current request which is usually an (in comparison) expensive request anyway. |
| 14 | +The idea behind this is that adding a few miliseconds to a request that already takes half a second makes no difference to the user. |
| 15 | +On the other hand, accessing a page and suddenly have a significantly longer loading time is unexpected to the visitor. |
| 16 | + |
| 17 | +Performing a full cache reset will still have the same latency impact as before. |
| 18 | + |
| 19 | +## General Guidelines |
| 20 | + |
| 21 | +- MUST NOT rely on any (runtime) cache. |
| 22 | +- Return a `CacheData` object instead of an `array`. |
| 23 | +- Parametrized caches are supported through `readonly` properties in the constructor. |
| 24 | + ```php |
| 25 | + namespace wcf\system\cache\tolerant; |
| 26 | + |
| 27 | + final class FooCache extends AbstractTolerantCache { |
| 28 | + public function __construct( |
| 29 | + public readonly int $categoryID |
| 30 | + ) {} |
| 31 | + // Additional methods … |
| 32 | + } |
| 33 | + ``` |
| 34 | + |
| 35 | +## Eager Caches |
| 36 | + |
| 37 | +The eager cache has no lifetime and must be rebuild manually by the developer if the data changes. |
| 38 | +An eager cache is guaranteed to be always present, either by fetching the cached data or by rebuilding it synchronously. |
| 39 | + |
| 40 | +```php |
| 41 | +(new FooCache())->rebuild() |
| 42 | +``` |
| 43 | + |
| 44 | +#### Example |
| 45 | + |
| 46 | +```php |
| 47 | +namespace wcf\system\cache\eager; |
| 48 | + |
| 49 | +use wcf\system\cache\eager\data\FooCacheData; |
| 50 | + |
| 51 | +/** |
| 52 | +* @extends AbstractEagerCache<FooCacheData> |
| 53 | +*/ |
| 54 | +final class FooCache extends AbstractEagerCache |
| 55 | +{ |
| 56 | + public function __construct( |
| 57 | + public readonly bool $snafucated, |
| 58 | + ) {} |
| 59 | + |
| 60 | + #[\Override] |
| 61 | + protected function getCacheData(): FooCacheData |
| 62 | + { |
| 63 | + // Load and return the data here … |
| 64 | + } |
| 65 | +} |
| 66 | +``` |
| 67 | + |
| 68 | +Parameters for a stateful cache are passed through the constructor and are required to be marked as `readonly`. |
| 69 | + |
| 70 | +```php |
| 71 | +$cache = (new FooCache(true))->getCache(); |
| 72 | +``` |
| 73 | + |
| 74 | +## Tolerant Caches |
| 75 | + |
| 76 | +A tolerant cache is similar to an eager cache but has a maximum lifetime after which it is queued up for a rebuild. |
| 77 | +Similar to the eager cache, it is also guaranteed to exist when queried, serving either cached data or rebuilding it synchronously. |
| 78 | + |
| 79 | +The main difference is that the tolerant cache is permitted to serve stale content. For example, a cache is set a lifetime of 300 seconds but querying it 307 seconds after its creation may still return the old data. |
| 80 | +The cache content will be refreshed eventually, but callees must not expect the data to be of a precise age. |
| 81 | + |
| 82 | +The cache is updated by a background job when the lifetime expires or by a [probabilistic early expiration](https://en.wikipedia.org/wiki/Cache_stampede#Probabilistic_early_expiration). |
| 83 | +The early expiration will randomly queue a tolerant cache for a rebuild before it exceeds its maximum lifetime. |
| 84 | +The odds of an early rebuild increases significantly the less time is remaining, making it very likely that a tolerant does not become stale. |
| 85 | + |
| 86 | +#### Example |
| 87 | + |
| 88 | +```php |
| 89 | +namespace wcf\system\cache\tolerant; |
| 90 | + |
| 91 | +use wcf\system\cache\tolerant\data\BarCacheData; |
| 92 | + |
| 93 | +/** |
| 94 | +* @extends AbstractTolerantCache<BarCacheData> |
| 95 | +*/ |
| 96 | +final class BarCache extends AbstractTolerantCache |
| 97 | +{ |
| 98 | + #[\Override] |
| 99 | + public function getLifetime(): int |
| 100 | + { |
| 101 | + // 3,600 seconds = 1 hour |
| 102 | + return 3_600; |
| 103 | + } |
| 104 | + |
| 105 | + #[\Override] |
| 106 | + protected function rebuildCacheData(): BarCacheData |
| 107 | + { |
| 108 | + // Load and return the data here … |
| 109 | + } |
| 110 | +} |
| 111 | +``` |
| 112 | + |
| 113 | +This cache can be used as follows: |
| 114 | + |
| 115 | +```php |
| 116 | +$cache = (new BarCache())->getCache(); |
| 117 | +``` |
0 commit comments