wiki.techinc.nl/tests/phpunit/includes/cache/BacklinkCacheTest.php
Tim Starling 0837da9484 Fix use of stale backlink partition cache
If a template is created, and then used on a page, and then the template
is updated, all within an hour, then the page_touched of the page is
never updated and the user will always see the old template contents.
This is because htmlCacheUpdate jobs are fully suppressed for one hour
following template creation, due to the WAN backlink partition cache.

So:

* Revert 4f3efbf406, so that htmlCacheUpdate jobs always do
  something regardless of the state of the partition cache.
* Factor out the job queueing parts of WikiPage::onArticleCreate,
  ::onArticleDelete and ::onArticleEdit. Instead of queueing a job
  unconditionally, check for the existence of backlinks in a post-send
  deferred update. If there are none, don't queue the job.
* It's convenient to use BacklinkCache::hasLinks(), however, it suffered
  from the same stale cache problem as BacklinkCache::partition(). It's
  a short and fast query, and code review shows that none of the callers
  are particularly performance sensitive. So, do not use the WAN cache
  in BacklinkCache::hasLinks().
* Since hasLinks() and getNumLinks() no longer share a significant
  amount of code, separate them. Remove the $max parameter from
  getNumLinks(), which only existed to support hasLinks() and has no
  other usages in codesearch.
* Log a debug message when entering the post-send request stage, so that
  it's easier to confirm that no additional pre-send queries are done.
* Add a regression test, confirmed to previously fail.

Bug: T368006
Change-Id: Id5c7af6d4fcdbeb6724a9036133742c5f76624df
2024-06-28 13:16:26 +10:00

153 lines
5.1 KiB
PHP

