Daniel Kinzler has uploaded a new change for review.
https://gerrit.wikimedia.org/r/71339
Change subject: (bug 49742) Adding caching layer for property info.
......................................................................
(bug 49742) Adding caching layer for property info.
Change-Id: I2ccb752a7edd1af955956dcf4ca7b332b0f3fa3b
---
M lib/WikibaseLib.classes.php
A lib/includes/store/CachingPropertyInfoStore.php
A lib/tests/phpunit/store/CachingPropertyInfoStoreTest.php
A lib/tests/phpunit/store/MockPropertyInfoStore.php
A lib/tests/phpunit/store/MockPropertyInfoStoreTest.php
5 files changed, 506 insertions(+), 0 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikibase
refs/changes/39/71339/1
diff --git a/lib/WikibaseLib.classes.php b/lib/WikibaseLib.classes.php
index 7098d8a..0bac36d 100644
--- a/lib/WikibaseLib.classes.php
+++ b/lib/WikibaseLib.classes.php
@@ -135,6 +135,7 @@
'Wikibase\TermPropertyLabelResolver' =>
'includes/store/TermPropertyLabelResolver.php',
'Wikibase\PropertyInfoStore' =>
'includes/store/PropertyInfoStore.php',
+ 'Wikibase\CachingPropertyInfoStore' =>
'includes/store/CachingPropertyInfoStore.php',
// includes/store/sql
'Wikibase\CachingEntityLoader' =>
'includes/store/sql/CachingEntityLoader.php',
@@ -178,6 +179,7 @@
'Wikibase\Test\EntityLookupTest' =>
'tests/phpunit/EntityLookupTest.php',
'Wikibase\Test\MockChunkAccess' =>
'tests/phpunit/store/MockChunkAccess.php',
'Wikibase\Test\TermIndexTest' =>
'tests/phpunit/store/TermIndexTest.php',
+ 'Wikibase\Test\MockPropertyInfoStore' =>
'tests/phpunit/store/MockPropertyInfoStore.php',
'Wikibase\Test\PropertyInfoStoreTestHelper' =>
'tests/phpunit/store/PropertyInfoStoreTestHelper.php',
);
diff --git a/lib/includes/store/CachingPropertyInfoStore.php
b/lib/includes/store/CachingPropertyInfoStore.php
new file mode 100644
index 0000000..47cbc6b
--- /dev/null
+++ b/lib/includes/store/CachingPropertyInfoStore.php
@@ -0,0 +1,220 @@
+<?php
+ /**
+ *
+ * Copyright © 26.06.13 by the authors listed below.
+ *
+ * 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
+ *
+ * @license GPL 2+
+ * @file
+ * @ingroup WikibaseLib
+ *
+ * @author Daniel Kinzler
+ */
+
+
+namespace Wikibase;
+
+/**
+ * Class CachingPropertyInfoStore is an implementation of PropertyInfoStore
+ * that maintains a cached copy of the property info in memcached.
+ *
+ * @since 0.4
+ *
+ * @package Wikibase
+ */
+class CachingPropertyInfoStore implements PropertyInfoStore {
+
+ /**
+ * @var PropertyInfoStore
+ */
+ protected $store;
+
+ /**
+ * @var \BagOStuff
+ */
+ protected $cache;
+
+ /**
+ * @var int
+ */
+ protected $cacheDuration;
+
+ /**
+ * @var string
+ */
+ protected $cacheKey;
+
+ /**
+ * Maps properties to info arrays
+ *
+ * @var array[]
+ */
+ protected $propertyInfo = null;
+
+ /**
+ * @param PropertyInfoStore $store The info store to call back to.
+ * @param \BagOStuff $cache The cache to use for labels
(typically from wfGetMainCache())
+ * @param int $cacheDuration Number of seconds to keep the
cached version for.
+ * Defaults to 3600 seconds = 1
hour.
+ * @param string|null $cacheKey The cache key to use,
auto-generated per default.
+ * Should be set to something
including the wiki name
+ * of the wiki that maintains the
properties.
+ */
+ public function __construct(
+ PropertyInfoStore $store,
+ \BagOStuff $cache,
+ $cacheDuration = 3600,
+ $cacheKey = null
+ ) {
+ $this->store = $store;
+ $this->cache = $cache;
+ $this->cacheDuration = $cacheDuration;
+
+ if ( $cacheKey === null ) {
+ // share cached data between wikis, only vary on
language code.
+ // XXX: should really include wiki ID of the wiki that
maintains this!
+ $cacheKey = __CLASS__;
+ }
+
+ $this->cacheKey = $cacheKey;
+ }
+
+ /**
+ * @see PropertyInfoStore::getPropertyInfo
+ *
+ * @param EntityId $propertyId
+ *
+ * @return array|null
+ * @throws \InvalidArgumentException
+ */
+ public function getPropertyInfo( EntityId $propertyId ) {
+ if ( $propertyId->getEntityType() !== Property::ENTITY_TYPE ) {
+ throw new \InvalidArgumentException( 'Property ID
expected! ' . $propertyId );
+ }
+
+ $propertyInfo = $this->getAllPropertyInfo();
+ $id = $propertyId->getNumericId();
+
+ if ( isset( $propertyInfo[$id] ) ) {
+ return $propertyInfo[$id];
+ }
+
+ return null;
+ }
+
+ /**
+ * @see PropertyInfoStore::getAllPropertyInfo
+ *
+ * @return array[]
+ */
+ public function getAllPropertyInfo() {
+ if ( $this->propertyInfo === null ) {
+ $this->propertyInfo = $this->cache->get(
$this->cacheKey );
+
+ if ( !is_array( $this->propertyInfo ) ) {
+ $this->propertyInfo =
$this->store->getAllPropertyInfo();
+ $this->cache->set( $this->cacheKey,
$this->propertyInfo, $this->cacheDuration );
+ wfDebugLog( __CLASS__, __FUNCTION__ . ': cached
fresh property info table' );
+ } else {
+ wfDebugLog( __CLASS__, __FUNCTION__ . ': using
cached property info table' );
+ }
+ }
+
+ assert( is_array( $this->propertyInfo ) );
+ return $this->propertyInfo;
+ }
+
+ /**
+ * @see PropertyInfoStore::setPropertyInfo
+ *
+ * @param EntityId $propertyId
+ * @param array $info
+ */
+ public function setPropertyInfo( EntityId $propertyId, array $info ) {
+ if ( $propertyId->getEntityType() !== Property::ENTITY_TYPE ) {
+ throw new \InvalidArgumentException( 'Property ID
expected! ' . $propertyId );
+ }
+
+ if ( !isset( $info[ PropertyInfoStore::KEY_DATA_TYPE ]) ) {
+ throw new \InvalidArgumentException( 'Missing required
info field: ' . PropertyInfoStore::KEY_DATA_TYPE );
+ }
+
+ // update primary store
+ $this->store->setPropertyInfo( $propertyId, $info );
+
+ // NOTE: Even if we don't have the propertyInfo locally, we
still need to
+ // fully load it to update memcached.
+
+ // Get local cached version.
+ // NOTE: this may be stale at this point, if it was already
loaded
+ $propertyInfo = $this->getAllPropertyInfo();
+ $id = $propertyId->getNumericId();
+
+ // update local cache
+ $propertyInfo[$id] = $info;
+ $this->propertyInfo = $propertyInfo;
+
+ // update external cache
+ wfDebugLog( __CLASS__, __FUNCTION__ . ': updating cache after
updating property ' . $id );
+ $this->cache->set( $this->cacheKey, $propertyInfo,
$this->cacheDuration );
+ }
+
+ /**
+ * @see PropertyInfoStore::removePropertyInfo
+ *
+ * @param EntityId $propertyId
+ *
+ * @return bool
+ */
+ public function removePropertyInfo( EntityId $propertyId ) {
+ if ( $propertyId->getEntityType() !== Property::ENTITY_TYPE ) {
+ throw new \InvalidArgumentException( 'Property ID
expected! ' . $propertyId );
+ }
+
+ $id = $propertyId->getNumericId();
+
+ // if we don't know it, don't delete it.
+ if ( is_array( $this->propertyInfo ) && !array_key_exists( $id,
$this->propertyInfo ) ) {
+ return false;
+ }
+
+ // update primary store
+ $ok = $this->store->removePropertyInfo( $propertyId );
+
+ if ( !$ok ) {
+ // nothing changed, nothing to do
+ return false;
+ }
+
+ // NOTE: Even if we don't have the propertyInfo locally, we
still need to
+ // fully load it to update memcached.
+
+ // Get local cached version.
+ // NOTE: this may be stale at this point, if it was already
loaded
+ $propertyInfo = $this->getAllPropertyInfo();
+
+ // update local cache
+ unset( $propertyInfo[$id] );
+ $this->propertyInfo = $propertyInfo;
+
+ // update external cache
+ wfDebugLog( __CLASS__, __FUNCTION__ . ': updating cache after
removing property ' . $id );
+ $this->cache->set( $this->cacheKey, $propertyInfo,
$this->cacheDuration );
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/lib/tests/phpunit/store/CachingPropertyInfoStoreTest.php
b/lib/tests/phpunit/store/CachingPropertyInfoStoreTest.php
new file mode 100644
index 0000000..e92c37e
--- /dev/null
+++ b/lib/tests/phpunit/store/CachingPropertyInfoStoreTest.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Wikibase\Test;
+
+use Wikibase\EntityId;
+use Wikibase\Property;
+use Wikibase\CachingPropertyInfoStore;
+use Wikibase\PropertyInfoStore;
+use Wikibase\Item;
+
+/**
+ * @covers Wikibase\CachingPropertyInfoStore
+ *
+ * @file
+ * @since 0.4
+ *
+ * @ingroup WikibaseLib
+ * @ingroup Test
+ *
+ * @group Wikibase
+ * @group WikibaseLib
+ * @group WikibasePropertyInfo
+ * @group WikibaseStore
+ *
+ * @licence GNU GPL v2+
+ * @author Daniel Kinzler
+ */
+class CachingPropertyInfoStoreTest extends \MediaWikiTestCase {
+
+ /**
+ * @var PropertyInfoStoreTestHelper
+ */
+ public $helper;
+
+ public function __construct( $name = null, $data = array(), $dataName =
'' ) {
+ parent::__construct( $name, $data, $dataName );
+
+ $this->helper = new PropertyInfoStoreTestHelper( $this, array(
$this, 'newCachingPropertyInfoStore' ) );
+ }
+
+ public function newCachingPropertyInfoStore() {
+ $mock = new MockPropertyInfoStore();
+ $cache = new \HashBagOStuff();
+ return new CachingPropertyInfoStore( $mock, $cache );
+ }
+
+ public function provideSetPropertyInfo() {
+ return $this->helper->provideSetPropertyInfo();
+ }
+
+ /**
+ * @dataProvider provideSetPropertyInfo
+ */
+ public function testSetPropertyInfo( EntityId $id, array $info,
$expectedException ) {
+ $this->helper->testSetPropertyInfo( $id, $info,
$expectedException );
+ }
+
+ public function testGetPropertyInfo() {
+ $this->helper->testGetPropertyInfo();
+ }
+
+ public function testGetAllPropertyInfo() {
+ $this->helper->testGetAllPropertyInfo();
+ }
+
+ public function testRemovePropertyInfo() {
+ $this->helper->testRemovePropertyInfo();
+ }
+
+ public function testPropertyInfoWriteThrough() {
+ $p23 = new EntityId( Property::ENTITY_TYPE, 23 );
+ $q23 = new EntityId( Item::ENTITY_TYPE, 23 );
+ $p42 = new EntityId( Property::ENTITY_TYPE, 42 );
+ $info23 = array( PropertyInfoStore::KEY_DATA_TYPE => 'string' );
+ $info42 = array( PropertyInfoStore::KEY_DATA_TYPE => 'string',
'foo' => 'bar' );
+
+ $mock = new MockPropertyInfoStore();
+ $cache = new \HashBagOStuff();
+
+ $mock->setPropertyInfo( $p23, $info23 );
+
+ $store = new CachingPropertyInfoStore( $mock, $cache );
+
+ $this->assertEquals( $info23, $store->getPropertyInfo( $p23 ),
"get from source" );
+ $this->assertEquals( $info23, $store->getPropertyInfo( $p23 ),
"get cached" );
+
+ $store->setPropertyInfo( $p42, $info42 );
+ $this->assertEquals( $info42, $store->getPropertyInfo( $p42 ),
"cache updated" );
+ $this->assertEquals( $info42, $mock->getPropertyInfo( $p42 ),
"source updated" );
+ }
+
+}
diff --git a/lib/tests/phpunit/store/MockPropertyInfoStore.php
b/lib/tests/phpunit/store/MockPropertyInfoStore.php
new file mode 100644
index 0000000..7a5fd04
--- /dev/null
+++ b/lib/tests/phpunit/store/MockPropertyInfoStore.php
@@ -0,0 +1,126 @@
+<?php
+ /**
+ *
+ * Copyright © 26.06.13 by the authors listed below.
+ *
+ * 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
+ *
+ * @license GPL 2+
+ * @file
+ *
+ * @ingroup WikibaseRepoTest
+ * @ingroup Test
+ *
+ * @author Daniel Kinzler
+ */
+
+
+namespace Wikibase\Test;
+
+use Wikibase\EntityId;
+use Wikibase\Property;
+use Wikibase\PropertyInfoStore;
+
+/**
+ * Class MockPropertyInfoStore is an implementation of PropertyInfoStore
+ * based on a local array.
+ *
+ * @since 0.4
+ *
+ * @package Wikibase\Test
+ */
+class MockPropertyInfoStore implements PropertyInfoStore {
+
+ /**
+ * Maps properties to info arrays
+ *
+ * @var array[]
+ */
+ protected $propertyInfo = array();
+
+ /**
+ * @see PropertyInfoStore::getPropertyInfo
+ *
+ * @param EntityId $propertyId
+ *
+ * @return array|null
+ * @throws \InvalidArgumentException
+ */
+ public function getPropertyInfo( EntityId $propertyId ) {
+ if ( $propertyId->getEntityType() !== Property::ENTITY_TYPE ) {
+ throw new \InvalidArgumentException( 'Property ID
expected! ' . $propertyId );
+ }
+
+ $propertyInfo = $this->getAllPropertyInfo();
+ $id = $propertyId->getNumericId();
+
+ if ( isset( $propertyInfo[$id] ) ) {
+ return $propertyInfo[$id];
+ }
+
+ return null;
+ }
+
+ /**
+ * @see PropertyInfoStore::getAllPropertyInfo
+ *
+ * @return array[]
+ */
+ public function getAllPropertyInfo() {
+ return $this->propertyInfo;
+ }
+
+ /**
+ * @see PropertyInfoStore::setPropertyInfo
+ *
+ * @param EntityId $propertyId
+ * @param array $info
+ */
+ public function setPropertyInfo( EntityId $propertyId, array $info ) {
+ if ( $propertyId->getEntityType() !== Property::ENTITY_TYPE ) {
+ throw new \InvalidArgumentException( 'Property ID
expected! ' . $propertyId );
+ }
+
+ if ( !isset( $info[ PropertyInfoStore::KEY_DATA_TYPE ]) ) {
+ throw new \InvalidArgumentException( 'Missing required
info field: ' . PropertyInfoStore::KEY_DATA_TYPE );
+ }
+
+ $id = $propertyId->getNumericId();
+ $this->propertyInfo[$id] = $info;
+ }
+
+ /**
+ * @see PropertyInfoStore::removePropertyInfo
+ *
+ * @param EntityId $propertyId
+ *
+ * @return bool
+ */
+ public function removePropertyInfo( EntityId $propertyId ) {
+ if ( $propertyId->getEntityType() !== Property::ENTITY_TYPE ) {
+ throw new \InvalidArgumentException( 'Property ID
expected! ' . $propertyId );
+ }
+
+ $id = $propertyId->getNumericId();
+
+ if ( array_key_exists( $id, $this->propertyInfo ) ) {
+ unset( $this->propertyInfo[$id] );
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/tests/phpunit/store/MockPropertyInfoStoreTest.php
b/lib/tests/phpunit/store/MockPropertyInfoStoreTest.php
new file mode 100644
index 0000000..cd2e607
--- /dev/null
+++ b/lib/tests/phpunit/store/MockPropertyInfoStoreTest.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Wikibase\Test;
+
+use Wikibase\EntityId;
+use Wikibase\Property;
+use Wikibase\Item;
+
+/**
+ * @covers Wikibase\Test\MockPropertyInfoStore
+ *
+ * @file
+ * @since 0.4
+ *
+ * @ingroup WikibaseLib
+ * @ingroup Test
+ *
+ * @group Wikibase
+ * @group WikibaseLib
+ * @group WikibasePropertyInfo
+ * @group WikibaseStore
+ *
+ * @licence GNU GPL v2+
+ * @author Daniel Kinzler
+ */
+class MockPropertyInfoStoreTest extends \MediaWikiTestCase {
+
+ /**
+ * @var PropertyInfoStoreTestHelper
+ */
+ public $helper;
+
+ public function __construct( $name = null, $data = array(), $dataName =
'' ) {
+ parent::__construct( $name, $data, $dataName );
+
+ $this->helper = new PropertyInfoStoreTestHelper( $this, array(
$this, 'newMockPropertyInfoStore' ) );
+ }
+
+ public function newMockPropertyInfoStore() {
+ return new MockPropertyInfoStore();
+ }
+
+ public function provideSetPropertyInfo() {
+ return $this->helper->provideSetPropertyInfo();
+ }
+
+ /**
+ * @dataProvider provideSetPropertyInfo
+ */
+ public function testSetPropertyInfo( EntityId $id, array $info,
$expectedException ) {
+ $this->helper->testSetPropertyInfo( $id, $info,
$expectedException );
+ }
+
+ public function testGetPropertyInfo() {
+ $this->helper->testGetPropertyInfo();
+ }
+
+ public function testGetAllPropertyInfo() {
+ $this->helper->testGetAllPropertyInfo();
+ }
+
+ public function testRemovePropertyInfo() {
+ $this->helper->testRemovePropertyInfo();
+ }
+
+}
--
To view, visit https://gerrit.wikimedia.org/r/71339
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I2ccb752a7edd1af955956dcf4ca7b332b0f3fa3b
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Daniel Kinzler <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits