[MCR] Introduce BlobStoreFactory
authoraddshore <addshorewiki@gmail.com>
Sat, 23 Dec 2017 17:14:28 +0000 (17:14 +0000)
committeraddshore <addshorewiki@gmail.com>
Sun, 24 Dec 2017 23:22:30 +0000 (23:22 +0000)
This allows Revision::getRevisionText to get
a different BlobStore instance when $wiki is passed in
restoring the behaviour for $wiki before the MCR Revision
overhaul patch was merged.
Ia4c20a91e98df0b9b14b138eb4825c55e5200384

Bug: T183634
Bug: T183631
bug: T183583
Change-Id: Ib0949454e9a003c2965adc1aab38e31fcf121afe

autoload.php
includes/MediaWikiServices.php
includes/Revision.php
includes/ServiceWiring.php
includes/Storage/BlobStoreFactory.php [new file with mode: 0644]
includes/Storage/SqlBlobStore.php
tests/phpunit/includes/MediaWikiServicesTest.php
tests/phpunit/includes/RevisionTest.php
tests/phpunit/includes/Storage/BlobStoreFactoryTest.php [new file with mode: 0644]

index 6b8387b..c37d9f7 100644 (file)
@@ -944,6 +944,7 @@ $wgAutoloadLocalClasses = [
        'MediaWiki\\Site\\MediaWikiPageNameNormalizer' => __DIR__ . '/includes/site/MediaWikiPageNameNormalizer.php',
        'MediaWiki\\Storage\\BlobAccessException' => __DIR__ . '/includes/Storage/BlobAccessException.php',
        'MediaWiki\\Storage\\BlobStore' => __DIR__ . '/includes/Storage/BlobStore.php',
+       'MediaWiki\\Storage\\BlobStoreFactory' => __DIR__ . '/includes/Storage/BlobStoreFactory.php',
        'MediaWiki\\Storage\\IncompleteRevisionException' => __DIR__ . '/includes/Storage/IncompleteRevisionException.php',
        'MediaWiki\\Storage\\MutableRevisionRecord' => __DIR__ . '/includes/Storage/MutableRevisionRecord.php',
        'MediaWiki\\Storage\\MutableRevisionSlots' => __DIR__ . '/includes/Storage/MutableRevisionSlots.php',
index 33d0fd4..04c67fb 100644 (file)
@@ -12,6 +12,7 @@ use Hooks;
 use IBufferingStatsdDataFactory;
 use MediaWiki\Shell\CommandFactory;
 use MediaWiki\Storage\BlobStore;
+use MediaWiki\Storage\BlobStoreFactory;
 use MediaWiki\Storage\RevisionStore;
 use Wikimedia\Rdbms\LBFactory;
 use LinkCache;
@@ -700,12 +701,20 @@ class MediaWikiServices extends ServiceContainer {
                return $this->getService( 'ExternalStoreFactory' );
        }
 
+       /**
+        * @since 1.31
+        * @return BlobStoreFactory
+        */
+       public function getBlobStoreFactory() {
+               return $this->getService( 'BlobStoreFactory' );
+       }
+
        /**
         * @since 1.31
         * @return BlobStore
         */
        public function getBlobStore() {
-               return $this->getService( 'BlobStore' );
+               return $this->getService( '_SqlBlobStore' );
        }
 
        /**
index ed0646a..af4fef5 100644 (file)
@@ -65,10 +65,14 @@ class Revision implements IDBAccessObject {
        }
 
        /**
+        * @param bool|string $wikiId The ID of the target wiki database. Use false for the local wiki.
+        *
         * @return SqlBlobStore
         */
-       protected static function getBlobStore() {
-               $store = MediaWikiServices::getInstance()->getBlobStore();
+       protected static function getBlobStore( $wiki = false ) {
+               $store = MediaWikiServices::getInstance()
+                       ->getBlobStoreFactory()
+                       ->newSqlBlobStore( $wiki );
 
                if ( !$store instanceof SqlBlobStore ) {
                        throw new RuntimeException(
@@ -942,7 +946,7 @@ class Revision implements IDBAccessObject {
 
                $cacheKey = isset( $row->old_id ) ? ( 'tt:' . $row->old_id ) : null;
 
-               return self::getBlobStore()->expandBlob( $text, $flags, $cacheKey );
+               return self::getBlobStore( $wiki )->expandBlob( $text, $flags, $cacheKey );
        }
 
        /**
index d21bcef..0d266fb 100644 (file)
@@ -42,6 +42,7 @@ use MediaWiki\Linker\LinkRendererFactory;
 use MediaWiki\Logger\LoggerFactory;
 use MediaWiki\MediaWikiServices;
 use MediaWiki\Shell\CommandFactory;
+use MediaWiki\Storage\BlobStoreFactory;
 use MediaWiki\Storage\RevisionStore;
 use MediaWiki\Storage\SqlBlobStore;
 
@@ -474,28 +475,22 @@ return [
                return $store;
        },
 
+       'BlobStoreFactory' => function ( MediaWikiServices $services ) {
+               global $wgContLang;
+               return new BlobStoreFactory(
+                       $services->getDBLoadBalancer(),
+                       $services->getMainWANObjectCache(),
+                       $services->getMainConfig(),
+                       $wgContLang
+               );
+       },
+
        'BlobStore' => function ( MediaWikiServices $services ) {
                return $services->getService( '_SqlBlobStore' );
        },
 
        '_SqlBlobStore' => function ( MediaWikiServices $services ) {
-               global $wgContLang; // TODO: manage $wgContLang as a service
-
-               $store = new SqlBlobStore(
-                       $services->getDBLoadBalancer(),
-                       $services->getMainWANObjectCache()
-               );
-
-               $config = $services->getMainConfig();
-               $store->setCompressBlobs( $config->get( 'CompressRevisions' ) );
-               $store->setCacheExpiry( $config->get( 'RevisionCacheExpiry' ) );
-               $store->setUseExternalStore( $config->get( 'DefaultExternalStore' ) !== false );
-
-               if ( $config->get( 'LegacyEncoding' ) ) {
-                       $store->setLegacyEncoding( $config->get( 'LegacyEncoding' ), $wgContLang );
-               }
-
-               return $store;
+               return $services->getBlobStoreFactory()->newSqlBlobStore();
        },
 
        ///////////////////////////////////////////////////////////////////////////
diff --git a/includes/Storage/BlobStoreFactory.php b/includes/Storage/BlobStoreFactory.php
new file mode 100644 (file)
index 0000000..63ca74d
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\Storage;
+
+use Config;
+use Language;
+use WANObjectCache;
+use Wikimedia\Rdbms\LoadBalancer;
+
+/**
+ * Service for instantiating BlobStores
+ *
+ * This can be used to create BlobStore objects for other wikis.
+ *
+ * @since 1.31
+ */
+class BlobStoreFactory {
+
+       /**
+        * @var LoadBalancer
+        */
+       private $loadBalancer;
+
+       /**
+        * @var WANObjectCache
+        */
+       private $cache;
+
+       /**
+        * @var Config
+        */
+       private $config;
+
+       /**
+        * @var Language
+        */
+       private $contLang;
+
+       public function __construct(
+               LoadBalancer $loadBalancer,
+               WANObjectCache $cache,
+               Config $mainConfig,
+               Language $contLang
+       ) {
+               $this->loadBalancer = $loadBalancer;
+               $this->cache = $cache;
+               $this->config = $mainConfig;
+               $this->contLang = $contLang;
+       }
+
+       /**
+        * @since 1.31
+        *
+        * @param bool|string $wikiId The ID of the target wiki database. Use false for the local wiki.
+        *
+        * @return BlobStore
+        */
+       public function newBlobStore( $wikiId = false ) {
+               return $this->newSqlBlobStore( $wikiId );
+       }
+
+       /**
+        * @internal Please call newBlobStore and use the BlobStore interface.
+        *
+        * @param bool|string $wikiId The ID of the target wiki database. Use false for the local wiki.
+        *
+        * @return SqlBlobStore
+        */
+       public function newSqlBlobStore( $wikiId = false ) {
+               $store = new SqlBlobStore(
+                       $this->loadBalancer,
+                       $this->cache,
+                       $wikiId
+               );
+
+               $store->setCompressBlobs( $this->config->get( 'CompressRevisions' ) );
+               $store->setCacheExpiry( $this->config->get( 'RevisionCacheExpiry' ) );
+               $store->setUseExternalStore( $this->config->get( 'DefaultExternalStore' ) !== false );
+
+               if ( $this->config->get( 'LegacyEncoding' ) ) {
+                       $store->setLegacyEncoding( $this->config->get( 'LegacyEncoding' ), $this->contLang );
+               }
+
+               return $store;
+       }
+
+}
index fcdc1b9..69e1539 100644 (file)
@@ -382,7 +382,7 @@ class SqlBlobStore implements IDBAccessObject, BlobStore {
                                return false;
                        }
 
-                       if ( $cacheKey ) {
+                       if ( $cacheKey && $this->wikiId === false ) {
                                // Make use of the wiki-local revision text cache.
                                // The cached value should be decompressed, so handle that and return here.
                                // NOTE: we rely on $this->cache being the right cache for $this->wikiId!
index 6bab16f..dbb7799 100644 (file)
@@ -8,6 +8,7 @@ use MediaWiki\Services\SalvageableService;
 use MediaWiki\Services\ServiceDisabledException;
 use MediaWiki\Shell\CommandFactory;
 use MediaWiki\Storage\BlobStore;
+use MediaWiki\Storage\BlobStoreFactory;
 use MediaWiki\Storage\RevisionStore;
 use MediaWiki\Storage\SqlBlobStore;
 
@@ -334,6 +335,7 @@ class MediaWikiServicesTest extends MediaWikiTestCase {
                        'LocalServerObjectCache' => [ 'LocalServerObjectCache', BagOStuff::class ],
                        'VirtualRESTServiceClient' => [ 'VirtualRESTServiceClient', VirtualRESTServiceClient::class ],
                        'ShellCommandFactory' => [ 'ShellCommandFactory', CommandFactory::class ],
+                       'BlobStoreFactory' => [ 'BlobStoreFactory', BlobStoreFactory::class ],
                        'BlobStore' => [ 'BlobStore', BlobStore::class ],
                        '_SqlBlobStore' => [ '_SqlBlobStore', SqlBlobStore::class ],
                        'RevisionStore' => [ 'RevisionStore', RevisionStore::class ],
index 566dc92..80257cc 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 
+use MediaWiki\Storage\BlobStoreFactory;
 use MediaWiki\Storage\RevisionStore;
 use MediaWiki\Storage\SqlBlobStore;
 use Wikimedia\Rdbms\IDatabase;
@@ -289,7 +290,7 @@ class RevisionTest extends MediaWikiTestCase {
                        ) );
 
                // Note override internal service, so RevisionStore uses it as well.
-               $this->setService( '_SqlBlobStore', $blobStore );
+               $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) );
 
                $row = (object)$arrayData;
                $rev = new Revision( $row, 0, $this->getMockTitle() );
@@ -435,6 +436,20 @@ class RevisionTest extends MediaWikiTestCase {
                return $blobStore;
        }
 
+       private function mockBlobStoreFactory( $blobStore ) {
+               /** @var LoadBalancer $lb */
+               $factory = $this->getMockBuilder( BlobStoreFactory::class )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $factory->expects( $this->any() )
+                       ->method( 'newBlobStore' )
+                       ->willReturn( $blobStore );
+               $factory->expects( $this->any() )
+                       ->method( 'newSqlBlobStore' )
+                       ->willReturn( $blobStore );
+               return $factory;
+       }
+
        /**
         * @return RevisionStore
         */
@@ -478,7 +493,7 @@ class RevisionTest extends MediaWikiTestCase {
        public function testGetRevisionWithLegacyEncoding( $expected, $lang, $encoding, $rowData ) {
                $blobStore = $this->getBlobStore();
                $blobStore->setLegacyEncoding( $encoding, Language::factory( $lang ) );
-               $this->setService( 'BlobStore', $blobStore );
+               $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) );
 
                $this->testGetRevisionText( $expected, $rowData );
        }
@@ -518,7 +533,7 @@ class RevisionTest extends MediaWikiTestCase {
 
                $blobStore = $this->getBlobStore();
                $blobStore->setLegacyEncoding( $encoding, Language::factory( $lang ) );
-               $this->setService( 'BlobStore', $blobStore );
+               $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) );
 
                $this->testGetRevisionText( $expected, $rowData );
        }
@@ -548,7 +563,7 @@ class RevisionTest extends MediaWikiTestCase {
 
                $blobStore = $this->getBlobStore();
                $blobStore->setCompressBlobs( true );
-               $this->setService( 'BlobStore', $blobStore );
+               $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) );
 
                $row = new stdClass;
                $row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
@@ -693,7 +708,7 @@ class RevisionTest extends MediaWikiTestCase {
                        $blobStore->setLegacyEncoding( $legacyEncoding, Language::factory( 'en' ) );
                }
 
-               $this->setService( 'BlobStore', $blobStore );
+               $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) );
                $this->assertSame(
                        $expected,
                        Revision::decompressRevisionText( $text, $flags )
@@ -802,7 +817,7 @@ class RevisionTest extends MediaWikiTestCase {
                        ->getMock();
 
                $blobStore = new SqlBlobStore( $lb, $cache );
-               $this->setService( 'BlobStore', $blobStore );
+               $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) );
 
                $this->assertSame(
                        'AAAABBAAA',
diff --git a/tests/phpunit/includes/Storage/BlobStoreFactoryTest.php b/tests/phpunit/includes/Storage/BlobStoreFactoryTest.php
new file mode 100644 (file)
index 0000000..46ba7a5
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+namespace MediaWiki\Tests\Storage;
+
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\BlobStore;
+use MediaWiki\Storage\SqlBlobStore;
+use MediaWikiTestCase;
+use Wikimedia\TestingAccessWrapper;
+
+/**
+ * @covers MediaWiki\Storage\BlobStore
+ */
+class BlobStoreFactoryTest extends MediaWikiTestCase {
+
+       public function provideWikiIds() {
+               yield [ false ];
+               yield [ 'someWiki' ];
+       }
+
+       /**
+        * @dataProvider provideWikiIds
+        */
+       public function testNewBlobStore( $wikiId ) {
+               $factory = MediaWikiServices::getInstance()->getBlobStoreFactory();
+               $store = $factory->newBlobStore( $wikiId );
+               $this->assertInstanceOf( BlobStore::class, $store );
+
+               // This only works as we currently know this is a SqlBlobStore object
+               $wrapper = TestingAccessWrapper::newFromObject( $store );
+               $this->assertEquals( $wikiId, $wrapper->wikiId );
+       }
+
+       /**
+        * @dataProvider provideWikiIds
+        */
+       public function testNewSqlBlobStore( $wikiId ) {
+               $factory = MediaWikiServices::getInstance()->getBlobStoreFactory();
+               $store = $factory->newSqlBlobStore( $wikiId );
+               $this->assertInstanceOf( SqlBlobStore::class, $store );
+
+               $wrapper = TestingAccessWrapper::newFromObject( $store );
+               $this->assertEquals( $wikiId, $wrapper->wikiId );
+       }
+
+}