storageId = $storage instanceof IStorage ? $storage->getId() : $storage; $this->storageId = self::adjustStorageId($this->storageId); if ($row = self::getStorageById($this->storageId)) { $this->numericId = $row['numeric_id']; } else { $available = $isAvailable ? 1 : 0; try { $qb = $connection->getQueryBuilder(); $qb->insert('storages') ->values([ 'id' => $qb->createNamedParameter($this->storageId), 'available' => $qb->createNamedParameter($available), ]) ->executeStatement(); $this->numericId = $qb->getLastInsertId(); } catch (DbalException $e) { if ($e->getReason() === DbalException::REASON_UNIQUE_CONSTRAINT_VIOLATION) { $qb = $connection->getQueryBuilder(); $result = $qb->select('numeric_id') ->from('storages') ->where($qb->expr()->eq('id', $qb->createNamedParameter($this->storageId))) ->executeQuery(); /** @var false|string|int $row */ $row = $result->fetchOne(); if ($row === false) { throw new \RuntimeException('Storage could neither be inserted nor be selected from the database: ' . $this->storageId, $e->getCode(), $e); } $this->numericId = (int)$row; } throw $e; } } } /** * @return array{id: string, numeric_id: int, available: bool, last_checked: int}|null */ public static function getStorageById(string $storageId): ?array { return self::getGlobalCache()->getStorageInfo($storageId); } /** * Adjusts the storage id to use md5 if too long * @param string $storageId storage id * @return string unchanged $storageId if its length is less than 64 characters, * else returns the md5 of $storageId */ public static function adjustStorageId(string $storageId): string { if (strlen($storageId) > 64) { return md5($storageId); } return $storageId; } /** * Get the numeric id for the storage */ public function getNumericId(): int { return $this->numericId; } /** * Get the string id for the storage * * @return string|null either the storage id string or null if the numeric id is not known */ public static function getStorageId(int $numericId): ?string { $storage = self::getGlobalCache()->getStorageInfoByNumericId($numericId); return $storage['id'] ?? null; } /** * @return array{available: bool, last_checked: int} */ public function getAvailability(): array { if ($row = self::getStorageById($this->storageId)) { return [ 'available' => (int)$row['available'] === 1, 'last_checked' => $row['last_checked'] ]; } return [ 'available' => true, 'last_checked' => time(), ]; } /** * @param int $delay amount of seconds to delay reconsidering that storage further */ public function setAvailability(bool $isAvailable, int $delay = 0): void { $available = $isAvailable ? 1 : 0; if (!$isAvailable) { Server::get(LoggerInterface::class)->info('Storage with ' . $this->storageId . ' marked as unavailable', ['app' => 'lib']); } $query = Server::get(IDBConnection::class)->getQueryBuilder(); $query->update('storages') ->set('available', $query->createNamedParameter($available)) ->set('last_checked', $query->createNamedParameter(time() + $delay)) ->where($query->expr()->eq('id', $query->createNamedParameter($this->storageId))); $query->executeStatement(); } /** * Remove the entry for the storage by the mount id. */ public static function cleanByMountId(int $mountId): void { $db = Server::get(IDBConnection::class); try { $db->beginTransaction(); $query = $db->getQueryBuilder(); $query->select('storage_id') ->from('mounts') ->where($query->expr()->eq('mount_id', $query->createNamedParameter($mountId, IQueryBuilder::PARAM_INT))); $storageIds = $query->executeQuery()->fetchAll(\PDO::FETCH_COLUMN); $storageIds = array_unique($storageIds); $query = $db->getQueryBuilder(); $query->delete('filecache') ->where($query->expr()->in('storage', $query->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY))) ->runAcrossAllShards() ->executeStatement(); $query = $db->getQueryBuilder(); $query->delete('storages') ->where($query->expr()->in('numeric_id', $query->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY))) ->executeStatement(); $query = $db->getQueryBuilder(); $query->delete('mounts') ->where($query->expr()->eq('mount_id', $query->createNamedParameter($mountId, IQueryBuilder::PARAM_INT))) ->executeStatement(); $db->commit(); } catch (\Exception $exception) { $db->rollBack(); throw $exception; } } }