MediaWiki
REL1_22
|
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 return $ret; 00046 } 00047 00057 public function batchFetchFromURLs( array $urls ) { 00058 $batched = $inverseUrlMap = array(); 00059 foreach ( $urls as $url ) { 00060 list( $cluster, $id, $itemID ) = $this->parseURL( $url ); 00061 $batched[$cluster][$id][] = $itemID; 00062 // false $itemID gets cast to int, but should be ok 00063 // since we do === from the $itemID in $batched 00064 $inverseUrlMap[$cluster][$id][$itemID] = $url; 00065 } 00066 $ret = array(); 00067 foreach ( $batched as $cluster => $batchByCluster ) { 00068 $res = $this->batchFetchBlobs( $cluster, $batchByCluster ); 00069 foreach ( $res as $id => $blob ) { 00070 foreach ( $batchByCluster[$id] as $itemID ) { 00071 $url = $inverseUrlMap[$cluster][$id][$itemID]; 00072 if ( $itemID === false ) { 00073 $ret[$url] = $blob; 00074 } else { 00075 $ret[$url] = $blob->getItem( $itemID ); 00076 } 00077 } 00078 } 00079 } 00080 return $ret; 00081 } 00082 00086 public function store( $cluster, $data ) { 00087 $dbw = $this->getMaster( $cluster ); 00088 $id = $dbw->nextSequenceValue( 'blob_blob_id_seq' ); 00089 $dbw->insert( $this->getTable( $dbw ), 00090 array( 'blob_id' => $id, 'blob_text' => $data ), 00091 __METHOD__ ); 00092 $id = $dbw->insertId(); 00093 if ( !$id ) { 00094 throw new MWException( __METHOD__ . ': no insert ID' ); 00095 } 00096 if ( $dbw->getFlag( DBO_TRX ) ) { 00097 $dbw->commit( __METHOD__ ); 00098 } 00099 return "DB://$cluster/$id"; 00100 } 00101 00108 function &getLoadBalancer( $cluster ) { 00109 $wiki = isset( $this->params['wiki'] ) ? $this->params['wiki'] : false; 00110 00111 return wfGetLBFactory()->getExternalLB( $cluster, $wiki ); 00112 } 00113 00120 function &getSlave( $cluster ) { 00121 global $wgDefaultExternalStore; 00122 00123 $wiki = isset( $this->params['wiki'] ) ? $this->params['wiki'] : false; 00124 $lb =& $this->getLoadBalancer( $cluster ); 00125 00126 if ( !in_array( "DB://" . $cluster, (array)$wgDefaultExternalStore ) ) { 00127 wfDebug( "read only external store" ); 00128 $lb->allowLagged( true ); 00129 } else { 00130 wfDebug( "writable external store" ); 00131 } 00132 00133 return $lb->getConnection( DB_SLAVE, array(), $wiki ); 00134 } 00135 00142 function &getMaster( $cluster ) { 00143 $wiki = isset( $this->params['wiki'] ) ? $this->params['wiki'] : false; 00144 $lb =& $this->getLoadBalancer( $cluster ); 00145 return $lb->getConnection( DB_MASTER, array(), $wiki ); 00146 } 00147 00154 function getTable( &$db ) { 00155 $table = $db->getLBInfo( 'blobs table' ); 00156 if ( is_null( $table ) ) { 00157 $table = 'blobs'; 00158 } 00159 return $table; 00160 } 00161 00172 function &fetchBlob( $cluster, $id, $itemID ) { 00179 static $externalBlobCache = array(); 00180 00181 $cacheID = ( $itemID === false ) ? "$cluster/$id" : "$cluster/$id/"; 00182 if ( isset( $externalBlobCache[$cacheID] ) ) { 00183 wfDebugLog( 'ExternalStoreDB-cache', 00184 "ExternalStoreDB::fetchBlob cache hit on $cacheID\n" ); 00185 return $externalBlobCache[$cacheID]; 00186 } 00187 00188 wfDebugLog( 'ExternalStoreDB-cache', 00189 "ExternalStoreDB::fetchBlob cache miss on $cacheID\n" ); 00190 00191 $dbr =& $this->getSlave( $cluster ); 00192 $ret = $dbr->selectField( $this->getTable( $dbr ), 00193 'blob_text', array( 'blob_id' => $id ), __METHOD__ ); 00194 if ( $ret === false ) { 00195 wfDebugLog( 'ExternalStoreDB', 00196 "ExternalStoreDB::fetchBlob master fallback on $cacheID\n" ); 00197 // Try the master 00198 $dbw =& $this->getMaster( $cluster ); 00199 $ret = $dbw->selectField( $this->getTable( $dbw ), 00200 'blob_text', array( 'blob_id' => $id ), __METHOD__ ); 00201 if ( $ret === false ) { 00202 wfDebugLog( 'ExternalStoreDB', 00203 "ExternalStoreDB::fetchBlob master failed to find $cacheID\n" ); 00204 } 00205 } 00206 if ( $itemID !== false && $ret !== false ) { 00207 // Unserialise object; caller extracts item 00208 $ret = unserialize( $ret ); 00209 } 00210 00211 $externalBlobCache = array( $cacheID => &$ret ); 00212 return $ret; 00213 } 00214 00222 function batchFetchBlobs( $cluster, array $ids ) { 00223 $dbr = $this->getSlave( $cluster ); 00224 $res = $dbr->select( $this->getTable( $dbr ), 00225 array( 'blob_id', 'blob_text' ), array( 'blob_id' => array_keys( $ids ) ), __METHOD__ ); 00226 $ret = array(); 00227 if ( $res !== false ) { 00228 $this->mergeBatchResult( $ret, $ids, $res ); 00229 } 00230 if ( $ids ) { 00231 wfDebugLog( __CLASS__, __METHOD__ . 00232 " master fallback on '$cluster' for: " . 00233 implode( ',', array_keys( $ids ) ) . "\n" ); 00234 // Try the master 00235 $dbw = $this->getMaster( $cluster ); 00236 $res = $dbw->select( $this->getTable( $dbr ), 00237 array( 'blob_id', 'blob_text' ), 00238 array( 'blob_id' => array_keys( $ids ) ), 00239 __METHOD__ ); 00240 if ( $res === false ) { 00241 wfDebugLog( __CLASS__, __METHOD__ . " master failed on '$cluster'\n" ); 00242 } else { 00243 $this->mergeBatchResult( $ret, $ids, $res ); 00244 } 00245 } 00246 if ( $ids ) { 00247 wfDebugLog( __CLASS__, __METHOD__ . 00248 " master on '$cluster' failed locating items: " . 00249 implode( ',', array_keys( $ids ) ) . "\n" ); 00250 } 00251 return $ret; 00252 } 00253 00260 private function mergeBatchResult( array &$ret, array &$ids, $res ) { 00261 foreach ( $res as $row ) { 00262 $id = $row->blob_id; 00263 $itemIDs = $ids[$id]; 00264 unset( $ids[$id] ); // to track if everything is found 00265 if ( count( $itemIDs ) === 1 && reset( $itemIDs ) === false ) { 00266 // single result stored per blob 00267 $ret[$id] = $row->blob_text; 00268 } else { 00269 // multi result stored per blob 00270 $ret[$id] = unserialize( $row->blob_text ); 00271 } 00272 } 00273 } 00274 00275 protected function parseURL( $url ) { 00276 $path = explode( '/', $url ); 00277 return array( 00278 $path[2], // cluster 00279 $path[3], // id 00280 isset( $path[4] ) ? $path[4] : false // itemID 00281 ); 00282 } 00283 }