From dd510dfe70782fba38d068e9180fe95cf9bf5a5a Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Thu, 1 Sep 2022 17:28:18 +0200 Subject: Allow to mesure free space in object storages, based on used size and quota If the object storage backend doesn't return quota or used bytes information, use a config override for the quota and count the size used in Nextcloud instead. Signed-off-by: Thomas Citharel --- .../Files/ObjectStore/ObjectStoreStorage.php | 68 ++++++++++++++++++++++ 1 file changed, 68 insertions(+) (limited to 'lib/private/Files/ObjectStore/ObjectStoreStorage.php') diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php index eb8aaffe1e0..48ca9486090 100644 --- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php +++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php @@ -47,6 +47,10 @@ use OCP\Files\ObjectStore\IObjectStore; use OCP\Files\ObjectStore\IObjectStoreMultiPartUpload; use OCP\Files\Storage\IChunkedFileWrite; use OCP\Files\Storage\IStorage; +use OCP\ICacheFactory; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\Server; class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFileWrite { use CopyDirectory; @@ -738,4 +742,68 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil $urn = $this->getURN($cacheEntry->getId()); $this->objectStore->abortMultipartUpload($urn, $writeToken); } + + /** + * In case the admin has overwritten the global instance quota, + * default to the quota minus the used object storage size known by Nextcloud + * instead of unknown + * + * @param $path + */ + public function free_space($path) { + $cacheFactory = Server::get(ICacheFactory::class); + $memcache = $cacheFactory->createLocal('object_storage_info'); + $cacheKey = 'object_store_used_size'; + $size = $memcache->get($cacheKey); + if ($size) { + return $size; + } + + // Ask for the object store backend + $quota = $this->objectStore->bytesQuota(); + + // If not given, by the object store backend, check if we have an override + if ($quota < 0) { + /** @var IConfig $config */ + $config = Server::get(IConfig::class); + $quota = (int) $config->getAppValue('core', 'quota_instance_global', '0'); + } + + // If there's a quota in place + if ($quota > 0) { + // The used size, directly given by the object store backend + $size = $this->objectStore->bytesUsed(); + if ($size < 0) { + // Otherwise we just make the sum of all of the storages sizes currently in Nextcloud + // Note: This will not take into account object versioning handled + // on the object store backend's side such as when using files_versions_s3 + $size = $this->getUsedObjectStorageSizeInNextcloud(); + } + $freeSpace = $quota - $size; + $memcache->set($cacheKey, $freeSpace, 5 * 60); + return $freeSpace; + } + // Otherwise default to unknown + return parent::free_space($path); + } + + /** + * Returns the sum of file sizes from all of the users storages using object storage, + * as well as the global object store storage. + */ + public function getUsedObjectStorageSizeInNextcloud(): ?int { + $qb = Server::get(IDBConnection::class)->getQueryBuilder(); + $result = $qb->select($qb->func()->sum('size')) + ->from('filecache', 'fc') + ->join('fc', 'storages', 's', $qb->expr()->eq('fc.storage', 's.numeric_id')) + ->where($qb->expr()->like('s.id', $qb->createNamedParameter('object::%'))) + ->andWhere($qb->expr()->eq('fc.path', $qb->createNamedParameter(''))) + ->executeQuery(); + + $row = $result->fetchOne(); + if ($row) { + return (int) $row; + } + return null; + } } -- cgit v1.2.3