diff --git a/lib/private/Files/Cache/CacheEntry.php b/lib/private/Files/Cache/CacheEntry.php index e9417c8012a..51e8f0bb9d7 100644 --- a/lib/private/Files/Cache/CacheEntry.php +++ b/lib/private/Files/Cache/CacheEntry.php @@ -110,7 +110,7 @@ class CacheEntry implements ICacheEntry { return $this->data['upload_time'] ?? null; } - public function getData() { + public function getData(): array { return $this->data; } diff --git a/lib/private/Files/Cache/CachedFileAccess.php b/lib/private/Files/Cache/CachedFileAccess.php new file mode 100644 index 00000000000..06f804550fb --- /dev/null +++ b/lib/private/Files/Cache/CachedFileAccess.php @@ -0,0 +1,111 @@ + + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Files\Cache; + +use OC\FilesMetadata\FilesMetadataManager; +use OC\SystemConfig; +use OCP\Files\Cache\ICacheEntry; +use OCP\Files\IMimeTypeLoader; +use OCP\ICache; +use OCP\ICacheFactory; +use OCP\IDBConnection; +use Psr\Log\LoggerInterface; + +/** + * Provides cached access to file metadata. + * + * Note that this does not include any logic for invalidation and should only be + * used in places where up to 5 minute outdated metadata is accepted + */ +class CachedFileAccess extends FileAccess { + const TTL = 5 * 60; + + private ICache $cache; + + public function __construct( + IDBConnection $connection, + SystemConfig $systemConfig, + LoggerInterface $logger, + FilesMetadataManager $metadataManager, + IMimeTypeLoader $mimeTypeLoader, + ICacheFactory $cacheFactory, + ) { + parent::__construct($connection, $systemConfig, $logger, $metadataManager, $mimeTypeLoader); + $this->cache = $cacheFactory->createLocal('file_access::'); + } + + /** + * @param int[] $fileIds + * @return string + */ + private function getCacheKey(array $fileIds): string { + return md5(implode(',', $fileIds)); + } + + /** + * @param int[] $fileIds + * @return null|ICacheEntry[] + */ + private function getCachedByFileIds(array $fileIds): ?array { + $cached = $this->cache->get($this->getCacheKey($fileIds)); + if (is_array($cached)) { + return array_map(function ($data) { + return new CacheEntry($data); + }, $cached); + } else { + return null; + } + } + + /** + * @param ICacheEntry[] $results + * @return void + */ + private function cacheEntries(array $results): void { + $resultFileIds = array_map(function(ICacheEntry $result) { + return $result->getId(); + }, $results); + $value = array_map(function(ICacheEntry $entry) { + return $entry->getData(); + }, $results); + $this->cache->set($this->getCacheKey($resultFileIds), $value, self::TTL); + } + + public function getByFileIdInStorage(int $fileId, int $storageId): ?CacheEntry { + $items = array_values($this->getByFileIdsInStorage([$fileId], $storageId)); + return $items[0] ?? null; + } + + public function getByFileId(int $fileId): ?CacheEntry { + $items = array_values($this->getByFileIds([$fileId])); + return $items[0] ?? null; + } + + public function getByFileIds(array $fileIds): array { + $cached = $this->getCachedByFileIds($fileIds); + if ($cached) { + return $cached; + } + $result = parent::getByFileIds($fileIds); + $this->cacheEntries($result); + return $result; + } + + public function getByFileIdsInStorage(array $fileIds, int $storageId): array { + $cached = $this->getCachedByFileIds($fileIds); + if ($cached) { + return $cached; + } + $result = parent::getByFileIdsInStorage($fileIds, $storageId); + $this->cacheEntries($result); + return $result; + } + + +}