Daniel Kinzler has uploaded a new change for review. https://gerrit.wikimedia.org/r/71595
Change subject: (bug 50303) improve cache key for shared cache. ...................................................................... (bug 50303) improve cache key for shared cache. This allows any object cache keys shared between clients (and the repo) to be based on by a) a version ID and b) the repo's name. TODO: make the default value use the repo ID per default. DEPLOYMENT NOTE: This causes cached objects to NOT be shared between wikis per default (see TODO)! When deploying on a wiki farm, make sure to set the sharedCacheKeyPrefix option appropriately: It should be based on the desired repo's database name and a version ID indicating the version of the deployed code. Suggested pattern: <repo>:deploy/<version> Change-Id: I78009d3eb1aed2b42ee53acf6f7bc812e7ee836f --- M client/config/WikibaseClient.example.php M client/includes/WikibaseClient.php M client/includes/parserhooks/PropertyParserFunction.php M client/includes/store/ClientStore.php M client/includes/store/sql/CachingSqlStore.php M client/includes/store/sql/DirectSqlStore.php M docs/options.wiki M lib/config/WikibaseLib.default.php M lib/includes/store/sql/WikiPageEntityLookup.php M repo/config/Wikibase.example.php M repo/includes/WikibaseRepo.php M repo/includes/store/Store.php M repo/includes/store/StoreFactory.php M repo/includes/store/sql/SqlStore.php M repo/tests/phpunit/includes/store/StoreTest.php 15 files changed, 222 insertions(+), 34 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikibase refs/changes/95/71595/1 diff --git a/client/config/WikibaseClient.example.php b/client/config/WikibaseClient.example.php index 9654b3a..9009b53 100644 --- a/client/config/WikibaseClient.example.php +++ b/client/config/WikibaseClient.example.php @@ -65,6 +65,9 @@ $wgWBClientSettings['injectRecentChanges'] = true; $wgWBClientSettings['showExternalRecentChanges'] = true; +// Make sure we use the same keys on repo and clients, so we can share cached objects. +$wgWBClientSettings['sharedCacheKeyPrefix'] = $wgWBClientSettings['repoDatabase'] . ':WBL/' . WBL_VERSION; + $wgLBFactoryConf = array( // In order to seamlessly access a remote wiki, to fetch entity data, // LBFactory_Multi must be used. diff --git a/client/includes/WikibaseClient.php b/client/includes/WikibaseClient.php index a7767a2..ad4039b 100644 --- a/client/includes/WikibaseClient.php +++ b/client/includes/WikibaseClient.php @@ -242,9 +242,11 @@ return $this->storeInstances[$store]; } - $instance = new $class( - $this->getContentLanguage(), - $repoDatabase + // Since we can't know what settings the ClientStore needs, + // delegate the instantiation to a factory method (poor man's builder). + $instance = $class::newFromSettings( + $this->settings, + $this->getContentLanguage() ); assert( $instance instanceof ClientStore ); diff --git a/client/includes/parserhooks/PropertyParserFunction.php b/client/includes/parserhooks/PropertyParserFunction.php index 4d09ab9..1301e70 100644 --- a/client/includes/parserhooks/PropertyParserFunction.php +++ b/client/includes/parserhooks/PropertyParserFunction.php @@ -182,7 +182,7 @@ $targetLanguage = $parser->getTargetLanguage(); $errorFormatter = new ParserErrorMessageFormatter( $targetLanguage ); - $wikibaseClient = WikibaseClient::newInstance(); + $wikibaseClient = WikibaseClient::getDefaultInstance(); $entityLookup = $wikibaseClient->getStore()->getEntityLookup(); $propertyLabelResolver = $wikibaseClient->getStore()->getPropertyLabelResolver(); diff --git a/client/includes/store/ClientStore.php b/client/includes/store/ClientStore.php index 8ed355d..7be44f0 100644 --- a/client/includes/store/ClientStore.php +++ b/client/includes/store/ClientStore.php @@ -2,6 +2,8 @@ namespace Wikibase; +use Language; + /** * Client store interface. * @@ -22,8 +24,6 @@ * * @since 0.1 * - * @todo: provide getXXX() methods for getting local pseudo-singletons (shared service objects). - * * @file * @ingroup WikibaseClient * @@ -34,6 +34,17 @@ interface ClientStore { /** + * Creates a new ClientStore instance based on the given settings. + * Use by StoreFactory to instantiate stores of unknown type. + * + * @param SettingsArray $settings + * @param Language $wikiLanguage + * + * @return ClientStore + */ + public static function newFromSettings( SettingsArray $settings, Language $wikiLanguage ); + + /** * Returns a SiteLinkLookup for this store. * * @since 0.4 diff --git a/client/includes/store/sql/CachingSqlStore.php b/client/includes/store/sql/CachingSqlStore.php index 919d006..c573e80 100644 --- a/client/includes/store/sql/CachingSqlStore.php +++ b/client/includes/store/sql/CachingSqlStore.php @@ -3,6 +3,7 @@ namespace Wikibase; use Language; +use ObjectCache; /** * Implementation of the client store interface using an SQL backend via MediaWiki's @@ -52,10 +53,50 @@ protected $language; /** - * @param Language $wikiLanguage + * @var string */ - public function __construct( Language $wikiLanguage ) { + private $cachePrefix; + + /** + * @var string + */ + private $cacheType; + + /** + * @var int + */ + private $cacheDuration; + + /** + * @param Language $wikiLanguage + * @param string $cachePrefix + * @param $cacheDuration + * @param $cacheType + */ + public function __construct( Language $wikiLanguage, $cachePrefix, $cacheDuration, $cacheType ) { $this->language = $wikiLanguage; + $this->cachePrefix = $cachePrefix; + $this->cacheDuration = $cacheDuration; + $this->cacheType = $cacheType; + } + + /** + * This pseudo-constructor uses the following settings from $settings: + * - sharedCacheKeyPrefix + * - sharedCacheDuration + * - sharedCacheType + * + * @param SettingsArray $settings + * @param Language $wikiLanguage + * + * @return CachingSqlStore + */ + public static function newFromSettings( SettingsArray $settings, Language $wikiLanguage ) { + $cachePrefix = $settings->getSetting( 'sharedCacheKeyPrefix' ); + $cacheDuration = $settings->getSetting( 'sharedCacheDuration' ); + $cacheType = $settings->getSetting( 'sharedCacheType' ); + + return new self( $wikiLanguage, $cachePrefix, $cacheDuration, $cacheType ); } /** @@ -185,7 +226,7 @@ * @return TermIndex */ public function getTermIndex() { - throw new MWException( "Not Implemented, " . __CLASS__ . " is incomplete." ); + throw new \MWException( "Not Implemented, " . __CLASS__ . " is incomplete." ); } /** @@ -194,10 +235,13 @@ * @return PropertyLabelResolver */ protected function newPropertyLabelResolver() { + $key = $this->cachePrefix . ':TermPropertyLabelResolver'; return new TermPropertyLabelResolver( $this->language->getCode(), $this->getTermIndex(), - wfGetMainCache() + ObjectCache::getInstance( $this->cacheType ), + $this->cacheDuration, + $key ); } diff --git a/client/includes/store/sql/DirectSqlStore.php b/client/includes/store/sql/DirectSqlStore.php index ffbd9e5..2b02062 100644 --- a/client/includes/store/sql/DirectSqlStore.php +++ b/client/includes/store/sql/DirectSqlStore.php @@ -3,6 +3,7 @@ namespace Wikibase; use Language; +use ObjectCache; /** * Implementation of the client store interface using direct access to the repository's @@ -72,13 +73,56 @@ private $entityUsageIndex = null; /** - * @param Language $wikiLanguage - * @param string $repoWiki the symbolic database name of the repo wiki + * @var string */ - public function __construct( Language $wikiLanguage, $repoWiki ) { + private $cachePrefix; + + /** + * @var int + */ + private $cacheType; + + /** + * @var int + */ + private $cacheDuration; + + /** + * @param Language $wikiLanguage + * @param string $repoWiki the symbolic database name of the repo wiki + * @param string $cachePrefix + * @param int $cacheDuration + * @param int $cacheType + */ + public function __construct( Language $wikiLanguage, $repoWiki, $cachePrefix, $cacheDuration, $cacheType ) { $this->repoWiki = $repoWiki; + $this->cachePrefix = $cachePrefix; + $this->cacheDuration = $cacheDuration; + $this->cacheType = $cacheType; $this->language = $wikiLanguage; } + + /** + * This pseudo-constructor uses the following settings from $settings: + * - sharedCacheKeyPrefix + * - sharedCacheDuration + * - sharedCacheType + * - repoDatabase + * + * @param SettingsArray $settings + * @param Language $wikiLanguage + * + * @return DirectSqlStore + */ + public static function newFromSettings( SettingsArray $settings, Language $wikiLanguage ) { + $cachePrefix = $settings->getSetting( 'sharedCacheKeyPrefix' ); + $cacheDuration = $settings->getSetting( 'sharedCacheDuration' ); + $cacheType = $settings->getSetting( 'sharedCacheType' ); + $repoWiki = $settings->getSetting( 'repoDatabase' ); + + return new self( $wikiLanguage, $repoWiki, $cachePrefix, $cacheDuration, $cacheType ); + } + /** * @see Store::getEntityUsageIndex @@ -158,8 +202,11 @@ * @return CachingEntityLoader */ protected function newEntityLookup() { - //TODO: get config for persistent cache from config - $lookup = new WikiPageEntityLookup( $this->repoWiki ); // entities are stored in wiki pages + //NOTE: two layers of caching: persistent external cache in WikiPageEntityLookup; + // transient local cache in CachingEntityLoader. + //NOTE: Keep in sync with SqlStore::newEntityLookup on the repo + $key = $this->cachePrefix . ':WikiPageEntityLookup'; + $lookup = new WikiPageEntityLookup( $this->repoWiki, $this->cacheType, $this->cacheDuration, $key ); return new CachingEntityLoader( $lookup ); } @@ -205,10 +252,13 @@ * @return PropertyLabelResolver */ protected function newPropertyLabelResolver() { + $key = $this->cachePrefix . ':TermPropertyLabelResolver'; return new TermPropertyLabelResolver( $this->language->getCode(), $this->getTermIndex(), - wfGetMainCache() + ObjectCache::getInstance( $this->cacheType ), + $this->cacheDuration, + $key ); } diff --git a/docs/options.wiki b/docs/options.wiki index 1dfd557..b3a85e3 100644 --- a/docs/options.wiki +++ b/docs/options.wiki @@ -14,6 +14,10 @@ === Expert Settings === +;sharedCacheKeyPrefix: Prefix to use for cache keys that should be shared among a wikibase repo and all its clients. The default is constructed from $wgDBname and WBL_VERSION. In order to share caches between clients (and the repo), set a prefix based on the repo's name and WBL_VERSION or a similar version ID. +:* NOTE: The default may change in order to use the repo's database name automatically. +;sharedCacheDuration: The default duration of entries in the shared object cache, in seconds. Default is 3600 seconds = 1 hour. +;sharedCacheType: The type of cache to use for the shared object cache. Defaults to $wgMainCacheType. Use CACHE_XXX constants. ;entityPrefixes: ID prefixes to use for the different entity types, as an associative array mapping prefixes to entity type constants. Default: <poem> array( diff --git a/lib/config/WikibaseLib.default.php b/lib/config/WikibaseLib.default.php index 2c4d095..abd4443 100644 --- a/lib/config/WikibaseLib.default.php +++ b/lib/config/WikibaseLib.default.php @@ -59,6 +59,21 @@ // may contain mappings from site-id to db-name. 'localClientDatabases' => array(), + // Prefix to use for cache keys that should be shared among + // a wikibase repo and all its clients. + // The default includes WBL_VERSION and $wgDBname; + // In order to share caches between clients (and the repo), + // set a prefix based on the repo's name and WBL_VERSION + // or a similar version ID. + // @todo: generate the default programmatically, so it can automatically use the right repo ID. + 'sharedCacheKeyPrefix' => $GLOBALS['wgDBname'] . ':WBL/' . WBL_VERSION, + + // The duration of the object cache, in seconds. + 'sharedCacheDuration' => 60 * 60, + + // The type of object cache to use. Use CACHE_XXX constants. + 'sharedCacheType' => $GLOBALS['wgMainCacheType'], + 'dispatchBatchChunkFactor' => 3, 'dispatchBatchCacheFactor' => 3, diff --git a/lib/includes/store/sql/WikiPageEntityLookup.php b/lib/includes/store/sql/WikiPageEntityLookup.php index 51ade77..e33c291 100644 --- a/lib/includes/store/sql/WikiPageEntityLookup.php +++ b/lib/includes/store/sql/WikiPageEntityLookup.php @@ -53,7 +53,7 @@ /** * @var int $cacheTimeout */ - protected $cacheTimeout = 3600; + protected $cacheTimeout; /** * @param String|bool $wiki The name of thw wiki database to use, in a form @@ -66,20 +66,22 @@ * Note that the $wiki parameter determines the cache compartment, * so multiple wikis loading entities from the same repository * will share the cache. + * @param int $cacheDuration Cache duration in seconds. * @param string $cacheKeyPrefix The key prefix to use for constructing cache keys. * Defaults to "wbentity". There should be no reason to change this. * * @return \Wikibase\WikiPageEntityLookup */ - public function __construct( $wiki = false, $cacheType = null, $cacheKeyPrefix = "wbentity" ) { + public function __construct( $wiki = false, $cacheType = null, $cacheDuration = 3600, $cacheKeyPrefix = "wbentity" ) { parent::__construct( $wiki ); if ( $cacheType === null ) { $cacheType = $GLOBALS[ 'wgMainCacheType' ]; } - $this->cacheType = $cacheType; //FIXME: take from Settings: defaultEntityCache + $this->cacheType = $cacheType; $this->cacheKeyPrefix = $cacheKeyPrefix; + $this->cacheTimeout = $cacheDuration; } /** @@ -90,15 +92,10 @@ * @return String */ protected function getEntityCacheKey( EntityId $entityId ) { - global $wgCachePrefix; + $cacheKey = $this->cacheKeyPrefix + . ':' . $entityId->getEntityType() + . ':' . $entityId->getNumericId(); - if ( $this->wiki === false ) { - $keyDB = $wgCachePrefix === false ? wfWikiID() : $wgCachePrefix; - } else { - $keyDB = $this->wiki; - } - - $cacheKey = wfForeignMemcKey( $keyDB, $this->cacheKeyPrefix, $entityId->getPrefixedId() ); return $cacheKey; } diff --git a/repo/config/Wikibase.example.php b/repo/config/Wikibase.example.php index 07598c8..a20438f 100644 --- a/repo/config/Wikibase.example.php +++ b/repo/config/Wikibase.example.php @@ -54,6 +54,9 @@ $wgWBRepoSettings['entityNamespaces'][CONTENT_MODEL_WIKIBASE_ITEM] = WB_NS_ITEM; $wgWBRepoSettings['entityNamespaces'][CONTENT_MODEL_WIKIBASE_PROPERTY] = WB_NS_PROPERTY; +// Make sure we use the same keys on repo and clients, so we can share cached objects. +$wgWBRepoSettings['sharedCacheKeyPrefix'] = $wgDBname . ':WBL/' . WBL_VERSION; + // NOTE: no need to set up $wgNamespaceContentModels, Wikibase will do that automatically based on $wgWBRepoSettings // Tell MediaWIki to search the item namespace diff --git a/repo/includes/WikibaseRepo.php b/repo/includes/WikibaseRepo.php index 3b87e11..334ad1b 100644 --- a/repo/includes/WikibaseRepo.php +++ b/repo/includes/WikibaseRepo.php @@ -263,8 +263,7 @@ * @return Store */ public function getStore() { - //TODO: inject this, get rid of global store instance(s) - return StoreFactory::getStore(); + return $this->store; } } diff --git a/repo/includes/store/Store.php b/repo/includes/store/Store.php index b61037a..8c6c2c2 100644 --- a/repo/includes/store/Store.php +++ b/repo/includes/store/Store.php @@ -31,10 +31,21 @@ * * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroended...@gmail.com > + * @author Daniel Kinzler */ interface Store { /** + * Creates a new Store instance based on the given settings. + * Use by StoreFactory to instantiate stores of unknown type. + * + * @param SettingsArray $settings + * + * @return Store + */ + public static function newFromSettings( SettingsArray $settings ); + + /** * Returns a new SiteLinkCache for this store. * * @since 0.1 diff --git a/repo/includes/store/StoreFactory.php b/repo/includes/store/StoreFactory.php index 88e776f..936429c 100644 --- a/repo/includes/store/StoreFactory.php +++ b/repo/includes/store/StoreFactory.php @@ -38,8 +38,12 @@ return $instances[$store]; } + $settings = Settings::singleton(); $class = $wgWBStores[$store]; - $instance = new $class(); + + // Since we can't know what settings the Store needs, + // delegate the instantiation to a factory method (poor man's builder). + $instance = $class::newFromSettings( $settings ); assert( $instance instanceof Store ); diff --git a/repo/includes/store/sql/SqlStore.php b/repo/includes/store/sql/SqlStore.php index 8869547..99e75b6 100644 --- a/repo/includes/store/sql/SqlStore.php +++ b/repo/includes/store/sql/SqlStore.php @@ -28,6 +28,7 @@ * * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroended...@gmail.com > + * @author Daniel Kinzler */ class SqlStore implements Store { @@ -40,6 +41,50 @@ * @var TermIndex */ private $termIndex = null; + + /** + * @var string + */ + private $cachePrefix; + + /** + * @var int + */ + private $cacheType; + + /** + * @var int + */ + private $cacheDuration; + + /** + * @param string $cachePrefix + * @param int $cacheDuration + * @param int $cacheType + */ + public function __construct( $cachePrefix, $cacheDuration, $cacheType ) { + $this->cachePrefix = $cachePrefix; + $this->cacheDuration = $cacheDuration; + $this->cacheType = $cacheType; + } + + /** + * This pseudo-constructor uses the following settings from $settings: + * - sharedCacheKeyPrefix + * - sharedCacheDuration + * - sharedCacheType + * + * @param SettingsArray + * + * @return \Wikibase\SqlStore + */ + public static function newFromSettings( SettingsArray $settings ) { + $cachePrefix = $settings->getSetting( 'sharedCacheKeyPrefix' ); + $cacheDuration = $settings->getSetting( 'sharedCacheDuration' ); + $cacheType = $settings->getSetting( 'sharedCacheType' ); + + return new self( $cachePrefix, $cacheDuration, $cacheType ); + } /** * @see Store::getTermIndex @@ -236,11 +281,11 @@ * @return CachingEntityLoader */ protected function newEntityLookup() { - //TODO: get cache type etc from config //NOTE: two layers of caching: persistent external cache in WikiPageEntityLookup; // transient local cache in CachingEntityLoader. - $lookup = new WikiPageEntityLookup( false ); + //NOTE: Keep in sync with DirectSqlStore::newEntityLookup on the client + $key = $this->cachePrefix . ':WikiPageEntityLookup'; + $lookup = new WikiPageEntityLookup( false, $this->cacheType, $this->cacheDuration, $key ); return new CachingEntityLoader( $lookup ); } - } diff --git a/repo/tests/phpunit/includes/store/StoreTest.php b/repo/tests/phpunit/includes/store/StoreTest.php index 5bab52d..5b4acc1 100644 --- a/repo/tests/phpunit/includes/store/StoreTest.php +++ b/repo/tests/phpunit/includes/store/StoreTest.php @@ -42,7 +42,7 @@ class StoreTest extends \MediaWikiTestCase { public function instanceProvider() { - $instances = array( new \Wikibase\SqlStore() ); + $instances = array( new \Wikibase\SqlStore( 'testing', 1, CACHE_NONE ) ); return $this->arrayWrap( $instances ); } -- To view, visit https://gerrit.wikimedia.org/r/71595 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I78009d3eb1aed2b42ee53acf6f7bc812e7ee836f Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Wikibase Gerrit-Branch: master Gerrit-Owner: Daniel Kinzler <daniel.kinz...@wikimedia.de> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits