Skip to content

Commit b2f59f9

Browse files
committed
Queue a background job to refresh the tolerant cache if it is not expired
1 parent b085d19 commit b2f59f9

File tree

5 files changed

+86
-110
lines changed

5 files changed

+86
-110
lines changed

wcfsetup/install/files/lib/system/background/job/TolerantCacheRebuildBackgroundJob.class.php

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace wcf\system\background\job;
44

5+
use Symfony\Component\Cache\CacheItem;
6+
use Symfony\Contracts\Cache\ItemInterface;
57
use wcf\system\cache\CacheHandler;
68
use wcf\system\cache\tolerant\AbstractTolerantCache;
79

@@ -13,12 +15,17 @@
1315
*/
1416
final class TolerantCacheRebuildBackgroundJob extends AbstractUniqueBackgroundJob
1517
{
18+
private readonly ItemInterface $item;
1619
public function __construct(
20+
/** @var ItemInterface $item */
21+
ItemInterface $item,
1722
/** @var class-string<AbstractTolerantCache<array|object> */
1823
public readonly string $cacheClass,
1924
/** @var array<string, mixed> */
20-
public readonly array $parameters = []
25+
public readonly array $parameters = [],
2126
) {
27+
$this->item = clone $item;
28+
$this->item->set(null);
2229
}
2330

2431
public function identifier(): string
@@ -34,7 +41,7 @@ public function identifier(): string
3441
#[\Override]
3542
public function newInstance(): static
3643
{
37-
return new TolerantCacheRebuildBackgroundJob($this->cacheClass, $this->parameters);
44+
return new TolerantCacheRebuildBackgroundJob($this->item, $this->cacheClass, $this->parameters);
3845
}
3946

4047
#[\Override]
@@ -50,11 +57,30 @@ public function perform()
5057
return;
5158
}
5259

53-
$asyncCache = new $this->cacheClass(...$this->parameters);
54-
if (!$asyncCache->needsRebuild()) {
55-
return;
56-
}
60+
// @see https://github.com/symfony/symfony/blob/7.2/src/Symfony/Component/Cache/Messenger/EarlyExpirationHandler.php
61+
62+
$startTime = microtime(true);
63+
64+
$tolerantCache = new $this->cacheClass(...$this->parameters);
65+
66+
$save = true;
67+
$value = ($tolerantCache)($this->item, $save);
68+
69+
static $setMetadata;
70+
71+
$setMetadata ??= \Closure::bind(
72+
function (CacheItem $item, float $startTime) {
73+
if ($item->expiry > $endTime = microtime(true)) {
74+
$item->newMetadata[ItemInterface::METADATA_EXPIRY] = $item->expiry;
75+
$item->newMetadata[ItemInterface::METADATA_CTIME] = (int)ceil(1000 * ($endTime - $startTime));
76+
}
77+
},
78+
null,
79+
CacheItem::class
80+
);
81+
82+
$setMetadata($this->item, $startTime);
5783

58-
$asyncCache->rebuild();
84+
CacheHandler::getInstance()->getCacheAdapter()->save($this->item->set($value));
5985
}
6086
}

wcfsetup/install/files/lib/system/cache/ProxyAdapter.class.php

Lines changed: 0 additions & 53 deletions
This file was deleted.

wcfsetup/install/files/lib/system/cache/eager/AbstractEagerCache.class.php

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use wcf\system\cache\builder\AbstractCacheBuilder;
66
use wcf\system\cache\CacheHandler;
77
use wcf\system\cache\ICacheCallback;
8+
use wcf\util\ClassUtil;
89

