[FileBackend] Added optional callback parameter to concatenate().
authorAaron Schulz <aschulz@wikimedia.org>
Sun, 18 Nov 2012 22:02:45 +0000 (14:02 -0800)
committerAaron Schulz <aschulz@wikimedia.org>
Sun, 18 Nov 2012 22:02:45 +0000 (14:02 -0800)
* This lets callers update the cache/db with the operation progress so that
  APIs can be made to use this and expose this information to client polling.

Change-Id: I9a86c1c5ffccf029be5a150e5998c3ce4740ec62

includes/filebackend/FileBackend.php
includes/filebackend/FileBackendStore.php
includes/filerepo/FileRepo.php

index b5e2315..f1a67c8 100644 (file)
@@ -586,11 +586,21 @@ abstract class FileBackend {
         * otherwise safe from modification from other processes. Normally,
         * the file will be a new temp file, which should be adequate.
         *
+        * If a callback function is given, it will be called each time a segment is
+        * appended and when the overall concatenate operation completes or fails.
+        * The arguments passed in are:
+        *   - 1) A Status object containing errors if any problems occurred.
+        *   - 2) The index of the relevant segment (starting at 1) if a segment was appended
+        *        (including the last one) or null in the case of overall success or failure.
+        * When a good Status is returned with a null segment, then the operation completed.
+        * Callbacks should generally avoid throwing exceptions.
+        *
         * @param $params Array Operation parameters
         * $params include:
         *   - srcs        : ordered source storage paths (e.g. chunk1, chunk2, ...)
         *   - dst         : file system path to 0-byte temp file
         *   - parallelize : try to do operations in parallel when possible
+        *   - callback    : closure called when chunks are appended and on success/failure
         * @return Status
         */
        abstract public function concatenate( array $params );
index 0f435a3..38d58ac 100644 (file)
@@ -310,8 +310,12 @@ abstract class FileBackendStore extends FileBackend {
         */
        protected function doConcatenate( array $params ) {
                $status = Status::newGood();
+
                $tmpPath = $params['dst']; // convenience
                unset( $params['latest'] ); // sanity
+               $callback = isset( $params['callback'] )
+                       ? $params['callback']
+                       : function( Status $status, $segment ) {};
 
                // Check that the specified temp file is valid...
                wfSuppressWarnings();
@@ -319,6 +323,7 @@ abstract class FileBackendStore extends FileBackend {
                wfRestoreWarnings();
                if ( !$ok ) { // not present or not empty
                        $status->fatal( 'backend-fail-opentemp', $tmpPath );
+                       $callback( $status, null ); // update progress
                        return $status;
                }
 
@@ -329,6 +334,7 @@ abstract class FileBackendStore extends FileBackend {
                                $fsFile = $this->getLocalReference( array( 'src' => $path ) );
                                if ( !$fsFile ) { // retry failed?
                                        $status->fatal( 'backend-fail-read', $path );
+                                       $callback( $status, null ); // update progress
                                        return $status;
                                }
                        }
@@ -339,16 +345,20 @@ abstract class FileBackendStore extends FileBackend {
                $tmpHandle = fopen( $tmpPath, 'ab' );
                if ( $tmpHandle === false ) {
                        $status->fatal( 'backend-fail-opentemp', $tmpPath );
+                       $callback( $status, null ); // update progress
                        return $status;
                }
 
+               $segment = 0; // segment number
                // Build up the temp file using the source chunks (in order)...
                foreach ( $fsFiles as $virtualSource => $fsFile ) {
+                       ++$segment; // first segment is "1"
                        // Get a handle to the local FS version
                        $sourceHandle = fopen( $fsFile->getPath(), 'rb' );
                        if ( $sourceHandle === false ) {
                                fclose( $tmpHandle );
                                $status->fatal( 'backend-fail-read', $virtualSource );
+                               $callback( $status, null ); // update progress
                                return $status;
                        }
                        // Append chunk to file (pass chunk size to avoid magic quotes)
@@ -356,16 +366,20 @@ abstract class FileBackendStore extends FileBackend {
                                fclose( $sourceHandle );
                                fclose( $tmpHandle );
                                $status->fatal( 'backend-fail-writetemp', $tmpPath );
+                               $callback( $status , null );
                                return $status;
                        }
                        fclose( $sourceHandle );
+                       $callback( $status, $segment ); // update progress (chunk success)
                }
                if ( !fclose( $tmpHandle ) ) {
                        $status->fatal( 'backend-fail-closetemp', $tmpPath );
+                       $callback( $status, null ); // update progress
                        return $status;
                }
 
                clearstatcache(); // temp file changed
+               $callback( $status, null ); // update progress (full success)
 
                return $status;
        }
index 651ee27..782ebf2 100644 (file)
@@ -988,9 +988,10 @@ class FileRepo {
         * @param $dstPath String Target file system path
         * @param $flags Integer: bitwise combination of the following flags:
         *     self::DELETE_SOURCE     Delete the source files
+        * @param $callback Closure Optional callback function (see FileBackend::concatenate())
         * @return FileRepoStatus
         */
-       public function concatenate( array $srcPaths, $dstPath, $flags = 0 ) {
+       public function concatenate( array $srcPaths, $dstPath, $flags = 0, Closure $callback = null ) {
                $this->assertWritableRepo(); // fail out if read-only
 
                $status = $this->newGood();
@@ -1003,7 +1004,7 @@ class FileRepo {
                }
 
                // Concatenate the chunks into one FS file
-               $params = array( 'srcs' => $sources, 'dst' => $dstPath );
+               $params = array( 'srcs' => $sources, 'dst' => $dstPath, 'callback' => $callback );
                $status->merge( $this->backend->concatenate( $params ) );
                if ( !$status->isOK() ) {
                        return $status;