Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 38 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 1 |
ExternalStoreMwstore | |
0.00% |
0 / 38 |
|
0.00% |
0 / 5 |
240 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
fetchFromURL | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
batchFetchFromURLs | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
20 | |||
store | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
20 | |||
isReadOnly | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | |
21 | use MediaWiki\WikiMap\WikiMap; |
22 | |
23 | /** |
24 | * External storage in a FileBackend. |
25 | * |
26 | * In this system, each store "location" maps to the name of a file backend. |
27 | * The file backends must be defined in $wgFileBackends and must be global |
28 | * and fully qualified with a global "wikiId" prefix in the configuration. |
29 | * |
30 | * @see ExternalStoreAccess |
31 | * @ingroup ExternalStorage |
32 | * @since 1.21 |
33 | */ |
34 | class ExternalStoreMwstore extends ExternalStoreMedium { |
35 | /** @var FileBackendGroup */ |
36 | private $fbGroup; |
37 | |
38 | /** |
39 | * @see ExternalStoreMedium::__construct() |
40 | * @param array $params Additional parameters include: |
41 | * - fbGroup: a FileBackendGroup instance |
42 | */ |
43 | public function __construct( array $params ) { |
44 | parent::__construct( $params ); |
45 | if ( !isset( $params['fbGroup'] ) || !( $params['fbGroup'] instanceof FileBackendGroup ) ) { |
46 | throw new InvalidArgumentException( "FileBackendGroup required in 'fbGroup' field." ); |
47 | } |
48 | $this->fbGroup = $params['fbGroup']; |
49 | } |
50 | |
51 | /** |
52 | * Fetch data from a given external store URL |
53 | * |
54 | * @see ExternalStoreMedium::fetchFromURL() |
55 | * @param string $url An external store URL in the form of mwstore://backend/container/wiki/id |
56 | * @return string|bool |
57 | */ |
58 | public function fetchFromURL( $url ) { |
59 | $be = $this->fbGroup->backendFromPath( $url ); |
60 | if ( $be instanceof FileBackend ) { |
61 | // We don't need "latest" since objects are immutable and |
62 | // backends should at least have "read-after-create" consistency. |
63 | return $be->getFileContents( [ 'src' => $url ] ); |
64 | } |
65 | |
66 | return false; |
67 | } |
68 | |
69 | /** |
70 | * Fetch data from given external store URLs. |
71 | * The URLs are in the form of mwstore://backend/container/wiki/id |
72 | * |
73 | * @param array $urls An array of external store URLs |
74 | * @return array A map from url to stored content. Failed results are not represented. |
75 | */ |
76 | public function batchFetchFromURLs( array $urls ) { |
77 | $pathsByBackend = []; |
78 | foreach ( $urls as $url ) { |
79 | $be = $this->fbGroup->backendFromPath( $url ); |
80 | if ( $be instanceof FileBackend ) { |
81 | $pathsByBackend[$be->getName()][] = $url; |
82 | } |
83 | } |
84 | $blobs = []; |
85 | foreach ( $pathsByBackend as $backendName => $paths ) { |
86 | $be = $this->fbGroup->get( $backendName ); |
87 | $blobs += $be->getFileContentsMulti( [ 'srcs' => $paths ] ); |
88 | } |
89 | |
90 | return $blobs; |
91 | } |
92 | |
93 | public function store( $backend, $data ) { |
94 | $be = $this->fbGroup->get( $backend ); |
95 | // Get three random base 36 characters to act as shard directories |
96 | $rand = Wikimedia\base_convert( (string)mt_rand( 0, 46655 ), 10, 36, 3 ); |
97 | // Make sure ID is roughly lexicographically increasing for performance |
98 | $id = str_pad( UIDGenerator::newTimestampedUID128( 32 ), 26, '0', STR_PAD_LEFT ); |
99 | // Segregate items by DB domain ID for the sake of bookkeeping |
100 | $domain = $this->isDbDomainExplicit |
101 | ? $this->dbDomain |
102 | // @FIXME: this does not include the schema for b/c but it ideally should |
103 | : WikiMap::getWikiIdFromDbDomain( $this->dbDomain ); |
104 | $url = $be->getContainerStoragePath( 'data' ) . '/' . rawurlencode( $domain ); |
105 | // Use directory/container sharding |
106 | $url .= ( $be instanceof FSFileBackend ) |
107 | ? "/{$rand[0]}/{$rand[1]}/{$rand[2]}/{$id}" // keep directories small |
108 | : "/{$rand[0]}/{$rand[1]}/{$id}"; // container sharding is only 2-levels |
109 | |
110 | $be->prepare( [ 'dir' => dirname( $url ), 'noAccess' => 1, 'noListing' => 1 ] ); |
111 | $status = $be->create( [ 'dst' => $url, 'content' => $data ] ); |
112 | |
113 | if ( $status->isOK() ) { |
114 | return $url; |
115 | } |
116 | |
117 | throw new ExternalStoreException( __METHOD__ . ": operation failed: $status" ); |
118 | } |
119 | |
120 | public function isReadOnly( $backend ) { |
121 | if ( parent::isReadOnly( $backend ) ) { |
122 | return true; |
123 | } |
124 | |
125 | $be = $this->fbGroup->get( $backend ); |
126 | |
127 | return $be->isReadOnly(); |
128 | } |
129 | } |