Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save ciencia/88e6bb68f16b4982971ecaa7b28a8a8d to your computer and use it in GitHub Desktop.

Select an option

Save ciencia/88e6bb68f16b4982971ecaa7b28a8a8d to your computer and use it in GitHub Desktop.
core-PagePropsDisplayTitleCache-REL1_43.patch
diff --git a/includes/MediaWikiServices.php b/includes/MediaWikiServices.php
index 2a040a7861a..8d857a9eec4 100644
--- a/includes/MediaWikiServices.php
+++ b/includes/MediaWikiServices.php
@@ -51,6 +51,7 @@ use MediaWiki\Cache\HTMLCacheUpdater;
use MediaWiki\Cache\LinkBatchFactory;
use MediaWiki\Cache\LinkCache;
use MediaWiki\Cache\UserCache;
+use MediaWiki\Cache\PagePropsDisplayTitleCache;
use MediaWiki\Category\TrackingCategories;
use MediaWiki\ChangeTags\ChangeTagsStore;
use MediaWiki\Collation\CollationFactory;
@@ -1521,6 +1522,13 @@ class MediaWikiServices extends ServiceContainer {
return $this->getService( 'PageProps' );
}
+ /**
+ * @since 1.XX
+ */
+ public function getPagePropsDisplayTitleCache(): PagePropsDisplayTitleCache {
+ return $this->getService( 'PagePropsDisplayTitleCache' );
+ }
+
/**
* @since 1.40
*/
diff --git a/includes/ServiceWiring.php b/includes/ServiceWiring.php
index af33de1f2c7..bdeca2c0799 100644
--- a/includes/ServiceWiring.php
+++ b/includes/ServiceWiring.php
@@ -69,6 +69,7 @@ use MediaWiki\Cache\GenderCache;
use MediaWiki\Cache\HTMLCacheUpdater;
use MediaWiki\Cache\LinkBatchFactory;
use MediaWiki\Cache\LinkCache;
+use MediaWiki\Cache\PagePropsDisplayTitleCache;
use MediaWiki\Cache\UserCache;
use MediaWiki\Category\TrackingCategories;
use MediaWiki\ChangeTags\ChangeTagsStore;
@@ -1104,7 +1105,8 @@ return [
$services->getGenderCache(),
$services->getConnectionProvider(),
$services->getLinksMigration(),
- LoggerFactory::getInstance( 'LinkBatch' )
+ LoggerFactory::getInstance( 'LinkBatch' ),
+ $services->getPagePropsDisplayTitleCache()
);
},
@@ -1462,6 +1464,14 @@ return [
);
},
+ 'PagePropsDisplayTitleCache' => static function ( MediaWikiServices $services ): PagePropsDisplayTitleCache {
+ // Database layer may be disabled, so processing without database connection
+ $dbLoadBalancer = $services->isServiceDisabled( 'DBLoadBalancer' )
+ ? null
+ : $services->getDBLoadBalancerFactory();
+ return new PagePropsDisplayTitleCache( $dbLoadBalancer );
+ },
+
'PageRestHelperFactory' => static function ( MediaWikiServices $services ): PageRestHelperFactory {
return new PageRestHelperFactory(
new ServiceOptions( PageRestHelperFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
diff --git a/includes/cache/LinkBatch.php b/includes/cache/LinkBatch.php
index 990c4c187b5..b6f02d0b4ae 100644
--- a/includes/cache/LinkBatch.php
+++ b/includes/cache/LinkBatch.php
@@ -92,6 +92,9 @@ class LinkBatch {
/** @var LoggerInterface */
private $logger;
+ /** @var PagePropsDisplayTitleCache */
+ private $pagePropsDisplayTitleCache;
+
/**
* @see \MediaWiki\Cache\LinkBatchFactory
*
@@ -113,7 +116,8 @@ class LinkBatch {
GenderCache $genderCache,
IConnectionProvider $dbProvider,
LinksMigration $linksMigration,
- LoggerInterface $logger
+ LoggerInterface $logger,
+ PagePropsDisplayTitleCache $pagePropsDisplayTitleCache
) {
$this->linkCache = $linkCache;
$this->titleFormatter = $titleFormatter;
@@ -122,6 +126,7 @@ class LinkBatch {
$this->dbProvider = $dbProvider;
$this->linksMigration = $linksMigration;
$this->logger = $logger;
+ $this->pagePropsDisplayTitleCache = $pagePropsDisplayTitleCache;
foreach ( $arr as $item ) {
$this->addObj( $item );
@@ -241,6 +246,7 @@ class LinkBatch {
protected function executeInto( $cache ) {
$res = $this->doQuery();
$this->doGenderQuery();
+ $this->doPagePropsDisplayTitleQuery( $res );
return $this->addResultToCache( $cache, $res );
}
@@ -356,6 +362,22 @@ class LinkBatch {
return true;
}
+ /**
+ * Do (and cache) page properties for display title in this LinkBatch
+ *
+ * @param IResultWrapper $res
+ * @return bool Whether the query was successful
+ */
+ public function doPagePropsDisplayTitleQuery( $res ) {
+ if ( $this->isEmpty() ) {
+ return false;
+ }
+
+ $this->pagePropsDisplayTitleCache->doLinkBatch( $res );
+
+ return true;
+ }
+
/**
* Construct a WHERE clause which will match all the given titles.
*
diff --git a/includes/cache/LinkBatchFactory.php b/includes/cache/LinkBatchFactory.php
index 78f46bcfa99..6b10e13274e 100644
--- a/includes/cache/LinkBatchFactory.php
+++ b/includes/cache/LinkBatchFactory.php
@@ -68,6 +68,9 @@ class LinkBatchFactory {
/** @var LoggerInterface */
private $logger;
+ /** @var PagePropsDisplayTitleCache */
+ private $pagePropsDisplayTitleCache;
+
public function __construct(
LinkCache $linkCache,
TitleFormatter $titleFormatter,
@@ -75,7 +78,8 @@ class LinkBatchFactory {
GenderCache $genderCache,
IConnectionProvider $dbProvider,
LinksMigration $linksMigration,
- LoggerInterface $logger
+ LoggerInterface $logger,
+ PagePropsDisplayTitleCache $pagePropsDisplayTitleCache
) {
$this->linkCache = $linkCache;
$this->titleFormatter = $titleFormatter;
@@ -84,6 +88,7 @@ class LinkBatchFactory {
$this->dbProvider = $dbProvider;
$this->linksMigration = $linksMigration;
$this->logger = $logger;
+ $this->pagePropsDisplayTitleCache = $pagePropsDisplayTitleCache;
}
/**
@@ -100,7 +105,8 @@ class LinkBatchFactory {
$this->genderCache,
$this->dbProvider,
$this->linksMigration,
- $this->logger
+ $this->logger,
+ $this->pagePropsDisplayTitleCache
);
}
}
diff --git a/includes/cache/PagePropsDisplayTitleCache.php b/includes/cache/PagePropsDisplayTitleCache.php
new file mode 100644
index 00000000000..277b42e3e8a
--- /dev/null
+++ b/includes/cache/PagePropsDisplayTitleCache.php
@@ -0,0 +1,131 @@
+<?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
+ * @author Ciencia Al Poder
+ */
+
+namespace MediaWiki\Cache;
+
+use Wikimedia\Rdbms\IConnectionProvider;
+
+/**
+ * Look up page properties for display title
+ *
+ * @since 1.43
+ * @ingroup Cache
+ */
+class PagePropsDisplayTitleCache {
+ /**
+ * Page properties to fetch
+ */
+ public const PAGE_PROPERTIES = [
+ 'displaylink',
+ 'displaytitle',
+ ];
+
+ /** @var array<int,array<string,string>> */
+ protected $cache = [];
+ /** @var int */
+ protected $misses = 0;
+ /**
+ * @internal Exposed for MediaWiki core unit tests.
+ * @var int
+ */
+ protected $missLimit = 1000;
+
+ private ?IConnectionProvider $dbProvider;
+
+ public function __construct(
+ ?IConnectionProvider $dbProvider = null
+ ) {
+ $this->dbProvider = $dbProvider;
+ }
+
+ /**
+ * Get display title properties for a page id
+ *
+ * @param int $pageId Page id
+ * @param string|null $caller Calling method for database profiling
+ * @return array<string,string>
+ */
+ public function getProperties( $pageId, $caller = '' ) {
+ if ( !array_key_exists( $pageId, $this->cache ) ) {
+ if ( $this->misses < $this->missLimit ) {
+ $this->misses++;
+ $this->doQuery( $pageId, $caller );
+ }
+ if ( $this->misses === $this->missLimit ) {
+ // Log only once and don't bother incrementing beyond limit+1
+ $this->misses++;
+ wfDebug( __METHOD__ . ': too many misses, returning default onwards' );
+ }
+ }
+
+ return $this->cache[$pageId] ?? [];
+ }
+
+ /**
+ * Wrapper for doQuery that processes LinkBatch results.
+ *
+ * @param IResultWrapper $res
+ * @param string|null $caller
+ */
+ public function doLinkBatch( $res, $caller = '' ) {
+ $pageIds = [];
+ foreach ( $res as $row ) {
+ $pageIds[] = (int)$row->page_id;
+ }
+ $this->doQuery( $pageIds, $caller );
+ }
+
+ /**
+ * Preload page properties for display title
+ *
+ * @param int[]|int $pageIds Page ids
+ * @param string|null $caller Calling method for database profiling
+ */
+ public function doQuery( $pageIds, $caller = '' ) {
+ // Skip query when database is unavailable (e.g. via the installer)
+ if ( !$pageIds || !$this->dbProvider ) {
+ return;
+ }
+
+ $caller = __METHOD__ . ( $caller ? "/$caller" : '' );
+
+ $res = $queryBuilder = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder()
+ ->select( [ 'pp_page', 'pp_propname', 'pp_value' ] )
+ ->from( 'page_props' )
+ ->where( [ 'pp_page' => $pageIds, 'pp_propname' => self::PAGE_PROPERTIES ] )
+ ->caller( $caller )
+ ->fetchResultSet();
+
+ foreach ( $res as $row ) {
+ if ( array_key_exists( $row->pp_page, $this->cache ) ) {
+ $this->cache[$row->pp_page][$row->pp_propname] = $row->pp_value;
+ } else {
+ $this->cache[$row->pp_page] = [ $row->pp_propname => $row->pp_value ];
+ }
+ }
+
+ // Store pages without properties
+ $missing = array_diff( (array)$pageIds, array_keys( $this->cache ) );
+ foreach ( $missing as $id ) {
+ $this->cache[$id] = [];
+ }
+ }
+}
diff --git a/includes/parser/LinkHolderArray.php b/includes/parser/LinkHolderArray.php
index 0d763484d9e..63c1e631bc7 100644
--- a/includes/parser/LinkHolderArray.php
+++ b/includes/parser/LinkHolderArray.php
@@ -168,6 +168,7 @@ class LinkHolderArray {
$classes = [];
$services = MediaWikiServices::getInstance();
$linkCache = $services->getLinkCache();
+ $pagePropsDisplayTitleCache = $services->getPagePropsDisplayTitleCache();
$output = $this->parent->getOutput();
$linkRenderer = $this->parent->getLinkRenderer();
@@ -214,6 +215,9 @@ class LinkHolderArray {
->caller( __METHOD__ )
->fetchResultSet();
+ // Why is this class duplicating what LinkCache does? IDK, but this forces me to add the PagePropsDisplayTitleCache call here too
+ $pagePropsDisplayTitleCache->doLinkBatch( $res );
+
# Fetch data and form into an associative array
# non-existent = broken
foreach ( $res as $s ) {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment