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
193 return $lb->getMaintenanceConnectionRef(
196 $this->
getDomainId( $lb->getServerInfo( $lb->getWriterIndex() ) ),
197 $lb::CONN_TRX_AUTOCOMMIT
206 if ( $this->isDbDomainExplicit ) {
210 if ( isset( $server[
'dbname'] ) ) {
218 $server[
'schema'] ??
null,
219 $server[
'tablePrefix'] ??
''
222 return $domain->getId();
236 if ( $cluster !==
null ) {
238 $info = $lb->getServerInfo( $lb->getWriterIndex() );
239 if ( isset( $info[
'blobs table'] ) ) {
240 return $info[
'blobs table'];
244 return $db->getLBInfo(
'blobs table' ) ??
'blobs';
257 static $supportedTypes = [
'mysql',
'sqlite' ];
260 if ( !in_array( $dbw->getType(), $supportedTypes,
true ) ) {
261 throw new DBUnexpectedError( $dbw,
"RDBMS type '{$dbw->getType()}' not supported." );
264 $sqlFilePath =
"$IP/maintenance/storage/blobs.sql";
265 $sql = file_get_contents( $sqlFilePath );
266 if ( $sql ===
false ) {
267 throw new RuntimeException(
"Failed to read '$sqlFilePath'." );
270 $rawTable = $this->
getTable( $dbw, $cluster );
271 $encTable = $dbw->tableName( $rawTable );
274 [
'/*$wgDBprefix*/blobs',
'/*_*/blobs' ],
275 [ $encTable, $encTable ],
279 $dbw::QUERY_IGNORE_DBO_TRX
299 static $externalBlobCache = [];
301 $cacheID = ( $itemID === false ) ?
"$cluster/$id" :
"$cluster/$id/";
302 $cacheID =
"$cacheID@{$this->dbDomain}";
304 if ( isset( $externalBlobCache[$cacheID] ) ) {
305 $this->logger->debug(
"ExternalStoreDB::fetchBlob cache hit on $cacheID" );
307 return $externalBlobCache[$cacheID];
310 $this->logger->debug(
"ExternalStoreDB::fetchBlob cache miss on $cacheID" );
313 $ret =
$dbr->selectField(
316 [
'blob_id' => $id ],
319 if ( $ret ===
false ) {
320 $this->logger->info(
"ExternalStoreDB::fetchBlob master fallback on $cacheID" );
323 $ret = $dbw->selectField(
326 [
'blob_id' => $id ],
329 if ( $ret ===
false ) {
330 $this->logger->error(
"ExternalStoreDB::fetchBlob master failed to find $cacheID" );
333 if ( $itemID !==
false && $ret !==
false ) {
338 $externalBlobCache = [ $cacheID => $ret ];
355 [
'blob_id',
'blob_text' ],
356 [
'blob_id' => array_keys( $ids ) ],
361 if (
$res !==
false ) {
366 __METHOD__ .
": master fallback on '$cluster' for: " .
367 implode(
',', array_keys( $ids ) )
373 [
'blob_id',
'blob_text' ],
374 [
'blob_id' => array_keys( $ids ) ],
376 if (
$res ===
false ) {
377 $this->logger->error( __METHOD__ .
": master failed on '$cluster'" );
383 $this->logger->error(
384 __METHOD__ .
": master on '$cluster' failed locating items: " .
385 implode(
',', array_keys( $ids ) )
399 foreach (
$res as $row ) {
401 $itemIDs = $ids[$id];
403 if ( count( $itemIDs ) === 1 && reset( $itemIDs ) ===
false ) {
405 $ret[$id] = $row->blob_text;
418 $path = explode(
'/', $url );
unserialize( $serialized)
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
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 ...