From 354c0996d83c853ee88dd1a26ce47e15f9866504 Mon Sep 17 00:00:00 2001 From: Adrian Brzezinski Date: Tue, 14 Jan 2020 09:47:18 +0100 Subject: [PATCH] It's a fix for issue #14116, Improves efficiency when downloading files from Swift storage. Before, files were downloaded and then pushed back to user. That behaevior causes all kinds of performance problems. Now, files are streamed directly to user. Signed-off-by: Adrian Brzezinski --- lib/private/Files/ObjectStore/Swift.php | 37 ++++++++++++------- .../Files/ObjectStore/SwiftFactory.php | 29 +++++++++++++-- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/lib/private/Files/ObjectStore/Swift.php b/lib/private/Files/ObjectStore/Swift.php index 9a2aa82295e..e0d819f8a2a 100644 --- a/lib/private/Files/ObjectStore/Swift.php +++ b/lib/private/Files/ObjectStore/Swift.php @@ -25,6 +25,12 @@ namespace OC\Files\ObjectStore; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\ClientException; +use GuzzleHttp\Exception\ConnectException; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Exception\BadResponseException; +use GuzzleHttp\HandlerStack; use function GuzzleHttp\Psr7\stream_for; use Icewind\Streams\RetryWrapper; use OCP\Files\NotFoundException; @@ -89,30 +95,33 @@ class Swift implements IObjectStore { /** * @param string $urn the unified resource name used to identify the object * @return resource stream with the read data - * @throws \Exception from openstack lib when something goes wrong + * @throws \Exception from openstack or GuzzleHttp libs when something goes wrong * @throws NotFoundException if file does not exist */ public function readObject($urn) { try { - $object = $this->getContainer()->getObject($urn); - - // we need to keep a reference to objectContent or - // the stream will be closed before we can do anything with it - $objectContent = $object->download(); - } catch (BadResponseError $e) { - if ($e->getResponse()->getStatusCode() === 404) { + $publicUri = $this->getContainer()->getObject($urn)->getPublicUri(); + $tokenId = $this->swiftFactory->getCachedTokenId(); + + $response = (new Client())->request('GET', $publicUri, + [ + 'stream' => true, + 'headers' => [ + 'X-Auth-Token' => $tokenId, + 'Cache-Control' => 'no-cache' + ], + ] + ); + + } catch (BadResponseException $e) { + if ($e->getResponse() && $e->getResponse()->getStatusCode() === 404) { throw new NotFoundException("object $urn not found in object store"); } else { throw $e; } } - $objectContent->rewind(); - - $stream = $objectContent->detach(); - // save the object content in the context of the stream to prevent it being gc'd until the stream is closed - stream_context_set_option($stream, 'swift', 'content', $objectContent); - return RetryWrapper::wrap($stream); + return RetryWrapper::wrap($response->getBody()->detach()); } /** diff --git a/lib/private/Files/ObjectStore/SwiftFactory.php b/lib/private/Files/ObjectStore/SwiftFactory.php index 7c8a1b995b4..6950b15d3f2 100644 --- a/lib/private/Files/ObjectStore/SwiftFactory.php +++ b/lib/private/Files/ObjectStore/SwiftFactory.php @@ -69,6 +69,25 @@ class SwiftFactory { $this->logger = $logger; } + /** + * Gets currently cached token id + * + * @return string + * @throws StorageAuthException + */ + public function getCachedTokenId() { + if ( !isset($this->params['cachedToken']) ) { + throw new StorageAuthException('Unauthenticated ObjectStore connection'); + } + + // Is it V2 token? + if ( isset($this->params['cachedToken']['token']) ) { + return $this->params['cachedToken']['token']['id']; + } + + return $this->params['cachedToken']['id']; + } + private function getCachedToken(string $cacheKey) { $cachedTokenString = $this->cache->get($cacheKey . '/token'); if ($cachedTokenString) { @@ -81,10 +100,10 @@ class SwiftFactory { private function cacheToken(Token $token, string $serviceUrl, string $cacheKey) { if ($token instanceof \OpenStack\Identity\v3\Models\Token) { // for v3 the catalog is cached as part of the token, so no need to cache $serviceUrl separately - $value = json_encode($token->export()); + $value = $token->export(); } else { /** @var \OpenStack\Identity\v2\Models\Token $token */ - $value = json_encode([ + $value = [ 'serviceUrl' => $serviceUrl, 'token' => [ 'issued_at' => $token->issuedAt->format('c'), @@ -92,9 +111,11 @@ class SwiftFactory { 'id' => $token->id, 'tenant' => $token->tenant ] - ]); + ]; } - $this->cache->set($cacheKey . '/token', $value); + + $this->params['cachedToken'] = $value; + $this->cache->set($cacheKey . '/token', json_encode($value)); } /** -- 2.39.5