Index: trunk/phase3/includes/filerepo/backend/FileBackendMultiWrite.php |
— | — | @@ -32,9 +32,7 @@ |
33 | 33 | |
34 | 34 | /** |
35 | 35 | * Construct a proxy backend that consists of several internal backends. |
36 | | - * $config contains: |
37 | | - * 'name' : The name of the proxy backend |
38 | | - * 'lockManager' : Registered name of the file lock manager to use |
| 36 | + * Additional $config params include: |
39 | 37 | * 'backends' : Array of backend config and multi-backend settings. |
40 | 38 | * Each value is the config used in the constructor of a |
41 | 39 | * FileBackend class, but with these additional settings: |
Index: trunk/phase3/includes/filerepo/backend/FileOp.php |
— | — | @@ -26,6 +26,7 @@ |
27 | 27 | protected $state = self::STATE_NEW; // integer |
28 | 28 | protected $failed = false; // boolean |
29 | 29 | protected $useBackups = true; // boolean |
| 30 | + protected $useLatest = true; // boolean |
30 | 31 | protected $destSameAsSource = false; // boolean |
31 | 32 | protected $destAlreadyExists = false; // boolean |
32 | 33 | |
— | — | @@ -61,6 +62,17 @@ |
62 | 63 | } |
63 | 64 | |
64 | 65 | /** |
| 66 | + * Allow stale data for file reads and existence checks. |
| 67 | + * If this is called, then disableBackups() should also be called |
| 68 | + * unless the affected files are known to have not changed recently. |
| 69 | + * |
| 70 | + * @return void |
| 71 | + */ |
| 72 | + final protected function allowStaleReads() { |
| 73 | + $this->useLatest = false; |
| 74 | + } |
| 75 | + |
| 76 | + /** |
65 | 77 | * Attempt a series of file operations. |
66 | 78 | * Callers are responsible for handling file locking. |
67 | 79 | * |
— | — | @@ -71,10 +83,14 @@ |
72 | 84 | final public static function attemptBatch( array $performOps, array $opts ) { |
73 | 85 | $status = Status::newGood(); |
74 | 86 | |
| 87 | + $allowStale = isset( $opts['allowStale'] ) && $opts['allowStale']; |
75 | 88 | $ignoreErrors = isset( $opts['ignoreErrors'] ) && $opts['ignoreErrors']; |
76 | 89 | $predicates = FileOp::newPredicates(); // account for previous op in prechecks |
77 | 90 | // Do pre-checks for each operation; abort on failure... |
78 | 91 | foreach ( $performOps as $index => $fileOp ) { |
| 92 | + if ( $allowStale ) { |
| 93 | + $fileOp->allowStaleReads(); // allow potentially stale reads |
| 94 | + } |
79 | 95 | $status->merge( $fileOp->precheck( $predicates ) ); |
80 | 96 | if ( !$status->isOK() ) { // operation failed? |
81 | 97 | if ( $ignoreErrors ) { |
— | — | @@ -320,7 +336,7 @@ |
321 | 337 | $status = Status::newGood(); |
322 | 338 | if ( $this->useBackups ) { |
323 | 339 | // Check if a file already exists at the source... |
324 | | - $params = array( 'src' => $this->params['src'] ); |
| 340 | + $params = array( 'src' => $this->params['src'], 'latest' => $this->useLatest ); |
325 | 341 | if ( $this->backend->fileExists( $params ) ) { |
326 | 342 | // Create a temporary backup copy... |
327 | 343 | $this->tmpSourcePath = $this->backend->getLocalCopy( $params ); |
— | — | @@ -350,7 +366,7 @@ |
351 | 367 | if ( $this->getParam( 'overwriteDest' ) ) { |
352 | 368 | if ( $this->useBackups ) { |
353 | 369 | // Create a temporary backup copy... |
354 | | - $params = array( 'src' => $this->params['dst'] ); |
| 370 | + $params = array( 'src' => $this->params['dst'], 'latest' => $this->useLatest ); |
355 | 371 | $this->tmpDestFile = $this->backend->getLocalCopy( $params ); |
356 | 372 | if ( !$this->tmpDestFile ) { |
357 | 373 | $status->fatal( 'backend-fail-backup', $this->params['dst'] ); |
— | — | @@ -402,7 +418,8 @@ |
403 | 419 | if ( FileBackend::isStoragePath( $path ) ) { |
404 | 420 | // For some backends (e.g. Swift, Azure) we can get |
405 | 421 | // standard hashes to use for this types of comparisons. |
406 | | - $hash = $this->backend->getFileSha1Base36( array( 'src' => $path ) ); |
| 422 | + $params = array( 'src' => $path, 'latest' => $this->useLatest ); |
| 423 | + $hash = $this->backend->getFileSha1Base36( $params ); |
407 | 424 | // Source file is on file system |
408 | 425 | } else { |
409 | 426 | wfSuppressWarnings(); |
— | — | @@ -470,7 +487,8 @@ |
471 | 488 | if ( isset( $predicates['exists'][$source] ) ) { |
472 | 489 | return $predicates['exists'][$source]; // previous op assures this |
473 | 490 | } else { |
474 | | - return $this->backend->fileExists( array( 'src' => $source ) ); |
| 491 | + $params = array( 'src' => $source, 'latest' => $this->useLatest ); |
| 492 | + return $this->backend->fileExists( $params ); |
475 | 493 | } |
476 | 494 | } |
477 | 495 | |
Index: trunk/phase3/includes/filerepo/backend/FSFileBackend.php |
— | — | @@ -337,9 +337,9 @@ |
338 | 338 | } |
339 | 339 | |
340 | 340 | /** |
341 | | - * @see FileBackend::fileExists() |
| 341 | + * @see FileBackend::doFileExists() |
342 | 342 | */ |
343 | | - public function fileExists( array $params ) { |
| 343 | + protected function doFileExists( array $params ) { |
344 | 344 | list( $c, $source ) = $this->resolveStoragePathReal( $params['src'] ); |
345 | 345 | if ( $source === null ) { |
346 | 346 | return false; // invalid storage path |
— | — | @@ -351,9 +351,9 @@ |
352 | 352 | } |
353 | 353 | |
354 | 354 | /** |
355 | | - * @see FileBackend::getFileTimestamp() |
| 355 | + * @see FileBackend::doGetFileTimestamp() |
356 | 356 | */ |
357 | | - public function getFileTimestamp( array $params ) { |
| 357 | + public function doGetFileTimestamp( array $params ) { |
358 | 358 | list( $c, $source ) = $this->resolveStoragePathReal( $params['src'] ); |
359 | 359 | if ( $source === null ) { |
360 | 360 | return false; // invalid storage path |
Index: trunk/phase3/includes/filerepo/backend/FileBackend.php |
— | — | @@ -37,9 +37,9 @@ |
38 | 38 | * This should only be called from within FileBackendGroup. |
39 | 39 | * |
40 | 40 | * $config includes: |
41 | | - * 'name' : The unique name of this backend |
42 | | - * 'wikiId' : Prefix to container names that is unique to this wiki |
43 | | - * 'lockManager' : Registered name of a file lock manager to use |
| 41 | + * 'name' : The unique name of this backend. |
| 42 | + * 'wikiId' : Prefix to container names that is unique to this wiki. |
| 43 | + * 'lockManager' : Registered name of a file lock manager to use. |
44 | 44 | * 'readOnly' : Write operations are disallowed if this is a non-empty string. |
45 | 45 | * It should be an explanation for the backend being read-only. |
46 | 46 | * |
— | — | @@ -783,6 +783,48 @@ |
784 | 784 | } |
785 | 785 | |
786 | 786 | /** |
| 787 | + * @see FileBackendBase::fileExists() |
| 788 | + */ |
| 789 | + final public function fileExists( array $params ) { |
| 790 | + $path = $params['src']; |
| 791 | + if ( isset( $this->cache[$path]['exists'] ) ) { |
| 792 | + return $this->cache[$path]['exists']; |
| 793 | + } |
| 794 | + $exists = $this->doFileExists( $params ); |
| 795 | + if ( $exists ) { // don't cache negatives |
| 796 | + $this->trimCache(); // limit memory |
| 797 | + $this->cache[$path]['exists'] = $exists; |
| 798 | + } |
| 799 | + return $exists; |
| 800 | + } |
| 801 | + |
| 802 | + /** |
| 803 | + * @see FileBackend::fileExists() |
| 804 | + */ |
| 805 | + abstract protected function doFileExists( array $params ); |
| 806 | + |
| 807 | + /** |
| 808 | + * @see FileBackendBase::getFileTimestamp() |
| 809 | + */ |
| 810 | + final public function getFileTimestamp( array $params ) { |
| 811 | + $path = $params['src']; |
| 812 | + if ( isset( $this->cache[$path]['timestamp'] ) ) { |
| 813 | + return $this->cache[$path]['timestamp']; |
| 814 | + } |
| 815 | + $timestamp = $this->doGetFileTimestamp( $params ); |
| 816 | + if ( $timestamp ) { // don't cache negatives |
| 817 | + $this->trimCache(); // limit memory |
| 818 | + $this->cache[$path]['timestamp'] = $timestamp; |
| 819 | + } |
| 820 | + return $timestamp; |
| 821 | + } |
| 822 | + |
| 823 | + /** |
| 824 | + * @see FileBackend::getFileTimestamp() |
| 825 | + */ |
| 826 | + abstract protected function doGetFileTimestamp( array $params ); |
| 827 | + |
| 828 | + /** |
787 | 829 | * @see FileBackendBase::getFileContents() |
788 | 830 | */ |
789 | 831 | public function getFileContents( array $params ) { |
— | — | @@ -804,16 +846,23 @@ |
805 | 847 | if ( isset( $this->cache[$path]['sha1'] ) ) { |
806 | 848 | return $this->cache[$path]['sha1']; |
807 | 849 | } |
| 850 | + $hash = $this->doGetFileSha1Base36( $params ); |
| 851 | + if ( $hash ) { // don't cache negatives |
| 852 | + $this->trimCache(); // limit memory |
| 853 | + $this->cache[$path]['sha1'] = $hash; |
| 854 | + } |
| 855 | + return $hash; |
| 856 | + } |
| 857 | + |
| 858 | + /** |
| 859 | + * @see FileBackend::getFileSha1Base36() |
| 860 | + */ |
| 861 | + protected function doGetFileSha1Base36( array $params ) { |
808 | 862 | $fsFile = $this->getLocalReference( $params ); |
809 | 863 | if ( !$fsFile ) { |
810 | 864 | return false; |
811 | 865 | } else { |
812 | | - $sha1 = $fsFile->getSha1Base36(); |
813 | | - if ( $sha1 !== false ) { // don't cache negatives |
814 | | - $this->trimCache(); // limit memory |
815 | | - $this->cache[$path]['sha1'] = $sha1; |
816 | | - } |
817 | | - return $sha1; |
| 866 | + return $fsFile->getSha1Base36(); |
818 | 867 | } |
819 | 868 | } |
820 | 869 | |
— | — | @@ -833,7 +882,16 @@ |
834 | 883 | * @see FileBackendBase::getLocalReference() |
835 | 884 | */ |
836 | 885 | public function getLocalReference( array $params ) { |
837 | | - return $this->getLocalCopy( $params ); |
| 886 | + $path = $params['src']; |
| 887 | + if ( isset( $this->cache[$path]['localRef'] ) ) { |
| 888 | + return $this->cache[$path]['localRef']; |
| 889 | + } |
| 890 | + $tmpFile = $this->getLocalCopy( $params ); |
| 891 | + if ( $tmpFile ) { // don't cache negatives |
| 892 | + $this->trimCache(); // limit memory |
| 893 | + $this->cache[$path]['localRef'] = $tmpFile; |
| 894 | + } |
| 895 | + return $tmpFile; |
838 | 896 | } |
839 | 897 | |
840 | 898 | /** |