diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 380d04f..330a10a 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -21,6 +21,7 @@ use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\AppFramework\Utility\ITimeFactory; use OCP\EventDispatcher\IEventDispatcher; use OCP\IAppConfig; +use OCP\ICacheFactory; use OCP\IConfig; use OCP\INavigationManager; use OCP\IRequest; @@ -62,7 +63,8 @@ class Application extends App implements IBootstrap $c->get(IUserSession::class), $c->get(IAppConfig::class), $c->get(ITimeFactory::class), - $c->get(LangRepository::class) + $c->get(LangRepository::class), + $c->get(ICacheFactory::class) ); }); diff --git a/lib/Service/CategoryRepository.php b/lib/Service/CategoryRepository.php index 76af1fb..4328dc4 100644 --- a/lib/Service/CategoryRepository.php +++ b/lib/Service/CategoryRepository.php @@ -6,6 +6,8 @@ use OC\App\AppStore\Fetcher\CategoryFetcher; use OCA\SideMenu\AppInfo\Application; use OCP\AppFramework\Utility\ITimeFactory; use OCP\IAppConfig; +use OCP\ICache; +use OCP\ICacheFactory; use OCP\IConfig; use OCP\IUserSession; use OCP\L10N\IFactory; @@ -33,6 +35,7 @@ class CategoryRepository protected IAppConfig $appConfig, protected ITimeFactory $timeFactory, protected LangRepository $langRepository, + protected ICacheFactory $cacheFactory, ) { } @@ -94,6 +97,7 @@ class CategoryRepository public function clearCache(): void { + $this->getDistributedCache()?->remove(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 - * cache that is stored lazily: it holds the names in every language, - * so it must not be loaded on every request with the other settings. + * Returns the app store categories with only their names. They are + * cached in the distributed cache (e.g. Redis) when one is configured, + * otherwise in a lazy app config value, so they are not loaded on + * every request with the other settings. */ 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()) { - return $cache['categories']; + if ($cache !== null) { + $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(); $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; } + + protected function getDistributedCache(): ?ICache + { + return $this->cacheFactory->isAvailable() + ? $this->cacheFactory->createDistributed(Application::APP_ID) + : null; + } }