910
/**
1011
* @author Olaf Braun
@@ -53,19 +54,7 @@ private function getCacheKey(): string
5354
\get_class($this)
5455
);
5556

56-
$parameters = [];
57-
foreach ($reflection->getProperties(\ReflectionProperty::IS_READONLY) as $property) {
58-
if (!$property->isInitialized($this)) {
59-
continue;
60-
}
61-
62-
$value = $property->getValue($this);
63-
if ($value === null) {
64-
continue;
65-
}
66-
67-
$parameters[$property->getName()] = $value;
68-
}
57+
$parameters = ClassUtil::getObjectProperties($this, \ReflectionProperty::IS_READONLY);
6958

7059
if ($parameters !== []) {
7160
$this->cacheName .= '-' . CacheHandler::getInstance()->getCacheIndex($parameters);

wcfsetup/install/files/lib/system/cache/tolerant/AbstractTolerantCache.class.php

Lines changed: 24 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
namespace wcf\system\cache\tolerant;
44

5-
use Symfony\Contracts\Cache\CacheInterface;
5+
use Symfony\Contracts\Cache\ItemInterface;
6+
use wcf\system\background\BackgroundQueueHandler;
7+
use wcf\system\background\job\TolerantCacheRebuildBackgroundJob;
68
use wcf\system\cache\CacheHandler;
79
use wcf\system\cache\ICacheCallback;
8-
use wcf\system\cache\ProxyAdapter;
10+
use wcf\util\ClassUtil;
911

1012
/**
1113
* @author Olaf Braun
@@ -30,9 +32,26 @@ abstract class AbstractTolerantCache implements ICacheCallback
3032
final public function get(): array|object
3133
{
3234
if (!isset($this->cache)) {
33-
$this->cache = AbstractTolerantCache::getCacheAdapter()->get(
35+
$this->cache = CacheHandler::getInstance()->getCacheAdapter()->get(
3436
$this->getCacheKey(),
35-
$this,
37+
function (ItemInterface $item, bool &$save) {
38+
if (!$item->isHit()) {
39+
return ($this)($item);
40+
}
41+
42+
BackgroundQueueHandler::getInstance()->enqueueIn(
43+
new TolerantCacheRebuildBackgroundJob(
44+
$item,
45+
\get_class($this),
46+
ClassUtil::getObjectProperties($this, \ReflectionProperty::IS_READONLY)
47+
)
48+
);
49+
50+
$save = false;
51+
52+
return $item->get();
53+
},
54+
5500.0
3655
);
3756
}
3857

@@ -49,7 +68,7 @@ private function getCacheKey(): string
4968
\get_class($this)
5069
);
5170

52-
$parameters = $this->getProperties();
71+
$parameters = ClassUtil::getObjectProperties($this, \ReflectionProperty::IS_READONLY);
5372

5473
if ($parameters !== []) {
5574
$this->cacheName .= '-' . CacheHandler::getInstance()->getCacheIndex($parameters);
@@ -58,36 +77,4 @@ private function getCacheKey(): string
5877

5978
return $this->cacheName;
6079
}
61-
62-
private static function getCacheAdapter(): CacheInterface
63-
{
64-
static $cacheAdapter;
65-
if (!isset($cacheAdapter)) {
66-
$cacheAdapter = new ProxyAdapter(CacheHandler::getInstance()->getCacheAdapter());
67-
}
68-
69-
return $cacheAdapter;
70-
}
71-
72-
/**
73-
* @return array<string, mixed>
74-
*/
75-
private function getProperties(): array
76-
{
77-
$reflection = new \ReflectionClass($this);
78-
$properties = [];
79-
foreach ($reflection->getProperties(\ReflectionProperty::IS_READONLY) as $property) {
80-
if (!$property->isInitialized($this)) {
81-
continue;
82-
}
83-
84-
if ($property->getValue($this) === null) {
85-
continue;
86-
}
87-
88-
$properties[$property->getName()] = $property->getValue($this);
89-
}
90-
91-
return $properties;
92-
}
9380
}

wcfsetup/install/files/lib/util/ClassUtil.class.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,33 @@ public static function isDecoratedInstanceOf($className, $targetClass)
9797
return false;
9898
}
9999

100+
/**
101+
* Returns the properties as a key-value array of the given object.
102+
* This array doesn't contain properties with `null` as value.
103+
*
104+
* @param ?\ReflectionProperty::IS_* $filter
105+
*
106+
* @return array<string, mixed>
107+
*/
108+
public static function getObjectProperties(object $object, ?int $filter = null): array
109+
{
110+
$reflection = new \ReflectionClass($object);
111+
$properties = [];
112+
foreach ($reflection->getProperties($filter) as $property) {
113+
if (!$property->isInitialized($object)) {
114+
continue;
115+
}
116+
117+
if ($property->getValue($object) === null) {
118+
continue;
119+
}
120+
121+
$properties[$property->getName()] = $property->getValue($object);
122+
}
123+
124+
return $properties;
125+
}
126+
100127
/**
101128
* Forbid creation of ClassUtil objects.
102129
*/

0 commit comments

Comments
 (0)