summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/CachingSemanticDataLookup.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/CachingSemanticDataLookup.php')
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/CachingSemanticDataLookup.php272
1 files changed, 272 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/CachingSemanticDataLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/CachingSemanticDataLookup.php
new file mode 100644
index 00000000..3826ed82
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/CachingSemanticDataLookup.php
@@ -0,0 +1,272 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use Onoi\Cache\Cache;
+use Onoi\Cache\NullCache;
+use Psr\Log\LoggerAwareTrait;
+use RuntimeException;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\RequestOptions;
+use SMW\SemanticData;
+use SMW\SQLStore\PropertyTableDefinition;
+use SMWDataItem as DataItem;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CachingSemanticDataLookup {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var SemanticDataLookup
+ */
+ private $semanticDataLookup;
+
+ /**
+ * @var Cache
+ */
+ private $cache;
+
+ /**
+ * Cache for SemanticData dataItems, indexed by SMW ID.
+ *
+ * @var array
+ */
+ private static $data = [];
+
+ /**
+ * Like SMWSQLStore3::data, but containing flags indicating
+ * completeness of the SemanticData objs.
+ *
+ * @var array
+ */
+ private static $state = [];
+
+ /**
+ * >0 while getSemanticData runs, used to prevent nested calls from clearing
+ * the cache while another call runs and is about to fill it with data
+ *
+ * @var int
+ */
+ private static $lookupCount = 0;
+
+ /**
+ * @since 3.0
+ *
+ * @param SemanticDataLookup $semanticDataLookup
+ * @param Cache|null $cache
+ */
+ public function __construct( SemanticDataLookup $semanticDataLookup, Cache $cache = null ) {
+ $this->semanticDataLookup = $semanticDataLookup;
+ $this->cache = $cache;
+
+ if ( $this->cache === null ) {
+ $this->cache = new NullCache();
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function lockCache() {
+ self::$lookupCount++;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function unlockCache() {
+ self::$lookupCount--;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ */
+ public function invalidateCache( $id ) {
+ unset( self::$data[$id] );
+ unset( self::$state[$id] );
+ }
+
+ /**
+ * @since 3.0
+ */
+ public static function clear() {
+ self::$data = [];
+ self::$state = [];
+ self::$lookupCount = 0;
+ }
+
+ /**
+ * Helper method to make sure there is a cache entry for the data about
+ * the given subject with the given ID.
+ *
+ * @todo The management of this cache should be revisited.
+ *
+ * @since 3.0
+ *
+ * @param int $id
+ * @param DIWikiPage $subject
+ */
+ public function initLookupCache( $id, DIWikiPage $subject ) {
+
+ // *** Prepare the cache ***//
+ if ( !isset( self::$data[$id] ) ) {
+ self::$data[$id] = $this->semanticDataLookup->newStubSemanticData( $subject );
+ self::$state[$id] = [];
+ }
+
+ // Issue #622
+ // If a redirect was cached preceding this request and points to the same
+ // subject id ensure that in all cases the requested subject matches with
+ // the selected DB id
+ if ( self::$data[$id]->getSubject()->getHash() !== $subject->getHash() ) {
+ self::$data[$id] = $this->semanticDataLookup->newStubSemanticData( $subject );
+ self::$state[$id] = [];
+ }
+
+ // It is not so easy to find the sweet spot between cache size and
+ // performance gains (both memory and time), The value of 20 was chosen
+ // by profiling runtimes for large inline queries and heavily annotated
+ // pages. However, things might have changed in the meantime ...
+ if ( ( count( self::$data ) > 20 ) && ( self::$lookupCount == 1 ) ) {
+ self::$data = [ $id => self::$data[$id] ];
+ self::$state = [ $id => self::$state[$id] ];
+ }
+ }
+
+ /**
+ * Set the semantic data lookup cache to hold exactly the given value for the
+ * given ID.
+ *
+ * @since 3.0
+ *
+ * @param integer $id
+ * @param SemanticData $semanticData
+ */
+ public function setLookupCache( $id, SemanticData $semanticData ) {
+
+ self::$data[$id] = $this->semanticDataLookup->newStubSemanticData(
+ $semanticData
+ );
+
+ self::$state[$id] = $this->semanticDataLookup->getTableUsageInfo(
+ $semanticData
+ );
+ }
+
+ /**
+ * Helper method to make sure there is a cache entry for the data about
+ * the given subject with the given ID.
+ *
+ * @since 3.0
+ *
+ * @param int $id
+ * @param DIWikiPage $subject
+ */
+ public function getSemanticDataById( $id ) {
+
+ if ( !isset( self::$data[$id] ) ) {
+ throw new RuntimeException( 'Data are not initialized.' );
+ }
+
+ return self::$data[$id];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param PropertyTableDefinition $propertyTableDef
+ * @param DIProperty $property
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return RequestOptions|null
+ */
+ public function newRequestOptions( PropertyTableDefinition $propertyTableDef, DIProperty $property, RequestOptions $requestOptions = null ) {
+ return $this->semanticDataLookup->newRequestOptions( $propertyTableDef, $property, $requestOptions );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ * @param DataItem $dataItem
+ * @param PropertyTableDefinition $propertyTableDef
+ * @param RequestOptions $requestOptions
+ *
+ * @return RequestOptions|null
+ */
+ public function fetchSemanticData( $id, DataItem $dataItem = null, PropertyTableDefinition $propertyTableDef, RequestOptions $requestOptions = null ) {
+ return $this->semanticDataLookup->fetchSemanticData( $id, $dataItem, $propertyTableDef, $requestOptions );
+ }
+
+ /**
+ * Fetch and cache the data about one subject for one particular table
+ *
+ * @param integer $id
+ * @param DIWikiPage $subject
+ * @param PropertyTableDefinition $propertyTableDef
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return SemanticData
+ */
+ public function getSemanticDataFromTable( $id, DataItem $dataItem = null, PropertyTableDefinition $propertyTableDef, RequestOptions $requestOptions = null ) {
+
+ // Avoid the cache when a request is constrainted
+ if ( $requestOptions !== null || !$dataItem instanceof DIWikiPage ) {
+ return $this->semanticDataLookup->getSemanticData( $id, $dataItem, $propertyTableDef, $requestOptions );
+ }
+
+ return $this->fetchFromCache( $id, $dataItem, $propertyTableDef );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage $subject
+ *
+ * @return StubSemanticData
+ */
+ public function newStubSemanticData( DIWikiPage $subject ) {
+ return $this->semanticDataLookup->newStubSemanticData( $subject );
+ }
+
+ private function fetchFromCache( $id, DataItem $dataItem = null, PropertyTableDefinition $propertyTableDef ) {
+
+ // Do not clear the cache when called recursively.
+ $this->lockCache();
+ $this->initLookupCache( $id, $dataItem );
+
+ // @see also setLookupCache
+ $name = $propertyTableDef->getName();
+
+ if ( isset( self::$state[$id][$name] ) ) {
+ $this->unlockCache();
+ return self::$data[$id];
+ }
+
+ $data = $this->semanticDataLookup->fetchSemanticData(
+ $id,
+ $dataItem,
+ $propertyTableDef
+ );
+
+ foreach ( $data as $d ) {
+ self::$data[$id]->addPropertyStubValue( reset( $d ), end( $d ) );
+ }
+
+ self::$state[$id][$name] = true;
+
+ $this->unlockCache();
+
+ return self::$data[$id];
+ }
+
+}