51 throw new InvalidArgumentException(
"LBFactory required in 'lbFactory' field." );
53 $this->lbFactory =
$params[
'lbFactory'];
65 list( $cluster, $id, $itemID ) = $this->
parseURL( $url );
66 $ret = $this->
fetchBlob( $cluster, $id, $itemID );
68 if ( $itemID !==
false && $ret !==
false ) {
69 return $ret->getItem( $itemID );
85 $batched = $inverseUrlMap = [];
86 foreach (
$urls as $url ) {
87 list( $cluster, $id, $itemID ) = $this->
parseURL( $url );
88 $batched[$cluster][$id][] = $itemID;
91 $inverseUrlMap[$cluster][$id][$itemID] = $url;
94 foreach ( $batched as $cluster => $batchByCluster ) {
98 foreach ( $batchByCluster[$id] as $itemID ) {
99 $url = $inverseUrlMap[$cluster][$id][$itemID];
100 if ( $itemID ===
false ) {
103 $ret[$url] =
$blob->getItem( $itemID );
115 public function store( $location, $data ) {
119 [
'blob_text' => $data ],
122 $id = $dbw->insertId();
124 throw new MWException( __METHOD__ .
': no insert ID' );
127 return "DB://$location/$id";
134 if ( parent::isReadOnly( $location ) ) {
139 $domainId = $this->
getDomainId( $lb->getServerInfo( $lb->getWriterIndex() ) );
141 return ( $lb->getReadOnlyReason( $domainId ) !==
false );
151 return $this->lbFactory->getExternalLB( $cluster );
164 return $lb->getConnectionRef(
167 $this->
getDomainId( $lb->getServerInfo( $lb->getWriterIndex() ) ),
168 $lb::CONN_TRX_AUTOCOMMIT
192 return $lb->getMaintenanceConnectionRef(
195 $this->
getDomainId( $lb->getServerInfo( $lb->getWriterIndex() ) ),
196 $lb::CONN_TRX_AUTOCOMMIT
205 if ( $this->isDbDomainExplicit ) {
209 if ( isset( $server[
'dbname'] ) ) {
217 $server[
'schema'] ??
null,
218 $server[
'tablePrefix'] ??
''
221 return $domain->getId();
235 if ( $cluster !==
null ) {
237 $info = $lb->getServerInfo( $lb->getWriterIndex() );
238 if ( isset( $info[
'blobs table'] ) ) {
239 return $info[
'blobs table'];
243 return $db->getLBInfo(
'blobs table' ) ??
'blobs';
256 static $supportedTypes = [
'mysql',
'sqlite' ];
259 if ( !in_array( $dbw->getType(), $supportedTypes,
true ) ) {
260 throw new DBUnexpectedError( $dbw,
"RDBMS type '{$dbw->getType()}' not supported." );
263 $sqlFilePath =
"$IP/maintenance/storage/blobs.sql";
264 $sql = file_get_contents( $sqlFilePath );
265 if ( $sql ===
false ) {
266 throw new RuntimeException(
"Failed to read '$sqlFilePath'." );
269 $rawTable = $this->
getTable( $dbw, $cluster );
270 $encTable = $dbw->tableName( $rawTable );
273 [
'/*$wgDBprefix*/blobs',
'/*_*/blobs' ],
274 [ $encTable, $encTable ],
278 $dbw::QUERY_IGNORE_DBO_TRX
298 static $externalBlobCache = [];
300 $cacheID = ( $itemID === false ) ?
"$cluster/$id" :
"$cluster/$id/";
301 $cacheID =
"$cacheID@{$this->dbDomain}";
303 if ( isset( $externalBlobCache[$cacheID] ) ) {
304 $this->logger->debug(
"ExternalStoreDB::fetchBlob cache hit on $cacheID" );
306 return $externalBlobCache[$cacheID];
309 $this->logger->debug(
"ExternalStoreDB::fetchBlob cache miss on $cacheID" );
312 $ret =
$dbr->selectField(
315 [
'blob_id' => $id ],
318 if ( $ret ===
false ) {
319 $this->logger->info(
"ExternalStoreDB::fetchBlob master fallback on $cacheID" );
322 $ret = $dbw->selectField(
325 [
'blob_id' => $id ],
328 if ( $ret ===
false ) {
329 $this->logger->error(
"ExternalStoreDB::fetchBlob master failed to find $cacheID" );
332 if ( $itemID !==
false && $ret !==
false ) {
337 $externalBlobCache = [ $cacheID => $ret ];
354 [
'blob_id',
'blob_text' ],
355 [
'blob_id' => array_keys( $ids ) ],
360 if (
$res !==
false ) {
365 __METHOD__ .
": master fallback on '$cluster' for: " .
366 implode(
',', array_keys( $ids ) )
372 [
'blob_id',
'blob_text' ],
373 [
'blob_id' => array_keys( $ids ) ],
375 if (
$res ===
false ) {
376 $this->logger->error( __METHOD__ .
": master failed on '$cluster'" );
382 $this->logger->error(
383 __METHOD__ .
": master on '$cluster' failed locating items: " .
384 implode(
',', array_keys( $ids ) )
398 foreach (
$res as $row ) {
400 $itemIDs = $ids[$id];
402 if ( count( $itemIDs ) === 1 && reset( $itemIDs ) ===
false ) {
404 $ret[$id] = $row->blob_text;
417 $path = explode(
'/', $url );
unserialize( $serialized)
DB accessible external objects.
getSlave( $cluster)
Get a replica DB connection for the specified cluster.
__construct(array $params)
batchFetchBlobs( $cluster, array $ids)
Fetch multiple blob items out of the database.
getReplica( $cluster)
Get a replica DB connection for the specified cluster.
mergeBatchResult(array &$ret, array &$ids, $res)
Helper function for self::batchFetchBlobs for merging master/replica DB results.
getDomainId(array $server)
getMaster( $cluster)
Get a master database connection for the specified cluster.
initializeTable( $cluster)
Create the appropriate blobs table on this cluster.
fetchBlob( $cluster, $id, $itemID)
Fetch a blob item out of the database; a cache of the last-loaded blob will be kept so that multiple ...
fetchFromURL( $url)
The provided URL is in the form of DB://cluster/id or DB://cluster/id/itemid for concatened storage.
getTable( $db, $cluster=null)
Get the 'blobs' table name for this database.
store( $location, $data)
Insert a data item into a given location.string|bool The URL of the stored data item,...
batchFetchFromURLs(array $urls)
Fetch data from given external store URLs.
isReadOnly( $location)
Check if a given location is read-only.bool Whether this location is read-only 1.31
getLoadBalancer( $cluster)
Get a LoadBalancer for the specified cluster.
Key/value blob storage for a particular storage medium type (e.g.
array $params
Usage context options for this instance.
string $dbDomain
Default database domain to store content under.
Class to handle database/schema/prefix specifications for IDatabase.
Helper class to handle automatically marking connections as reusable (via RAII pattern) as well handl...
Base class for general text storage via the "object" flag in old_flags, or two-part external storage ...