<?php
use MediaWiki\Title\Title;
/**
* @group Database
* @group Cache
* @covers \MediaWiki\Cache\BacklinkCache
*/
class BacklinkCacheTest extends MediaWikiIntegrationTestCase {
private static $backlinkCacheTest;
public function addDBDataOnce() {
$this->insertPage( 'Template:BacklinkCacheTestA', 'wooooooo' );
$this->insertPage( 'Template:BacklinkCacheTestB', '{{BacklinkCacheTestA}}' );
self::$backlinkCacheTest = $this->insertPage( 'BacklinkCacheTest_1', '{{BacklinkCacheTestB}}' );
$this->insertPage( 'BacklinkCacheTest_2', '[[BacklinkCacheTest_1]] [[Image:test.png]]' );
$this->insertPage( 'BacklinkCacheTest_3', '[[BacklinkCacheTest_1]]' );
$this->insertPage( 'BacklinkCacheTest_4', '[[BacklinkCacheTest_1]]' );
$this->insertPage( 'BacklinkCacheTest_5', '[[BacklinkCacheTest_1]]' );
$cascade = 1;
$this->getServiceContainer()->getWikiPageFactory()->newFromTitle( self::$backlinkCacheTest['title'] )->doUpdateRestrictions(
[ 'edit' => 'sysop' ],
[],
$cascade,
'test',
$this->getTestSysop()->getUser()
);
}
public static function provideCasesForHasLink() {
return [
[ true, 'BacklinkCacheTest_1', 'pagelinks' ],
[ false, 'BacklinkCacheTest_2', 'pagelinks' ],
[ true, 'Image:test.png', 'imagelinks' ]
];
}
/**
* @dataProvider provideCasesForHasLink
* @covers \MediaWiki\Cache\BacklinkCache::hasLinks
*/
public function testHasLink( bool $expected, string $title, string $table, string $msg = '' ) {
$blcFactory = $this->getServiceContainer()->getBacklinkCacheFactory();
$backlinkCache = $blcFactory->getBacklinkCache( Title::newFromText( $title ) );
$this->assertEquals( $expected, $backlinkCache->hasLinks( $table ), $msg );
}
public static function provideCasesForGetNumLinks() {
return [
[ 4, 'BacklinkCacheTest_1', 'pagelinks' ],
[ 0, 'BacklinkCacheTest_2', 'pagelinks' ],
[ 1, 'Image:test.png', 'imagelinks' ],
];
}
/**
* @dataProvider provideCasesForGetNumLinks
* @covers \MediaWiki\Cache\BacklinkCache::getNumLinks
*/
public function testGetNumLinks( int $numLinks, string $title, string $table ) {
$blcFactory = $this->getServiceContainer()->getBacklinkCacheFactory();
$backlinkCache = $blcFactory->getBacklinkCache( Title::newFromText( $title ) );
$this->assertEquals( $numLinks, $backlinkCache->getNumLinks( $table ) );
}
public static function provideCasesForGetLinks() {
return [
[
[ 'BacklinkCacheTest_2', 'BacklinkCacheTest_3', 'BacklinkCacheTest_4', 'BacklinkCacheTest_5' ],
'BacklinkCacheTest_1',
'pagelinks'
],
[
[ 'BacklinkCacheTest_4', 'BacklinkCacheTest_5' ],
'BacklinkCacheTest_1',
'pagelinks',
'BacklinkCacheTest_4'
],
[
[ 'BacklinkCacheTest_2', 'BacklinkCacheTest_3' ],
'BacklinkCacheTest_1',
'pagelinks',
false,
'BacklinkCacheTest_3'
],
[
[ 'BacklinkCacheTest_3', 'BacklinkCacheTest_4' ],
'BacklinkCacheTest_1',
'pagelinks',
'BacklinkCacheTest_3',
'BacklinkCacheTest_4'
],
[ [ 'BacklinkCacheTest_2' ], 'BacklinkCacheTest_1', 'pagelinks', false, false, 1 ],
[ [], 'BacklinkCacheTest_2', 'pagelinks' ],
[ [ 'BacklinkCacheTest_2' ], 'Image:test.png', 'imagelinks' ],
];
}
/**
* @dataProvider provideCasesForGetLinks
* @covers \MediaWiki\Cache\BacklinkCache::getLinkPages
*/
public function testGetLinkPages(
array $expectedTitles, string $title, string $table, $startId = false, $endId = false, $max = INF
) {
$startId = $startId ? Title::newFromText( $startId )->getId() : false;
$endId = $endId ? Title::newFromText( $endId )->getId() : false;
$blcFactory = $this->getServiceContainer()->getBacklinkCacheFactory();
$backlinkCache = $blcFactory->getBacklinkCache( Title::newFromText( $title ) );
$titlesArray = iterator_to_array( $backlinkCache->getLinkPages( $table, $startId, $endId, $max ) );
$this->assertSameSize( $expectedTitles, $titlesArray );
$numOfTitles = count( $titlesArray );
for ( $i = 0; $i < $numOfTitles; $i++ ) {
$this->assertEquals( $expectedTitles[$i], $titlesArray[$i]->getDbKey() );
}
}
/**
* @covers \MediaWiki\Cache\BacklinkCache::partition
*/
public function testPartition() {
$targetId = $this->getServiceContainer()->getLinkTargetLookup()->acquireLinkTargetId(
Title::makeTitle( NS_MAIN, 'BLCTest1234' ),
$this->getDb()
);
$targetRow = [
'tl_target_id' => $targetId,
];
$this->getDb()->newInsertQueryBuilder()
->insertInto( 'templatelinks' )
->rows( [
[ 'tl_from' => 56890, 'tl_from_namespace' => 0 ] + $targetRow,
[ 'tl_from' => 56891, 'tl_from_namespace' => 0 ] + $targetRow,
[ 'tl_from' => 56892, 'tl_from_namespace' => 0 ] + $targetRow,
[ 'tl_from' => 56893, 'tl_from_namespace' => 0 ] + $targetRow,
[ 'tl_from' => 56894, 'tl_from_namespace' => 0 ] + $targetRow,
] )
->caller( __METHOD__ )
->execute();
$blcFactory = $this->getServiceContainer()->getBacklinkCacheFactory();
$backlinkCache = $blcFactory->getBacklinkCache( Title::makeTitle( NS_MAIN, 'BLCTest1234' ) );
$partition = $backlinkCache->partition( 'templatelinks', 2 );
$this->assertArrayEquals( [
[ false, 56891 ],
[ 56892, 56893 ],
[ 56894, false ]
], $partition );
}
}