diff options
Diffstat (limited to 'lib/private/FilesMetadata')
9 files changed, 190 insertions, 270 deletions
diff --git a/lib/private/FilesMetadata/FilesMetadataManager.php b/lib/private/FilesMetadata/FilesMetadataManager.php index 9bfa8ae49d6..4846799b3d4 100644 --- a/lib/private/FilesMetadata/FilesMetadataManager.php +++ b/lib/private/FilesMetadata/FilesMetadataManager.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> - * - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\FilesMetadata; @@ -51,8 +34,7 @@ use OCP\FilesMetadata\IFilesMetadataManager; use OCP\FilesMetadata\IMetadataQuery; use OCP\FilesMetadata\Model\IFilesMetadata; use OCP\FilesMetadata\Model\IMetadataValueWrapper; -use OCP\IConfig; -use OCP\IDBConnection; +use OCP\IAppConfig; use Psr\Log\LoggerInterface; /** @@ -69,7 +51,7 @@ class FilesMetadataManager implements IFilesMetadataManager { public function __construct( private IEventDispatcher $eventDispatcher, private IJobList $jobList, - private IConfig $config, + private IAppConfig $appConfig, private LoggerInterface $logger, private MetadataRequestService $metadataRequestService, private IndexRequestService $indexRequestService, @@ -93,13 +75,16 @@ class FilesMetadataManager implements IFilesMetadataManager { public function refreshMetadata( Node $node, int $process = self::PROCESS_LIVE, - string $namedEvent = '' + string $namedEvent = '', ): IFilesMetadata { + $storageId = $node->getStorage()->getCache()->getNumericStorageId(); try { + /** @var FilesMetadata $metadata */ $metadata = $this->metadataRequestService->getMetadataFromFileId($node->getId()); } catch (FilesMetadataNotFoundException) { $metadata = new FilesMetadata($node->getId()); } + $metadata->setStorageId($storageId); // if $process is LIVE, we enforce LIVE // if $process is NAMED, we go NAMED @@ -122,7 +107,7 @@ class FilesMetadataManager implements IFilesMetadataManager { return $this->refreshMetadata($node, self::PROCESS_BACKGROUND); } - $this->jobList->add(UpdateSingleMetadata::class, [$node->getOwner()->getUID(), $node->getId()]); + $this->jobList->add(UpdateSingleMetadata::class, [$node->getOwner()?->getUID(), $node->getId()]); } return $metadata; @@ -206,7 +191,7 @@ class FilesMetadataManager implements IFilesMetadataManager { // update metadata types list $current = $this->getKnownMetadata(); $current->import($filesMetadata->jsonSerialize(true)); - $this->config->setAppValue('core', self::CONFIG_KEY, json_encode($current)); + $this->appConfig->setValueArray('core', self::CONFIG_KEY, $current->jsonSerialize(), lazy: true); } /** @@ -235,20 +220,16 @@ class FilesMetadataManager implements IFilesMetadataManager { * @param string $fileIdField alias of the field that contains file ids * * @inheritDoc - * @return IMetadataQuery|null + * @return IMetadataQuery * @see IMetadataQuery * @since 28.0.0 */ public function getMetadataQuery( IQueryBuilder $qb, string $fileTableAlias, - string $fileIdField - ): ?IMetadataQuery { - if (!$this->metadataInitiated()) { - return null; - } - - return new MetadataQuery($qb, $this->getKnownMetadata(), $fileTableAlias, $fileIdField); + string $fileIdField, + ): IMetadataQuery { + return new MetadataQuery($qb, $this, $fileTableAlias, $fileIdField); } /** @@ -257,14 +238,13 @@ class FilesMetadataManager implements IFilesMetadataManager { * @since 28.0.0 */ public function getKnownMetadata(): IFilesMetadata { - if (null !== $this->all) { + if ($this->all !== null) { return $this->all; } $this->all = new FilesMetadata(); try { - $data = json_decode($this->config->getAppValue('core', self::CONFIG_KEY, '[]'), true, 127, JSON_THROW_ON_ERROR); - $this->all->import($data); + $this->all->import($this->appConfig->getValueArray('core', self::CONFIG_KEY, lazy: true)); } catch (JsonException) { $this->logger->warning('issue while reading stored list of metadata. Advised to run ./occ files:scan --all --generate-metadata'); } @@ -296,7 +276,7 @@ class FilesMetadataManager implements IFilesMetadataManager { string $key, string $type, bool $indexed = false, - int $editPermission = IMetadataValueWrapper::EDIT_FORBIDDEN + int $editPermission = IMetadataValueWrapper::EDIT_FORBIDDEN, ): void { $current = $this->getKnownMetadata(); try { @@ -310,7 +290,7 @@ class FilesMetadataManager implements IFilesMetadataManager { } $current->import([$key => ['type' => $type, 'indexed' => $indexed, 'editPermission' => $editPermission]]); - $this->config->setAppValue('core', self::CONFIG_KEY, json_encode($current)); + $this->appConfig->setValueArray('core', self::CONFIG_KEY, $current->jsonSerialize(), lazy: true); $this->all = $current; } @@ -323,26 +303,4 @@ class FilesMetadataManager implements IFilesMetadataManager { $eventDispatcher->addServiceListener(NodeWrittenEvent::class, MetadataUpdate::class); $eventDispatcher->addServiceListener(CacheEntryRemovedEvent::class, MetadataDelete::class); } - - /** - * Will confirm that tables were created and store an app value to cache the result. - * Can be removed in 29 as this is to avoid strange situation when Nextcloud files were - * replaced but the upgrade was not triggered yet. - * - * @return bool - */ - private function metadataInitiated(): bool { - if ($this->config->getAppValue('core', self::MIGRATION_DONE, '0') === '1') { - return true; - } - - $dbConnection = \OCP\Server::get(IDBConnection::class); - if ($dbConnection->tableExists(MetadataRequestService::TABLE_METADATA)) { - $this->config->setAppValue('core', self::MIGRATION_DONE, '1'); - - return true; - } - - return false; - } } diff --git a/lib/private/FilesMetadata/Job/UpdateSingleMetadata.php b/lib/private/FilesMetadata/Job/UpdateSingleMetadata.php index d18c8aa3680..5746493ddef 100644 --- a/lib/private/FilesMetadata/Job/UpdateSingleMetadata.php +++ b/lib/private/FilesMetadata/Job/UpdateSingleMetadata.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> - * - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\FilesMetadata\Job; @@ -46,7 +29,7 @@ class UpdateSingleMetadata extends QueuedJob { ITimeFactory $time, private IRootFolder $rootFolder, private FilesMetadataManager $filesMetadataManager, - private LoggerInterface $logger + private LoggerInterface $logger, ) { parent::__construct($time); } @@ -55,10 +38,9 @@ class UpdateSingleMetadata extends QueuedJob { [$userId, $fileId] = $argument; try { - $node = $this->rootFolder->getUserFolder($userId)->getById($fileId); - if (count($node) > 0) { - $file = array_shift($node); - $this->filesMetadataManager->refreshMetadata($file, IFilesMetadataManager::PROCESS_BACKGROUND); + $node = $this->rootFolder->getUserFolder($userId)->getFirstNodeById($fileId); + if ($node) { + $this->filesMetadataManager->refreshMetadata($node, IFilesMetadataManager::PROCESS_BACKGROUND); } } catch (\Exception $e) { $this->logger->warning('issue while running UpdateSingleMetadata', ['exception' => $e, 'userId' => $userId, 'fileId' => $fileId]); diff --git a/lib/private/FilesMetadata/Listener/MetadataDelete.php b/lib/private/FilesMetadata/Listener/MetadataDelete.php index d950c2cea5f..226bd3bdafa 100644 --- a/lib/private/FilesMetadata/Listener/MetadataDelete.php +++ b/lib/private/FilesMetadata/Listener/MetadataDelete.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> - * - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\FilesMetadata\Listener; @@ -40,7 +23,7 @@ use Psr\Log\LoggerInterface; class MetadataDelete implements IEventListener { public function __construct( private IFilesMetadataManager $filesMetadataManager, - private LoggerInterface $logger + private LoggerInterface $logger, ) { } diff --git a/lib/private/FilesMetadata/Listener/MetadataUpdate.php b/lib/private/FilesMetadata/Listener/MetadataUpdate.php index 9848f079882..4c5c913c740 100644 --- a/lib/private/FilesMetadata/Listener/MetadataUpdate.php +++ b/lib/private/FilesMetadata/Listener/MetadataUpdate.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> - * - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\FilesMetadata\Listener; @@ -43,7 +26,7 @@ use Psr\Log\LoggerInterface; class MetadataUpdate implements IEventListener { public function __construct( private IFilesMetadataManager $filesMetadataManager, - private LoggerInterface $logger + private LoggerInterface $logger, ) { } diff --git a/lib/private/FilesMetadata/MetadataQuery.php b/lib/private/FilesMetadata/MetadataQuery.php index aa079c678d7..deae433e2fc 100644 --- a/lib/private/FilesMetadata/MetadataQuery.php +++ b/lib/private/FilesMetadata/MetadataQuery.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> - * - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\FilesMetadata; @@ -31,9 +14,11 @@ use OC\FilesMetadata\Service\MetadataRequestService; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException; use OCP\FilesMetadata\Exceptions\FilesMetadataTypeException; +use OCP\FilesMetadata\IFilesMetadataManager; use OCP\FilesMetadata\IMetadataQuery; use OCP\FilesMetadata\Model\IFilesMetadata; use OCP\FilesMetadata\Model\IMetadataValueWrapper; +use Psr\Log\LoggerInterface; /** * @inheritDoc @@ -43,12 +28,23 @@ class MetadataQuery implements IMetadataQuery { private array $knownJoinedIndex = []; public function __construct( private IQueryBuilder $queryBuilder, - private IFilesMetadata $knownMetadata, + private IFilesMetadata|IFilesMetadataManager $manager, private string $fileTableAlias = 'fc', private string $fileIdField = 'fileid', private string $alias = 'meta', - private string $aliasIndexPrefix = 'meta_index' + private string $aliasIndexPrefix = 'meta_index', ) { + if ($manager instanceof IFilesMetadata) { + /** + * Since 29, because knownMetadata is stored in lazy appconfig, it seems smarter + * to not call getKnownMetadata() at the load of this class as it is only needed + * in {@see getMetadataValueField}. + * + * FIXME: remove support for IFilesMetadata + */ + $logger = \OCP\Server::get(LoggerInterface::class); + $logger->debug('It is deprecated to use IFilesMetadata as second parameter when calling MetadataQuery::__construct()'); + } } /** @@ -158,7 +154,20 @@ class MetadataQuery implements IMetadataQuery { * @since 28.0.0 */ public function getMetadataValueField(string $metadataKey): string { - return match ($this->knownMetadata->getType($metadataKey)) { + if ($this->manager instanceof IFilesMetadataManager) { + /** + * Since 29, because knownMetadata is stored in lazy appconfig, it seems smarter + * to not call getKnownMetadata() at the load of this class as it is only needed + * in this method. + * + * FIXME: keep only this line and remove support for previous IFilesMetadata in constructor + */ + $knownMetadata = $this->manager->getKnownMetadata(); + } else { + $knownMetadata = $this->manager; + } + + return match ($knownMetadata->getType($metadataKey)) { IMetadataValueWrapper::TYPE_STRING => $this->joinedTableAlias($metadataKey) . '.meta_value_string', IMetadataValueWrapper::TYPE_INT, IMetadataValueWrapper::TYPE_BOOL => $this->joinedTableAlias($metadataKey) . '.meta_value_int', default => throw new FilesMetadataTypeException('metadata is not set as indexed'), diff --git a/lib/private/FilesMetadata/Model/FilesMetadata.php b/lib/private/FilesMetadata/Model/FilesMetadata.php index 629b537dabe..b66e1fe3711 100644 --- a/lib/private/FilesMetadata/Model/FilesMetadata.php +++ b/lib/private/FilesMetadata/Model/FilesMetadata.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> - * - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\FilesMetadata\Model; @@ -44,9 +27,10 @@ class FilesMetadata implements IFilesMetadata { private bool $updated = false; private int $lastUpdate = 0; private string $syncToken = ''; + private ?int $storageId = null; public function __construct( - private int $fileId = 0 + private int $fileId = 0, ) { } @@ -59,6 +43,22 @@ class FilesMetadata implements IFilesMetadata { return $this->fileId; } + public function getStorageId(): ?int { + return $this->storageId; + } + + /** + * Set which storage the file this metadata belongs to. + * + * This helps with sharded filecache setups to know where to store the metadata + * + * @param int $storageId + * @return void + */ + public function setStorageId(int $storageId): void { + $this->storageId = $storageId; + } + /** * @inheritDoc * @return int timestamp @@ -156,6 +156,23 @@ class FilesMetadata implements IFilesMetadata { $this->metadata[$key]->setEditPermission($permission); } + + public function getEtag(string $key): string { + if (!array_key_exists($key, $this->metadata)) { + return ''; + } + + return $this->metadata[$key]->getEtag(); + } + + public function setEtag(string $key, string $etag): void { + if (!array_key_exists($key, $this->metadata)) { + throw new FilesMetadataNotFoundException(); + } + + $this->metadata[$key]->setEtag($etag); + } + /** * @param string $key metadata key * @@ -480,7 +497,7 @@ class FilesMetadata implements IFilesMetadata { // if value does not exist, or type has changed, we keep on the writing } - $valueWrapper = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_STRING_LIST); + $valueWrapper = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_INT_LIST); $this->metadata[$key] = $valueWrapper->setValueIntList($value)->setIndexed($index); $this->updated = true; diff --git a/lib/private/FilesMetadata/Model/MetadataValueWrapper.php b/lib/private/FilesMetadata/Model/MetadataValueWrapper.php index 90f1554180d..710a8129340 100644 --- a/lib/private/FilesMetadata/Model/MetadataValueWrapper.php +++ b/lib/private/FilesMetadata/Model/MetadataValueWrapper.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> - * - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\FilesMetadata\Model; @@ -38,6 +21,7 @@ class MetadataValueWrapper implements IMetadataValueWrapper { private string $type; /** @var string|int|float|bool|array|string[]|int[] */ private mixed $value = null; + private string $etag = ''; private bool $indexed = false; private int $editPermission = self::EDIT_FORBIDDEN; @@ -233,7 +217,7 @@ class MetadataValueWrapper implements IMetadataValueWrapper { */ public function getValueString(): string { $this->assertType(self::TYPE_STRING); - if (null === $this->value) { + if ($this->value === null) { throw new FilesMetadataNotFoundException('value is not set'); } @@ -249,7 +233,7 @@ class MetadataValueWrapper implements IMetadataValueWrapper { */ public function getValueInt(): int { $this->assertType(self::TYPE_INT); - if (null === $this->value) { + if ($this->value === null) { throw new FilesMetadataNotFoundException('value is not set'); } @@ -265,7 +249,7 @@ class MetadataValueWrapper implements IMetadataValueWrapper { */ public function getValueFloat(): float { $this->assertType(self::TYPE_FLOAT); - if (null === $this->value) { + if ($this->value === null) { throw new FilesMetadataNotFoundException('value is not set'); } @@ -281,7 +265,7 @@ class MetadataValueWrapper implements IMetadataValueWrapper { */ public function getValueBool(): bool { $this->assertType(self::TYPE_BOOL); - if (null === $this->value) { + if ($this->value === null) { throw new FilesMetadataNotFoundException('value is not set'); } @@ -297,7 +281,7 @@ class MetadataValueWrapper implements IMetadataValueWrapper { */ public function getValueArray(): array { $this->assertType(self::TYPE_ARRAY); - if (null === $this->value) { + if ($this->value === null) { throw new FilesMetadataNotFoundException('value is not set'); } @@ -313,7 +297,7 @@ class MetadataValueWrapper implements IMetadataValueWrapper { */ public function getValueStringList(): array { $this->assertType(self::TYPE_STRING_LIST); - if (null === $this->value) { + if ($this->value === null) { throw new FilesMetadataNotFoundException('value is not set'); } @@ -329,7 +313,7 @@ class MetadataValueWrapper implements IMetadataValueWrapper { */ public function getValueIntList(): array { $this->assertType(self::TYPE_INT_LIST); - if (null === $this->value) { + if ($this->value === null) { throw new FilesMetadataNotFoundException('value is not set'); } @@ -343,7 +327,7 @@ class MetadataValueWrapper implements IMetadataValueWrapper { * @since 28.0.0 */ public function getValueAny(): mixed { - if (null === $this->value) { + if ($this->value === null) { throw new FilesMetadataNotFoundException('value is not set'); } @@ -351,6 +335,27 @@ class MetadataValueWrapper implements IMetadataValueWrapper { } /** + * @inheritDoc + * @return string stored etag + * @since 29.0.0 + */ + public function getEtag(): string { + return $this->etag; + } + + /** + * @param string $etag etag value + * + * @inheritDoc + * @return self + * @since 29.0.0 + */ + public function setEtag(string $etag): self { + $this->etag = $etag; + return $this; + } + + /** * @param bool $indexed TRUE to set the stored value as an indexed value * * @inheritDoc @@ -405,6 +410,7 @@ class MetadataValueWrapper implements IMetadataValueWrapper { public function import(array $data): self { $this->value = $data['value'] ?? null; $this->type = $data['type'] ?? ''; + $this->setEtag($data['etag'] ?? ''); $this->setIndexed($data['indexed'] ?? false); $this->setEditPermission($data['editPermission'] ?? self::EDIT_FORBIDDEN); return $this; @@ -414,6 +420,7 @@ class MetadataValueWrapper implements IMetadataValueWrapper { return [ 'value' => ($emptyValues) ? null : $this->value, 'type' => $this->getType(), + 'etag' => $this->getEtag(), 'indexed' => $this->isIndexed(), 'editPermission' => $this->getEditPermission() ]; diff --git a/lib/private/FilesMetadata/Service/IndexRequestService.php b/lib/private/FilesMetadata/Service/IndexRequestService.php index 2a23e2c9a67..91bd9f0b11e 100644 --- a/lib/private/FilesMetadata/Service/IndexRequestService.php +++ b/lib/private/FilesMetadata/Service/IndexRequestService.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> - * - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\FilesMetadata\Service; @@ -42,7 +25,7 @@ class IndexRequestService { public function __construct( private IDBConnection $dbConnection, - private LoggerInterface $logger + private LoggerInterface $logger, ) { } @@ -99,9 +82,9 @@ class IndexRequestService { private function insertIndexString(int $fileId, string $key, string $value): void { $qb = $this->dbConnection->getQueryBuilder(); $qb->insert(self::TABLE_METADATA_INDEX) - ->setValue('meta_key', $qb->createNamedParameter($key)) - ->setValue('meta_value_string', $qb->createNamedParameter($value)) - ->setValue('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)); + ->setValue('meta_key', $qb->createNamedParameter($key)) + ->setValue('meta_value_string', $qb->createNamedParameter($value)) + ->setValue('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)); $qb->executeStatement(); } @@ -117,9 +100,9 @@ class IndexRequestService { public function insertIndexInt(int $fileId, string $key, int $value): void { $qb = $this->dbConnection->getQueryBuilder(); $qb->insert(self::TABLE_METADATA_INDEX) - ->setValue('meta_key', $qb->createNamedParameter($key)) - ->setValue('meta_value_int', $qb->createNamedParameter($value, IQueryBuilder::PARAM_INT)) - ->setValue('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)); + ->setValue('meta_key', $qb->createNamedParameter($key)) + ->setValue('meta_value_int', $qb->createNamedParameter($value, IQueryBuilder::PARAM_INT)) + ->setValue('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)); $qb->executeStatement(); } @@ -135,9 +118,9 @@ class IndexRequestService { public function insertIndexBool(int $fileId, string $key, bool $value): void { $qb = $this->dbConnection->getQueryBuilder(); $qb->insert(self::TABLE_METADATA_INDEX) - ->setValue('meta_key', $qb->createNamedParameter($key)) - ->setValue('meta_value_int', $qb->createNamedParameter(($value) ? '1' : '0', IQueryBuilder::PARAM_INT)) - ->setValue('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)); + ->setValue('meta_key', $qb->createNamedParameter($key)) + ->setValue('meta_value_int', $qb->createNamedParameter(($value) ? '1' : '0', IQueryBuilder::PARAM_INT)) + ->setValue('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)); $qb->executeStatement(); } @@ -184,7 +167,7 @@ class IndexRequestService { $qb = $this->dbConnection->getQueryBuilder(); $expr = $qb->expr(); $qb->delete(self::TABLE_METADATA_INDEX) - ->where($expr->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); + ->where($expr->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); if ($key !== '') { $qb->andWhere($expr->eq('meta_key', $qb->createNamedParameter($key))); diff --git a/lib/private/FilesMetadata/Service/MetadataRequestService.php b/lib/private/FilesMetadata/Service/MetadataRequestService.php index cdce624d75c..c308ae1c9c8 100644 --- a/lib/private/FilesMetadata/Service/MetadataRequestService.php +++ b/lib/private/FilesMetadata/Service/MetadataRequestService.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> - * - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\FilesMetadata\Service; @@ -41,10 +24,31 @@ class MetadataRequestService { public function __construct( private IDBConnection $dbConnection, - private LoggerInterface $logger + private LoggerInterface $logger, ) { } + private function getStorageId(IFilesMetadata $filesMetadata): int { + if ($filesMetadata instanceof FilesMetadata) { + $storage = $filesMetadata->getStorageId(); + if ($storage) { + return $storage; + } + } + // all code paths that lead to saving metadata *should* have the storage id set + // this fallback is there just in case + $query = $this->dbConnection->getQueryBuilder(); + $query->select('storage') + ->from('filecache') + ->where($query->expr()->eq('fileid', $query->createNamedParameter($filesMetadata->getFileId(), IQueryBuilder::PARAM_INT))); + $storageId = $query->executeQuery()->fetchColumn(); + + if ($filesMetadata instanceof FilesMetadata) { + $filesMetadata->setStorageId($storageId); + } + return $storageId; + } + /** * store metadata into database * @@ -55,10 +59,11 @@ class MetadataRequestService { public function store(IFilesMetadata $filesMetadata): void { $qb = $this->dbConnection->getQueryBuilder(); $qb->insert(self::TABLE_METADATA) - ->setValue('file_id', $qb->createNamedParameter($filesMetadata->getFileId(), IQueryBuilder::PARAM_INT)) - ->setValue('json', $qb->createNamedParameter(json_encode($filesMetadata->jsonSerialize()))) - ->setValue('sync_token', $qb->createNamedParameter($this->generateSyncToken())) - ->setValue('last_update', (string) $qb->createFunction('NOW()')); + ->hintShardKey('storage', $this->getStorageId($filesMetadata)) + ->setValue('file_id', $qb->createNamedParameter($filesMetadata->getFileId(), IQueryBuilder::PARAM_INT)) + ->setValue('json', $qb->createNamedParameter(json_encode($filesMetadata->jsonSerialize()))) + ->setValue('sync_token', $qb->createNamedParameter($this->generateSyncToken())) + ->setValue('last_update', (string)$qb->createFunction('NOW()')); $qb->executeStatement(); } @@ -74,16 +79,12 @@ class MetadataRequestService { try { $qb = $this->dbConnection->getQueryBuilder(); $qb->select('json', 'sync_token')->from(self::TABLE_METADATA); - $qb->where( - $qb->expr()->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)) - ); + $qb->where($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); $result = $qb->executeQuery(); $data = $result->fetch(); $result->closeCursor(); } catch (Exception $e) { - $this->logger->warning( - 'exception while getMetadataFromDatabase()', ['exception' => $e, 'fileId' => $fileId] - ); + $this->logger->warning('exception while getMetadataFromDatabase()', ['exception' => $e, 'fileId' => $fileId]); throw new FilesMetadataNotFoundException(); } @@ -100,8 +101,6 @@ class MetadataRequestService { /** * returns metadata for multiple file ids * - * If - * * @param array $fileIds file ids * * @return array File ID is the array key, files without metadata are not returned in the array @@ -110,14 +109,12 @@ class MetadataRequestService { public function getMetadataFromFileIds(array $fileIds): array { $qb = $this->dbConnection->getQueryBuilder(); $qb->select('file_id', 'json', 'sync_token')->from(self::TABLE_METADATA); - $qb->where( - $qb->expr()->in('file_id', $qb->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)) - ); + $qb->where($qb->expr()->in('file_id', $qb->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY))); $list = []; $result = $qb->executeQuery(); while ($data = $result->fetch()) { - $fileId = (int) $data['file_id']; + $fileId = (int)$data['file_id']; $metadata = new FilesMetadata($fileId); try { $metadata->importFromDatabase($data); @@ -142,7 +139,7 @@ class MetadataRequestService { public function dropMetadata(int $fileId): void { $qb = $this->dbConnection->getQueryBuilder(); $qb->delete(self::TABLE_METADATA) - ->where($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); + ->where($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); $qb->executeStatement(); } @@ -159,15 +156,16 @@ class MetadataRequestService { $expr = $qb->expr(); $qb->update(self::TABLE_METADATA) - ->set('json', $qb->createNamedParameter(json_encode($filesMetadata->jsonSerialize()))) - ->set('sync_token', $qb->createNamedParameter($this->generateSyncToken())) - ->set('last_update', $qb->createFunction('NOW()')) - ->where( - $expr->andX( - $expr->eq('file_id', $qb->createNamedParameter($filesMetadata->getFileId(), IQueryBuilder::PARAM_INT)), - $expr->eq('sync_token', $qb->createNamedParameter($filesMetadata->getSyncToken())) - ) - ); + ->hintShardKey('files_metadata', $this->getStorageId($filesMetadata)) + ->set('json', $qb->createNamedParameter(json_encode($filesMetadata->jsonSerialize()))) + ->set('sync_token', $qb->createNamedParameter($this->generateSyncToken())) + ->set('last_update', $qb->createFunction('NOW()')) + ->where( + $expr->andX( + $expr->eq('file_id', $qb->createNamedParameter($filesMetadata->getFileId(), IQueryBuilder::PARAM_INT)), + $expr->eq('sync_token', $qb->createNamedParameter($filesMetadata->getSyncToken())) + ) + ); return $qb->executeStatement(); } |