fix(config): keep the category cache in the memory cache when available
A cache belongs in Nextcloud's memory cache rather than in the app config. Use the distributed cache (e.g. Redis) with a one-week TTL when one is available; Nextcloud falls back to the local cache (APCu) when only that is configured. Without any memory cache the lazy app config value stays as the fallback. A leftover app config value is removed when the categories are stored in the memory cache, and saving the settings clears both.
This commit is contained in:
@@ -21,6 +21,7 @@ use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
|||||||
use OCP\AppFramework\Utility\ITimeFactory;
|
use OCP\AppFramework\Utility\ITimeFactory;
|
||||||
use OCP\EventDispatcher\IEventDispatcher;
|
use OCP\EventDispatcher\IEventDispatcher;
|
||||||
use OCP\IAppConfig;
|
use OCP\IAppConfig;
|
||||||
|
use OCP\ICacheFactory;
|
||||||
use OCP\IConfig;
|
use OCP\IConfig;
|
||||||
use OCP\INavigationManager;
|
use OCP\INavigationManager;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
@@ -62,7 +63,8 @@ class Application extends App implements IBootstrap
|
|||||||
$c->get(IUserSession::class),
|
$c->get(IUserSession::class),
|
||||||
$c->get(IAppConfig::class),
|
$c->get(IAppConfig::class),
|
||||||
$c->get(ITimeFactory::class),
|
$c->get(ITimeFactory::class),
|
||||||
$c->get(LangRepository::class)
|
$c->get(LangRepository::class),
|
||||||
|
$c->get(ICacheFactory::class)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ use OC\App\AppStore\Fetcher\CategoryFetcher;
|
|||||||
use OCA\SideMenu\AppInfo\Application;
|
use OCA\SideMenu\AppInfo\Application;
|
||||||
use OCP\AppFramework\Utility\ITimeFactory;
|
use OCP\AppFramework\Utility\ITimeFactory;
|
||||||
use OCP\IAppConfig;
|
use OCP\IAppConfig;
|
||||||
|
use OCP\ICache;
|
||||||
|
use OCP\ICacheFactory;
|
||||||
use OCP\IConfig;
|
use OCP\IConfig;
|
||||||
use OCP\IUserSession;
|
use OCP\IUserSession;
|
||||||
use OCP\L10N\IFactory;
|
use OCP\L10N\IFactory;
|
||||||
@@ -33,6 +35,7 @@ class CategoryRepository
|
|||||||
protected IAppConfig $appConfig,
|
protected IAppConfig $appConfig,
|
||||||
protected ITimeFactory $timeFactory,
|
protected ITimeFactory $timeFactory,
|
||||||
protected LangRepository $langRepository,
|
protected LangRepository $langRepository,
|
||||||
|
protected ICacheFactory $cacheFactory,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +97,7 @@ class CategoryRepository
|
|||||||
|
|
||||||
public function clearCache(): void
|
public function clearCache(): void
|
||||||
{
|
{
|
||||||
|
$this->getDistributedCache()?->remove(self::CACHE_KEY);
|
||||||
$this->appConfig->deleteKey(Application::APP_ID, self::CACHE_KEY);
|
$this->appConfig->deleteKey(Application::APP_ID, self::CACHE_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,18 +121,52 @@ class CategoryRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the app store categories with only their names, from a
|
* Returns the app store categories with only their names. They are
|
||||||
* cache that is stored lazily: it holds the names in every language,
|
* cached in the distributed cache (e.g. Redis) when one is configured,
|
||||||
* so it must not be loaded on every request with the other settings.
|
* otherwise in a lazy app config value, so they are not loaded on
|
||||||
|
* every request with the other settings.
|
||||||
*/
|
*/
|
||||||
protected function getCategories(): array
|
protected function getCategories(): array
|
||||||
{
|
{
|
||||||
$cache = $this->appConfig->getValueArray(Application::APP_ID, self::CACHE_KEY, [], lazy: true);
|
$cache = $this->getDistributedCache();
|
||||||
|
|
||||||
if (isset($cache['expires'], $cache['categories']) && $cache['expires'] > $this->timeFactory->getTime()) {
|
if ($cache !== null) {
|
||||||
return $cache['categories'];
|
$categories = $cache->get(self::CACHE_KEY);
|
||||||
|
|
||||||
|
if (is_array($categories)) {
|
||||||
|
return $categories;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$stored = $this->appConfig->getValueArray(Application::APP_ID, self::CACHE_KEY, [], lazy: true);
|
||||||
|
|
||||||
|
if (isset($stored['expires'], $stored['categories']) && $stored['expires'] > $this->timeFactory->getTime()) {
|
||||||
|
return $stored['categories'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$categories = $this->fetchCategories();
|
||||||
|
|
||||||
|
// Don't cache an empty list, e.g. when the app store is unreachable
|
||||||
|
if (empty($categories)) {
|
||||||
|
return $categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->clearCache();
|
||||||
|
|
||||||
|
if ($cache !== null) {
|
||||||
|
$cache->set(self::CACHE_KEY, $categories, self::CACHE_TTL);
|
||||||
|
} else {
|
||||||
|
$this->appConfig->setValueArray(Application::APP_ID, self::CACHE_KEY, [
|
||||||
|
'expires' => $this->timeFactory->getTime() + self::CACHE_TTL,
|
||||||
|
'categories' => $categories,
|
||||||
|
], lazy: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function fetchCategories(): array
|
||||||
|
{
|
||||||
$langs = $this->getCachedLangs();
|
$langs = $this->getCachedLangs();
|
||||||
$categories = [];
|
$categories = [];
|
||||||
|
|
||||||
@@ -147,15 +185,13 @@ class CategoryRepository
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't cache an empty list, e.g. when the app store is unreachable
|
|
||||||
if (!empty($categories)) {
|
|
||||||
$this->clearCache();
|
|
||||||
$this->appConfig->setValueArray(Application::APP_ID, self::CACHE_KEY, [
|
|
||||||
'expires' => $this->timeFactory->getTime() + self::CACHE_TTL,
|
|
||||||
'categories' => $categories,
|
|
||||||
], lazy: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $categories;
|
return $categories;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getDistributedCache(): ?ICache
|
||||||
|
{
|
||||||
|
return $this->cacheFactory->isAvailable()
|
||||||
|
? $this->cacheFactory->createDistributed(Application::APP_ID)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user