]> source.dussan.org Git - nextcloud-server.git/commitdiff
It's a fix for issue #14116, 18883/head
authorAdrian Brzezinski <adrian.brzezinski@eo.pl>
Tue, 14 Jan 2020 08:47:18 +0000 (09:47 +0100)
committerAdrian Brzezinski <adrian.brzezinski@eo.pl>
Mon, 27 Apr 2020 12:16:50 +0000 (14:16 +0200)
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 <adrian.brzezinski@eo.pl>
lib/private/Files/ObjectStore/Swift.php
lib/private/Files/ObjectStore/SwiftFactory.php

index 9a2aa82295e8f8c4737d41270bb6a9d58f7fa5d0..e0d819f8a2ae133252b0502a84e87d7675e068b0 100644 (file)
 
 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());
        }
 
        /**
index 7c8a1b995b46c766ab3f0e08e81a13bc5cd0da52..6950b15d3f23f0ffa0d3bdda7a0676a1952a4061 100644 (file)
@@ -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));
        }
 
        /**