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

Reply via email to