MediaWiki
REL1_23
|
00001 <?php 00031 class ExternalStoreDB extends ExternalStoreMedium { 00038 public function fetchFromURL( $url ) { 00039 list( $cluster, $id, $itemID ) = $this->parseURL( $url ); 00040 $ret = $this->fetchBlob( $cluster, $id, $itemID ); 00041 00042 if ( $itemID !== false && $ret !== false ) { 00043 return $ret->getItem( $itemID ); 00044 } 00045 00046 return $ret; 00047 } 00048 00058 public function batchFetchFromURLs( array $urls ) { 00059 $batched = $inverseUrlMap = array(); 00060 foreach ( $urls as $url ) { 00061 list( $cluster, $id, $itemID ) = $this->parseURL( $url ); 00062 $batched[$cluster][$id][] = $itemID; 00063 // false $itemID gets cast to int, but should be ok 00064 // since we do === from the $itemID in $batched 00065 $inverseUrlMap[$cluster][$id][$itemID] = $url; 00066 } 00067 $ret = array(); 00068 foreach ( $batched as $cluster => $batchByCluster ) { 00069 $res = $this->batchFetchBlobs( $cluster, $batchByCluster ); 00071 foreach ( $res as $id => $blob ) { 00072 foreach ( $batchByCluster[$id] as $itemID ) { 00073 $url = $inverseUrlMap[$cluster][$id][$itemID]; 00074 if ( $itemID === false ) { 00075 $ret[$url] = $blob; 00076 } else { 00077 $ret[$url] = $blob->getItem( $itemID ); 00078 } 00079 } 00080 } 00081 } 00082 00083 return $ret; 00084 } 00085 00089 public function store( $cluster, $data ) { 00090 $dbw = $this->getMaster( $cluster ); 00091 $id = $dbw->nextSequenceValue( 'blob_blob_id_seq' ); 00092 $dbw->insert( $this->getTable( $dbw ), 00093 array( 'blob_id' => $id, 'blob_text' => $data ), 00094 __METHOD__ ); 00095 $id = $dbw->insertId(); 00096 if ( !$id ) { 00097 throw new MWException( __METHOD__ . ': no insert ID' ); 00098 } 00099 if ( $dbw->getFlag( DBO_TRX ) ) { 00100 $dbw->commit( __METHOD__ ); 00101 } 00102 00103 return "DB://$cluster/$id"; 00104 } 00105 00112 function getLoadBalancer( $cluster ) { 00113 $wiki = isset( $this->params['wiki'] ) ? $this->params['wiki'] : false; 00114 00115 return wfGetLBFactory()->getExternalLB( $cluster, $wiki ); 00116 } 00117 00124 function getSlave( $cluster ) { 00125 global $wgDefaultExternalStore; 00126 00127 $wiki = isset( $this->params['wiki'] ) ? $this->params['wiki'] : false; 00128 $lb = $this->getLoadBalancer( $cluster ); 00129 00130 if ( !in_array( "DB://" . $cluster, (array)$wgDefaultExternalStore ) ) { 00131 wfDebug( "read only external store\n" ); 00132 $lb->allowLagged( true ); 00133 } else { 00134 wfDebug( "writable external store\n" ); 00135 } 00136 00137 return $lb->getConnection( DB_SLAVE, array(), $wiki ); 00138 } 00139 00146 function getMaster( $cluster ) { 00147 $wiki = isset( $this->params['wiki'] ) ? $this->params['wiki'] : false; 00148 $lb = $this->getLoadBalancer( $cluster ); 00149 00150 return $lb->getConnection( DB_MASTER, array(), $wiki ); 00151 } 00152 00159 function getTable( $db ) { 00160 $table = $db->getLBInfo( 'blobs table' ); 00161 if ( is_null( $table ) ) { 00162 $table = 'blobs'; 00163 } 00164 00165 return $table; 00166 } 00167 00178 function fetchBlob( $cluster, $id, $itemID ) { 00185 static $externalBlobCache = array(); 00186 00187 $cacheID = ( $itemID === false ) ? "$cluster/$id" : "$cluster/$id/"; 00188 if ( isset( $externalBlobCache[$cacheID] ) ) { 00189 wfDebugLog( 'ExternalStoreDB-cache', 00190 "ExternalStoreDB::fetchBlob cache hit on $cacheID" ); 00191 00192 return $externalBlobCache[$cacheID]; 00193 } 00194 00195 wfDebugLog( 'ExternalStoreDB-cache', 00196 "ExternalStoreDB::fetchBlob cache miss on $cacheID" ); 00197 00198 $dbr = $this->getSlave( $cluster ); 00199 $ret = $dbr->selectField( $this->getTable( $dbr ), 00200 'blob_text', array( 'blob_id' => $id ), __METHOD__ ); 00201 if ( $ret === false ) { 00202 wfDebugLog( 'ExternalStoreDB', 00203 "ExternalStoreDB::fetchBlob master fallback on $cacheID" ); 00204 // Try the master 00205 $dbw = $this->getMaster( $cluster ); 00206 $ret = $dbw->selectField( $this->getTable( $dbw ), 00207 'blob_text', array( 'blob_id' => $id ), __METHOD__ ); 00208 if ( $ret === false ) { 00209 wfDebugLog( 'ExternalStoreDB', 00210 "ExternalStoreDB::fetchBlob master failed to find $cacheID" ); 00211 } 00212 } 00213 if ( $itemID !== false && $ret !== false ) { 00214 // Unserialise object; caller extracts item 00215 $ret = unserialize( $ret ); 00216 } 00217 00218 $externalBlobCache = array( $cacheID => $ret ); 00219 00220 return $ret; 00221 } 00222 00231 function batchFetchBlobs( $cluster, array $ids ) { 00232 $dbr = $this->getSlave( $cluster ); 00233 $res = $dbr->select( $this->getTable( $dbr ), 00234 array( 'blob_id', 'blob_text' ), array( 'blob_id' => array_keys( $ids ) ), __METHOD__ ); 00235 $ret = array(); 00236 if ( $res !== false ) { 00237 $this->mergeBatchResult( $ret, $ids, $res ); 00238 } 00239 if ( $ids ) { 00240 wfDebugLog( __CLASS__, __METHOD__ . 00241 " master fallback on '$cluster' for: " . 00242 implode( ',', array_keys( $ids ) ) ); 00243 // Try the master 00244 $dbw = $this->getMaster( $cluster ); 00245 $res = $dbw->select( $this->getTable( $dbr ), 00246 array( 'blob_id', 'blob_text' ), 00247 array( 'blob_id' => array_keys( $ids ) ), 00248 __METHOD__ ); 00249 if ( $res === false ) { 00250 wfDebugLog( __CLASS__, __METHOD__ . " master failed on '$cluster'" ); 00251 } else { 00252 $this->mergeBatchResult( $ret, $ids, $res ); 00253 } 00254 } 00255 if ( $ids ) { 00256 wfDebugLog( __CLASS__, __METHOD__ . 00257 " master on '$cluster' failed locating items: " . 00258 implode( ',', array_keys( $ids ) ) ); 00259 } 00260 00261 return $ret; 00262 } 00263 00270 private function mergeBatchResult( array &$ret, array &$ids, $res ) { 00271 foreach ( $res as $row ) { 00272 $id = $row->blob_id; 00273 $itemIDs = $ids[$id]; 00274 unset( $ids[$id] ); // to track if everything is found 00275 if ( count( $itemIDs ) === 1 && reset( $itemIDs ) === false ) { 00276 // single result stored per blob 00277 $ret[$id] = $row->blob_text; 00278 } else { 00279 // multi result stored per blob 00280 $ret[$id] = unserialize( $row->blob_text ); 00281 } 00282 } 00283 } 00284 00285 protected function parseURL( $url ) { 00286 $path = explode( '/', $url ); 00287 00288 return array( 00289 $path[2], // cluster 00290 $path[3], // id 00291 isset( $path[4] ) ? $path[4] : false // itemID 00292 ); 00293 } 00294 }