MediaWiki
REL1_24
|
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 00100 return "DB://$cluster/$id"; 00101 } 00102 00109 function getLoadBalancer( $cluster ) { 00110 $wiki = isset( $this->params['wiki'] ) ? $this->params['wiki'] : false; 00111 00112 return wfGetLBFactory()->getExternalLB( $cluster, $wiki ); 00113 } 00114 00121 function getSlave( $cluster ) { 00122 global $wgDefaultExternalStore; 00123 00124 $wiki = isset( $this->params['wiki'] ) ? $this->params['wiki'] : false; 00125 $lb = $this->getLoadBalancer( $cluster ); 00126 00127 if ( !in_array( "DB://" . $cluster, (array)$wgDefaultExternalStore ) ) { 00128 wfDebug( "read only external store\n" ); 00129 $lb->allowLagged( true ); 00130 } else { 00131 wfDebug( "writable external store\n" ); 00132 } 00133 00134 $db = $lb->getConnection( DB_SLAVE, array(), $wiki ); 00135 $db->clearFlag( DBO_TRX ); // sanity 00136 00137 return $db; 00138 } 00139 00146 function getMaster( $cluster ) { 00147 $wiki = isset( $this->params['wiki'] ) ? $this->params['wiki'] : false; 00148 $lb = $this->getLoadBalancer( $cluster ); 00149 00150 $db = $lb->getConnection( DB_MASTER, array(), $wiki ); 00151 $db->clearFlag( DBO_TRX ); // sanity 00152 00153 return $db; 00154 } 00155 00162 function getTable( $db ) { 00163 $table = $db->getLBInfo( 'blobs table' ); 00164 if ( is_null( $table ) ) { 00165 $table = 'blobs'; 00166 } 00167 00168 return $table; 00169 } 00170 00181 function fetchBlob( $cluster, $id, $itemID ) { 00188 static $externalBlobCache = array(); 00189 00190 $cacheID = ( $itemID === false ) ? "$cluster/$id" : "$cluster/$id/"; 00191 if ( isset( $externalBlobCache[$cacheID] ) ) { 00192 wfDebugLog( 'ExternalStoreDB-cache', 00193 "ExternalStoreDB::fetchBlob cache hit on $cacheID" ); 00194 00195 return $externalBlobCache[$cacheID]; 00196 } 00197 00198 wfDebugLog( 'ExternalStoreDB-cache', 00199 "ExternalStoreDB::fetchBlob cache miss on $cacheID" ); 00200 00201 $dbr = $this->getSlave( $cluster ); 00202 $ret = $dbr->selectField( $this->getTable( $dbr ), 00203 'blob_text', array( 'blob_id' => $id ), __METHOD__ ); 00204 if ( $ret === false ) { 00205 wfDebugLog( 'ExternalStoreDB', 00206 "ExternalStoreDB::fetchBlob master fallback on $cacheID" ); 00207 // Try the master 00208 $dbw = $this->getMaster( $cluster ); 00209 $ret = $dbw->selectField( $this->getTable( $dbw ), 00210 'blob_text', array( 'blob_id' => $id ), __METHOD__ ); 00211 if ( $ret === false ) { 00212 wfDebugLog( 'ExternalStoreDB', 00213 "ExternalStoreDB::fetchBlob master failed to find $cacheID" ); 00214 } 00215 } 00216 if ( $itemID !== false && $ret !== false ) { 00217 // Unserialise object; caller extracts item 00218 $ret = unserialize( $ret ); 00219 } 00220 00221 $externalBlobCache = array( $cacheID => $ret ); 00222 00223 return $ret; 00224 } 00225 00234 function batchFetchBlobs( $cluster, array $ids ) { 00235 $dbr = $this->getSlave( $cluster ); 00236 $res = $dbr->select( $this->getTable( $dbr ), 00237 array( 'blob_id', 'blob_text' ), array( 'blob_id' => array_keys( $ids ) ), __METHOD__ ); 00238 $ret = array(); 00239 if ( $res !== false ) { 00240 $this->mergeBatchResult( $ret, $ids, $res ); 00241 } 00242 if ( $ids ) { 00243 wfDebugLog( __CLASS__, __METHOD__ . 00244 " master fallback on '$cluster' for: " . 00245 implode( ',', array_keys( $ids ) ) ); 00246 // Try the master 00247 $dbw = $this->getMaster( $cluster ); 00248 $res = $dbw->select( $this->getTable( $dbr ), 00249 array( 'blob_id', 'blob_text' ), 00250 array( 'blob_id' => array_keys( $ids ) ), 00251 __METHOD__ ); 00252 if ( $res === false ) { 00253 wfDebugLog( __CLASS__, __METHOD__ . " master failed on '$cluster'" ); 00254 } else { 00255 $this->mergeBatchResult( $ret, $ids, $res ); 00256 } 00257 } 00258 if ( $ids ) { 00259 wfDebugLog( __CLASS__, __METHOD__ . 00260 " master on '$cluster' failed locating items: " . 00261 implode( ',', array_keys( $ids ) ) ); 00262 } 00263 00264 return $ret; 00265 } 00266 00273 private function mergeBatchResult( array &$ret, array &$ids, $res ) { 00274 foreach ( $res as $row ) { 00275 $id = $row->blob_id; 00276 $itemIDs = $ids[$id]; 00277 unset( $ids[$id] ); // to track if everything is found 00278 if ( count( $itemIDs ) === 1 && reset( $itemIDs ) === false ) { 00279 // single result stored per blob 00280 $ret[$id] = $row->blob_text; 00281 } else { 00282 // multi result stored per blob 00283 $ret[$id] = unserialize( $row->blob_text ); 00284 } 00285 } 00286 } 00287 00292 protected function parseURL( $url ) { 00293 $path = explode( '/', $url ); 00294 00295 return array( 00296 $path[2], // cluster 00297 $path[3], // id 00298 isset( $path[4] ) ? $path[4] : false // itemID 00299 ); 00300 } 00301 }