Merge "Fix and make PHPDoc tags in FileBackend more specific"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 18 Mar 2015 16:45:20 +0000 (16:45 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 18 Mar 2015 16:45:20 +0000 (16:45 +0000)
1  2 
includes/filebackend/FileBackendStore.php
includes/filebackend/SwiftFileBackend.php

@@@ -118,7 -118,7 +118,7 @@@ abstract class FileBackendStore extend
         * @return Status
         */
        final public function createInternal( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                if ( strlen( $params['content'] ) > $this->maxFileSizeInternal() ) {
                        $status = Status::newFatal( 'backend-fail-maxsize',
                                $params['dst'], $this->maxFileSizeInternal() );
         * @return Status
         */
        final public function storeInternal( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                if ( filesize( $params['src'] ) > $this->maxFileSizeInternal() ) {
                        $status = Status::newFatal( 'backend-fail-maxsize',
                                $params['dst'], $this->maxFileSizeInternal() );
         * @return Status
         */
        final public function copyInternal( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $status = $this->doCopyInternal( $params );
                $this->clearCache( array( $params['dst'] ) );
                if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
         * @return Status
         */
        final public function deleteInternal( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $status = $this->doDeleteInternal( $params );
                $this->clearCache( array( $params['src'] ) );
                $this->deleteFileCache( $params['src'] ); // persistent cache
         * @return Status
         */
        final public function moveInternal( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $status = $this->doMoveInternal( $params );
                $this->clearCache( array( $params['src'], $params['dst'] ) );
                $this->deleteFileCache( $params['src'] ); // persistent cache
         * @return Status
         */
        final public function describeInternal( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                if ( count( $params['headers'] ) ) {
                        $status = $this->doDescribeInternal( $params );
                        $this->clearCache( array( $params['src'] ) );
        }
  
        final public function concatenate( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $status = Status::newGood();
  
                // Try to lock the source files for the scope of this function
        }
  
        final protected function doPrepare( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $status = Status::newGood();
  
                list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
        }
  
        final protected function doSecure( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $status = Status::newGood();
  
                list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
        }
  
        final protected function doPublish( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $status = Status::newGood();
  
                list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
        }
  
        final protected function doClean( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $status = Status::newGood();
  
                // Recursive: first delete all empty subdirs recursively
        }
  
        final public function fileExists( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $stat = $this->getFileStat( $params );
  
                return ( $stat === null ) ? null : (bool)$stat; // null => failure
        }
  
        final public function getFileTimestamp( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $stat = $this->getFileStat( $params );
  
                return $stat ? $stat['mtime'] : false;
        }
  
        final public function getFileSize( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $stat = $this->getFileStat( $params );
  
                return $stat ? $stat['size'] : false;
                if ( $path === null ) {
                        return false; // invalid storage path
                }
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $latest = !empty( $params['latest'] ); // use latest data?
 -              if ( !$this->cheapCache->has( $path, 'stat', self::CACHE_TTL ) ) {
 +              if ( !$latest && !$this->cheapCache->has( $path, 'stat', self::CACHE_TTL ) ) {
                        $this->primeFileCache( array( $path ) ); // check persistent cache
                }
                if ( $this->cheapCache->has( $path, 'stat', self::CACHE_TTL ) ) {
                                }
                        }
                }
 -              wfProfileIn( __METHOD__ . '-miss-' . $this->name );
                $stat = $this->doGetFileStat( $params );
 -              wfProfileOut( __METHOD__ . '-miss-' . $this->name );
                if ( is_array( $stat ) ) { // file exists
                        // Strongly consistent backends can automatically set "latest"
                        $stat['latest'] = isset( $stat['latest'] ) ? $stat['latest'] : $latest;
        abstract protected function doGetFileStat( array $params );
  
        public function getFileContentsMulti( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
  
                $params = $this->setConcurrencyFlags( $params );
                $contents = $this->doGetFileContentsMulti( $params );
                if ( $path === null ) {
                        return false; // invalid storage path
                }
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $latest = !empty( $params['latest'] ); // use latest data?
                if ( $this->cheapCache->has( $path, 'xattr', self::CACHE_TTL ) ) {
                        $stat = $this->cheapCache->get( $path, 'xattr' );
                                return $stat['map'];
                        }
                }
 -              wfProfileIn( __METHOD__ . '-miss' );
 -              wfProfileIn( __METHOD__ . '-miss-' . $this->name );
                $fields = $this->doGetFileXAttributes( $params );
                $fields = is_array( $fields ) ? self::normalizeXAttributes( $fields ) : false;
 -              wfProfileOut( __METHOD__ . '-miss-' . $this->name );
 -              wfProfileOut( __METHOD__ . '-miss' );
                $this->cheapCache->set( $path, 'xattr', array( 'map' => $fields, 'latest' => $latest ) );
  
                return $fields;
                if ( $path === null ) {
                        return false; // invalid storage path
                }
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $latest = !empty( $params['latest'] ); // use latest data?
                if ( $this->cheapCache->has( $path, 'sha1', self::CACHE_TTL ) ) {
                        $stat = $this->cheapCache->get( $path, 'sha1' );
                                return $stat['hash'];
                        }
                }
 -              wfProfileIn( __METHOD__ . '-miss-' . $this->name );
                $hash = $this->doGetFileSha1Base36( $params );
 -              wfProfileOut( __METHOD__ . '-miss-' . $this->name );
                $this->cheapCache->set( $path, 'sha1', array( 'hash' => $hash, 'latest' => $latest ) );
  
                return $hash;
        }
  
        final public function getFileProps( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $fsFile = $this->getLocalReference( $params );
                $props = $fsFile ? $fsFile->getProps() : FSFile::placeholderProps();
  
        }
  
        final public function getLocalReferenceMulti( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
  
                $params = $this->setConcurrencyFlags( $params );
  
        }
  
        final public function getLocalCopyMulti( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
  
                $params = $this->setConcurrencyFlags( $params );
                $tmpFiles = $this->doGetLocalCopyMulti( $params );
        }
  
        final public function streamFile( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $status = Status::newGood();
  
                $info = $this->getFileStat( $params );
                if ( $res == StreamFile::NOT_MODIFIED ) {
                        // do nothing; client cache is up to date
                } elseif ( $res == StreamFile::READY_STREAM ) {
 -                      wfProfileIn( __METHOD__ . '-send-' . $this->name );
                        $status = $this->doStreamFile( $params );
 -                      wfProfileOut( __METHOD__ . '-send-' . $this->name );
                        if ( !$status->isOK() ) {
                                // Per bug 41113, nasty things can happen if bad cache entries get
                                // stuck in cache. It's also possible that this error can come up
        }
  
        final protected function doOperationsInternal( array $ops, array $opts ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $status = Status::newGood();
  
                // Fix up custom header name/value pairs...
        }
  
        final protected function doQuickOperationsInternal( array $ops ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $status = Status::newGood();
  
                // Fix up custom header name/value pairs...
         * The resulting Status object fields will correspond
         * to the order in which the handles where given.
         *
-        * @param array $fileOpHandles
+        * @param FileBackendStoreOpHandle[] $fileOpHandles
+        *
         * @throws FileBackendError
-        * @internal param array $handles List of FileBackendStoreOpHandle objects
         * @return array Map of Status objects
         */
        final public function executeOpHandlesInternal( array $fileOpHandles ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
  
                foreach ( $fileOpHandles as $fileOpHandle ) {
                        if ( !( $fileOpHandle instanceof FileBackendStoreOpHandle ) ) {
  
        /**
         * @see FileBackendStore::executeOpHandlesInternal()
-        * @param array $fileOpHandles
+        *
+        * @param FileBackendStoreOpHandle[] $fileOpHandles
+        *
         * @throws FileBackendError
-        * @return array List of corresponding Status objects
+        * @return Status[] List of corresponding Status objects
         */
        protected function doExecuteOpHandlesInternal( array $fileOpHandles ) {
                if ( count( $fileOpHandles ) ) {
        }
  
        final public function preloadFileStat( array $params ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $success = true; // no network errors
  
                $params['concurrency'] = ( $this->parallelize !== 'off' ) ? $this->concurrency : 1;
         * @param array $items
         */
        final protected function primeContainerCache( array $items ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
  
                $paths = array(); // list of storage paths
                $contNames = array(); // (cache key => resolved container name)
         * @param array $items List of storage paths
         */
        final protected function primeFileCache( array $items ) {
 -              $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
  
                $paths = array(); // list of storage paths
                $pathNames = array(); // (cache key => storage path)
                // Get all cache entries for these container cache keys...
                $values = $this->memCache->getMulti( array_keys( $pathNames ) );
                foreach ( $values as $cacheKey => $val ) {
 +                      $path = $pathNames[$cacheKey];
                        if ( is_array( $val ) ) {
 -                              $path = $pathNames[$cacheKey];
 +                              $val['latest'] = false; // never completely trust cache
                                $this->cheapCache->set( $path, 'stat', $val );
                                if ( isset( $val['sha1'] ) ) { // some backends store SHA-1 as metadata
                                        $this->cheapCache->set( $path, 'sha1',
 -                                              array( 'hash' => $val['sha1'], 'latest' => $val['latest'] ) );
 +                                              array( 'hash' => $val['sha1'], 'latest' => false ) );
                                }
                                if ( isset( $val['xattr'] ) ) { // some backends store headers/metadata
                                        $val['xattr'] = self::normalizeXAttributes( $val['xattr'] );
                                        $this->cheapCache->set( $path, 'xattr',
 -                                              array( 'map' => $val['xattr'], 'latest' => $val['latest'] ) );
 +                                              array( 'map' => $val['xattr'], 'latest' => false ) );
                                }
                        }
                }
@@@ -647,7 -647,7 +647,7 @@@ class SwiftFileBackend extends FileBack
                        $timestamp = new MWTimestamp( $ts );
  
                        return $timestamp->getTimestamp( $format );
 -              } catch ( MWException $e ) {
 +              } catch ( Exception $e ) {
                        throw new FileBackendError( $e->getMessage() );
                }
        }
                        return $objHdrs; // nothing to do
                }
  
 -              $section = new ProfileSection( __METHOD__ . '-' . $this->name );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                trigger_error( "$path was not stored with SHA-1 metadata.", E_USER_WARNING );
  
                $auth = $this->getAuthentication();
                        return $dirs; // nothing more
                }
  
 -              $section = new ProfileSection( __METHOD__ . '-' . $this->name );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
  
                $prefix = ( $dir == '' ) ? null : "{$dir}/";
                // Non-recursive: only list dirs right under $dir
                        return $files; // nothing more
                }
  
 -              $section = new ProfileSection( __METHOD__ . '-' . $this->name );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
  
                $prefix = ( $dir == '' ) ? null : "{$dir}/";
                // $objects will contain a list of unfiltered names or CF_Object items
                                        // Convert various random Swift dates to TS_MW
                                        'mtime'  => $this->convertSwiftDate( $object->last_modified, TS_MW ),
                                        'size'   => (int)$object->bytes,
 +                                      'sha1'   => null,
                                        // Note: manifiest ETags are not an MD5 of the file
                                        'md5'    => ctype_xdigit( $object->hash ) ? $object->hash : null,
                                        'latest' => false // eventually consistent
                        $tmpFiles[$path] = $tmpFile;
                }
  
 +              $isLatest = ( $this->isRGW || !empty( $params['latest'] ) );
                $opts = array( 'maxConnsPerHost' => $params['concurrency'] );
                $reqs = $this->http->runMulti( $reqs, $opts );
                foreach ( $reqs as $path => $op ) {
                                        $this->onError( null, __METHOD__,
                                                array( 'src' => $path ) + $ep, $rerr, $rcode, $rdesc );
                                }
 +                              // Set the file stat process cache in passing
 +                              $stat = $this->getStatFromHeaders( $rhdrs );
 +                              $stat['latest'] = $isLatest;
 +                              $this->cheapCache->set( $path, 'stat', $stat );
                        } elseif ( $rcode === 404 ) {
                                $tmpFiles[$path] = false;
                        } else {
                return $hdrs;
        }
  
+       /**
+        * @param FileBackendStoreOpHandle[] $fileOpHandles
+        *
+        * @return Status[]
+        */
        protected function doExecuteOpHandlesInternal( array $fileOpHandles ) {
                $statuses = array();
  
         * @return array|bool|null False on 404, null on failure
         */
        protected function getContainerStat( $container, $bypassCache = false ) {
 -              $section = new ProfileSection( __METHOD__ . '-' . $this->name );
 +              $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
  
                if ( $bypassCache ) { // purge cache
                        $this->containerStatCache->clear( $container );
                                return null;
                        }
  
 -                      wfProfileIn( __METHOD__ . "-{$this->name}-miss" );
                        list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run( array(
                                'method' => 'HEAD',
                                'url' => $this->storageUrl( $auth, $container ),
                                'headers' => $this->authTokenHeaders( $auth )
                        ) );
 -                      wfProfileOut( __METHOD__ . "-{$this->name}-miss" );
  
                        if ( $rcode === 204 ) {
                                $stat = array(
                        if ( $rcode === 200 || $rcode === 204 ) {
                                // Update the object if it is missing some headers
                                $rhdrs = $this->addMissingMetadata( $rhdrs, $path );
 -                              // Fetch all of the custom metadata headers
 -                              $metadata = array();
 -                              foreach ( $rhdrs as $name => $value ) {
 -                                      if ( strpos( $name, 'x-object-meta-' ) === 0 ) {
 -                                              $metadata[substr( $name, strlen( 'x-object-meta-' ) )] = $value;
 -                                      }
 -                              }
 -                              // Fetch all of the custom raw HTTP headers
 -                              $headers = $this->sanitizeHdrs( array( 'headers' => $rhdrs ) );
 -                              $stat = array(
 -                                      // Convert various random Swift dates to TS_MW
 -                                      'mtime' => $this->convertSwiftDate( $rhdrs['last-modified'], TS_MW ),
 -                                      // Empty objects actually return no content-length header in Ceph
 -                                      'size'  => isset( $rhdrs['content-length'] ) ? (int)$rhdrs['content-length'] : 0,
 -                                      'sha1'  => $rhdrs['x-object-meta-sha1base36'],
 -                                      // Note: manifiest ETags are not an MD5 of the file
 -                                      'md5'   => ctype_xdigit( $rhdrs['etag'] ) ? $rhdrs['etag'] : null,
 -                                      'xattr' => array( 'metadata' => $metadata, 'headers' => $headers )
 -                              );
 +                              // Load the stat array from the headers
 +                              $stat = $this->getStatFromHeaders( $rhdrs );
                                if ( $this->isRGW ) {
                                        $stat['latest'] = true; // strong consistency
                                }
                return $stats;
        }
  
 +      /**
 +       * @param array $rhdrs
 +       * @return array
 +       */
 +      protected function getStatFromHeaders( array $rhdrs ) {
 +              // Fetch all of the custom metadata headers
 +              $metadata = array();
 +              foreach ( $rhdrs as $name => $value ) {
 +                      if ( strpos( $name, 'x-object-meta-' ) === 0 ) {
 +                              $metadata[substr( $name, strlen( 'x-object-meta-' ) )] = $value;
 +                      }
 +              }
 +              // Fetch all of the custom raw HTTP headers
 +              $headers = $this->sanitizeHdrs( array( 'headers' => $rhdrs ) );
 +              return array(
 +                      // Convert various random Swift dates to TS_MW
 +                      'mtime' => $this->convertSwiftDate( $rhdrs['last-modified'], TS_MW ),
 +                      // Empty objects actually return no content-length header in Ceph
 +                      'size'  => isset( $rhdrs['content-length'] ) ? (int)$rhdrs['content-length'] : 0,
 +                      'sha1'  => isset( $rhdrs['x-object-meta-sha1base36'] )
 +                              ? $rhdrs['x-object-meta-sha1base36']
 +                              : null,
 +                      // Note: manifiest ETags are not an MD5 of the file
 +                      'md5'   => ctype_xdigit( $rhdrs['etag'] ) ? $rhdrs['etag'] : null,
 +                      'xattr' => array( 'metadata' => $metadata, 'headers' => $headers )
 +              );
 +      }
 +
        /**
         * @return array|null Credential map
         */
                        }
                        // Ceph RGW does not use <account> in URLs (OpenStack Swift uses "/v1/<account>")
                        if ( substr( $this->authCreds['storage_url'], -3 ) === '/v1' ) {
 -                              $this->isRGW = true; // take advantage of strong consistency
 +                              $this->isRGW = true; // take advantage of strong consistency in Ceph
                        }
                }