2013-02-06 22:45:33 +00:00
|
|
|
<?php
|
|
|
|
|
|
2018-01-12 21:44:12 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2023-02-23 20:44:38 +00:00
|
|
|
use MediaWiki\WikiMap\WikiMap;
|
2018-01-12 21:44:12 +00:00
|
|
|
|
2013-02-06 22:45:33 +00:00
|
|
|
/**
|
|
|
|
|
* @group JobQueue
|
|
|
|
|
* @group medium
|
|
|
|
|
* @group Database
|
objectcache,resourceloader,rdbms,jobqueue: Widen @covers annotations
Follows-up I4c7d826c7ec654b, I1287f3979aba1bf1.
We lose useful coverage and spend valuable time keeping these accurate
through refactors (or worse, forget to do so). The theoretically "bad"
accidental coverage is almost never actually bad.
Having said that, I'm not removing them wholesale (yet). I've audited
each of these specific files to confirm it is a general test of the
specified subject class, and also kept it limited to those specified
classes. That's imho more than 100% of the benefit for less than 1%
of the cost (more because `@covers` is more valuable than the fragile
and corrosive individual private method tracking in tests that
inevitably get out of date with no local incentive to keep them up to
date).
Cases like structure tests keep `@coversNothing` etc and we still don't
count coverage of other classes. There may be a handful of large
legacy classes where some methods are effectively class-like in
complexity and that's why it's good for PHPUnit to offer the precision
instrument but that doesn't meant we have to use that by-default for
everything.
I think best practice is to write good narrow unit tests, that reflect
how the code should be used in practice. Not to write bad tests and
hide part of its coverage within the same class or even namespace.
Fortunately, that's generally what we do already it's just that we
also kept these annotations still in many cases.
This wastes time to keep methods in sync, time to realize (and fix)
when other people inevitably didn't keep them in sync, time to find
uncovered code only to realize it is already covered, time for a less
experienced engineer to feel obligate to and do write a low quality
test to cover the "missing" branch in an unrealistic way, time wasted
in on-boarding by using such "bad" tests as example for how to use
the code and then having to unlearn it months/years later, loss of
telemetry in knowing what code actually isn't propertly tested due to
being masked by a bad test, and lost oppertunities to find actually
ununused/unreachable code and to think about how to instead structure
the code such that maybe that code can be removed.
------
Especially cases like LBFactoryTest.php were getting out of hand,
and in GlobalIdGeneratorTest.php we even resorted to reminding people
with inline comments to keep tags in sync.
Change-Id: I69b5385868cc6b451e5f2ebec9539694968bf58c
2023-04-10 21:25:34 +00:00
|
|
|
* @covers JobQueue
|
2013-02-06 22:45:33 +00:00
|
|
|
*/
|
2020-06-30 15:09:24 +00:00
|
|
|
class JobQueueTest extends MediaWikiIntegrationTestCase {
|
2013-02-08 18:52:54 +00:00
|
|
|
protected $key;
|
2023-01-07 14:09:18 +00:00
|
|
|
protected $queueRand, $queueRandTTL, $queueTimestamp, $queueTimestampTTL, $queueFifo, $queueFifoTTL;
|
2013-02-06 22:45:33 +00:00
|
|
|
|
2021-07-22 03:11:47 +00:00
|
|
|
protected function setUp(): void {
|
2013-03-21 19:35:44 +00:00
|
|
|
global $wgJobTypeConf;
|
2013-02-06 22:45:33 +00:00
|
|
|
parent::setUp();
|
2013-03-21 19:35:44 +00:00
|
|
|
|
2020-12-23 10:03:34 +00:00
|
|
|
$this->tablesUsed[] = 'job';
|
|
|
|
|
|
2022-01-12 20:13:39 +00:00
|
|
|
$services = $this->getServiceContainer();
|
2014-05-05 16:10:07 +00:00
|
|
|
if ( $this->getCliArg( 'use-jobqueue' ) ) {
|
|
|
|
|
$name = $this->getCliArg( 'use-jobqueue' );
|
2013-02-08 18:52:54 +00:00
|
|
|
if ( !isset( $wgJobTypeConf[$name] ) ) {
|
|
|
|
|
throw new MWException( "No \$wgJobTypeConf entry for '$name'." );
|
|
|
|
|
}
|
|
|
|
|
$baseConfig = $wgJobTypeConf[$name];
|
|
|
|
|
} else {
|
2018-01-13 00:02:09 +00:00
|
|
|
$baseConfig = [ 'class' => JobQueueDBSingle::class ];
|
2013-02-08 18:52:54 +00:00
|
|
|
}
|
|
|
|
|
$baseConfig['type'] = 'null';
|
2019-03-28 22:37:52 +00:00
|
|
|
$baseConfig['domain'] = WikiMap::getCurrentWikiDbDomain()->getId();
|
2019-03-30 04:41:34 +00:00
|
|
|
$baseConfig['stash'] = new HashBagOStuff();
|
|
|
|
|
$baseConfig['wanCache'] = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
|
2019-05-31 23:24:56 +00:00
|
|
|
$baseConfig['idGenerator'] = $services->getGlobalIdGenerator();
|
2016-02-17 09:09:32 +00:00
|
|
|
$variants = [
|
|
|
|
|
'queueRand' => [ 'order' => 'random', 'claimTTL' => 0 ],
|
|
|
|
|
'queueRandTTL' => [ 'order' => 'random', 'claimTTL' => 10 ],
|
|
|
|
|
'queueTimestamp' => [ 'order' => 'timestamp', 'claimTTL' => 0 ],
|
|
|
|
|
'queueTimestampTTL' => [ 'order' => 'timestamp', 'claimTTL' => 10 ],
|
|
|
|
|
'queueFifo' => [ 'order' => 'fifo', 'claimTTL' => 0 ],
|
|
|
|
|
'queueFifoTTL' => [ 'order' => 'fifo', 'claimTTL' => 10 ],
|
|
|
|
|
];
|
2013-03-13 21:26:28 +00:00
|
|
|
foreach ( $variants as $q => $settings ) {
|
|
|
|
|
try {
|
|
|
|
|
$this->$q = JobQueue::factory( $settings + $baseConfig );
|
2013-04-26 12:00:22 +00:00
|
|
|
} catch ( MWException $e ) {
|
2013-05-15 01:12:35 +00:00
|
|
|
// unsupported?
|
|
|
|
|
// @todo What if it was another error?
|
2019-06-12 13:35:59 +00:00
|
|
|
}
|
2013-02-08 18:52:54 +00:00
|
|
|
}
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-07-22 03:11:47 +00:00
|
|
|
protected function tearDown(): void {
|
2013-03-25 23:27:14 +00:00
|
|
|
foreach (
|
2016-02-17 09:09:32 +00:00
|
|
|
[
|
2013-03-25 23:27:14 +00:00
|
|
|
'queueRand', 'queueRandTTL', 'queueTimestamp', 'queueTimestampTTL',
|
|
|
|
|
'queueFifo', 'queueFifoTTL'
|
2016-02-17 09:09:32 +00:00
|
|
|
] as $q
|
2013-03-25 23:27:14 +00:00
|
|
|
) {
|
2013-03-13 21:26:28 +00:00
|
|
|
if ( $this->$q ) {
|
2013-05-15 02:29:22 +00:00
|
|
|
$this->$q->delete();
|
2013-03-13 21:26:28 +00:00
|
|
|
}
|
|
|
|
|
$this->$q = null;
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
2020-12-23 10:03:34 +00:00
|
|
|
parent::tearDown();
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provider_queueLists
|
|
|
|
|
*/
|
2013-10-24 19:35:04 +00:00
|
|
|
public function testGetWiki( $queue, $recycles, $desc ) {
|
2021-04-01 20:55:37 +00:00
|
|
|
$this->hideDeprecated( 'JobQueue::getWiki' );
|
2013-02-06 22:45:33 +00:00
|
|
|
$queue = $this->$queue;
|
2013-03-13 21:26:28 +00:00
|
|
|
if ( !$queue ) {
|
|
|
|
|
$this->markTestSkipped( $desc );
|
|
|
|
|
}
|
2021-12-21 00:47:14 +00:00
|
|
|
$this->assertEquals( WikiMap::getCurrentWikiId(), $queue->getWiki(), "Proper wiki ID ($desc)" );
|
2019-03-28 22:37:52 +00:00
|
|
|
$this->assertEquals(
|
|
|
|
|
WikiMap::getCurrentWikiDbDomain()->getId(),
|
|
|
|
|
$queue->getDomain(),
|
|
|
|
|
"Proper wiki ID ($desc)" );
|
2013-10-24 19:35:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provider_queueLists
|
|
|
|
|
*/
|
|
|
|
|
public function testGetType( $queue, $recycles, $desc ) {
|
|
|
|
|
$queue = $this->$queue;
|
|
|
|
|
if ( !$queue ) {
|
|
|
|
|
$this->markTestSkipped( $desc );
|
|
|
|
|
}
|
2013-02-06 22:45:33 +00:00
|
|
|
$this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provider_queueLists
|
|
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testBasicOperations( $queue, $recycles, $desc ) {
|
2013-02-06 22:45:33 +00:00
|
|
|
$queue = $this->$queue;
|
2013-03-13 21:26:28 +00:00
|
|
|
if ( !$queue ) {
|
|
|
|
|
$this->markTestSkipped( $desc );
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-06 22:45:33 +00:00
|
|
|
$this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
|
|
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $queue->getSize(), "Queue is empty ($desc)" );
|
|
|
|
|
$this->assertSame( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
|
2014-04-16 17:51:11 +00:00
|
|
|
$this->assertNull( $queue->push( $this->newJob() ), "Push worked ($desc)" );
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->assertNull( $queue->batchPush( [ $this->newJob() ] ), "Push worked ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
|
|
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
|
|
|
|
$this->assertEquals( 2, $queue->getSize(), "Queue size is correct ($desc)" );
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
|
2013-04-09 21:32:20 +00:00
|
|
|
$jobs = iterator_to_array( $queue->getAllQueuedJobs() );
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertCount( 2, $jobs, "Queue iterator size is correct ($desc)" );
|
2013-04-09 21:32:20 +00:00
|
|
|
|
2013-02-06 22:45:33 +00:00
|
|
|
$job1 = $queue->pop();
|
|
|
|
|
$this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
|
|
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
2020-05-30 10:36:42 +00:00
|
|
|
$this->assertSame( 1, $queue->getSize(), "Queue size is correct ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
|
|
|
|
if ( $recycles ) {
|
2020-05-30 10:36:42 +00:00
|
|
|
$this->assertSame( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$job2 = $queue->pop();
|
|
|
|
|
$this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $queue->getSize(), "Queue is empty ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
|
|
|
|
if ( $recycles ) {
|
|
|
|
|
$this->assertEquals( 2, $queue->getAcquiredCount(), "Active job count ($desc)" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$queue->ack( $job1 );
|
|
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
|
|
|
|
if ( $recycles ) {
|
2020-05-30 10:36:42 +00:00
|
|
|
$this->assertSame( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$queue->ack( $job2 );
|
|
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
|
2013-05-15 02:29:22 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->assertNull( $queue->batchPush( [ $this->newJob(), $this->newJob() ] ),
|
2013-05-15 02:29:22 +00:00
|
|
|
"Push worked ($desc)" );
|
|
|
|
|
$this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
|
|
|
|
|
|
|
|
|
|
$queue->delete();
|
|
|
|
|
$queue->flushCaches();
|
|
|
|
|
$this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $queue->getSize(), "Queue is empty ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provider_queueLists
|
|
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testBasicDeduplication( $queue, $recycles, $desc ) {
|
2013-02-06 22:45:33 +00:00
|
|
|
$queue = $this->$queue;
|
2013-03-13 21:26:28 +00:00
|
|
|
if ( !$queue ) {
|
|
|
|
|
$this->markTestSkipped( $desc );
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-06 22:45:33 +00:00
|
|
|
$this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
|
|
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $queue->getSize(), "Queue is empty ($desc)" );
|
|
|
|
|
$this->assertSame( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
|
2014-04-16 17:51:11 +00:00
|
|
|
$this->assertNull(
|
2013-03-25 23:27:14 +00:00
|
|
|
$queue->batchPush(
|
2016-02-17 09:09:32 +00:00
|
|
|
[ $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() ]
|
2013-03-25 23:27:14 +00:00
|
|
|
),
|
2013-02-06 22:45:33 +00:00
|
|
|
"Push worked ($desc)" );
|
|
|
|
|
|
|
|
|
|
$this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
|
|
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
2020-05-30 10:36:42 +00:00
|
|
|
$this->assertSame( 1, $queue->getSize(), "Queue size is correct ($desc)" );
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
|
2014-04-16 17:51:11 +00:00
|
|
|
$this->assertNull(
|
2013-03-25 23:27:14 +00:00
|
|
|
$queue->batchPush(
|
2016-02-17 09:09:32 +00:00
|
|
|
[ $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() ]
|
2013-03-25 23:27:14 +00:00
|
|
|
),
|
|
|
|
|
"Push worked ($desc)"
|
|
|
|
|
);
|
2013-02-06 22:45:33 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
|
|
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
2020-05-30 10:36:42 +00:00
|
|
|
$this->assertSame( 1, $queue->getSize(), "Queue size is correct ($desc)" );
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
|
|
|
|
|
$job1 = $queue->pop();
|
|
|
|
|
$this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
|
|
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $queue->getSize(), "Queue is empty ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
if ( $recycles ) {
|
2020-05-30 10:36:42 +00:00
|
|
|
$this->assertSame( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$queue->ack( $job1 );
|
|
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provider_queueLists
|
2015-11-10 05:24:34 +00:00
|
|
|
*/
|
|
|
|
|
public function testDeduplicationWhileClaimed( $queue, $recycles, $desc ) {
|
|
|
|
|
$queue = $this->$queue;
|
|
|
|
|
if ( !$queue ) {
|
|
|
|
|
$this->markTestSkipped( $desc );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$job = $this->newDedupedJob();
|
|
|
|
|
$queue->push( $job );
|
|
|
|
|
|
|
|
|
|
// De-duplication does not apply to already-claimed jobs
|
|
|
|
|
$j = $queue->pop();
|
|
|
|
|
$queue->push( $job );
|
|
|
|
|
$queue->ack( $j );
|
|
|
|
|
|
|
|
|
|
$j = $queue->pop();
|
|
|
|
|
// Make sure ack() of the twin did not delete the sibling data
|
2019-12-13 18:21:35 +00:00
|
|
|
$this->assertInstanceOf( NullJob::class, $j );
|
2015-11-10 05:24:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provider_queueLists
|
2013-02-06 22:45:33 +00:00
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testRootDeduplication( $queue, $recycles, $desc ) {
|
2013-02-06 22:45:33 +00:00
|
|
|
$queue = $this->$queue;
|
2013-03-13 21:26:28 +00:00
|
|
|
if ( !$queue ) {
|
|
|
|
|
$this->markTestSkipped( $desc );
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-06 22:45:33 +00:00
|
|
|
$this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
|
|
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $queue->getSize(), "Queue is empty ($desc)" );
|
|
|
|
|
$this->assertSame( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
|
2019-05-04 20:41:10 +00:00
|
|
|
$root1 = Job::newRootJobParams( "nulljobspam:testId" ); // task ID/timestamp
|
2013-02-15 10:17:52 +00:00
|
|
|
for ( $i = 0; $i < 5; ++$i ) {
|
2014-04-16 17:51:11 +00:00
|
|
|
$this->assertNull( $queue->push( $this->newJob( 0, $root1 ) ), "Push worked ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
|
|
|
|
$queue->deduplicateRootJob( $this->newJob( 0, $root1 ) );
|
2014-10-22 21:22:10 +00:00
|
|
|
|
|
|
|
|
$root2 = $root1;
|
|
|
|
|
# Add a second to UNIX epoch and format back to TS_MW
|
|
|
|
|
$root2_ts = strtotime( $root2['rootJobTimestamp'] );
|
|
|
|
|
$root2_ts++;
|
|
|
|
|
$root2['rootJobTimestamp'] = wfTimestamp( TS_MW, $root2_ts );
|
|
|
|
|
|
2013-02-06 22:45:33 +00:00
|
|
|
$this->assertNotEquals( $root1['rootJobTimestamp'], $root2['rootJobTimestamp'],
|
|
|
|
|
"Root job signatures have different timestamps." );
|
2013-02-15 10:17:52 +00:00
|
|
|
for ( $i = 0; $i < 5; ++$i ) {
|
2014-04-16 17:51:11 +00:00
|
|
|
$this->assertNull( $queue->push( $this->newJob( 0, $root2 ) ), "Push worked ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
|
|
|
|
$queue->deduplicateRootJob( $this->newJob( 0, $root2 ) );
|
|
|
|
|
|
|
|
|
|
$this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
|
|
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
|
|
|
|
$this->assertEquals( 10, $queue->getSize(), "Queue size is correct ($desc)" );
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
|
|
|
|
|
$dupcount = 0;
|
2016-02-17 09:09:32 +00:00
|
|
|
$jobs = [];
|
2013-02-06 22:45:33 +00:00
|
|
|
do {
|
|
|
|
|
$job = $queue->pop();
|
|
|
|
|
if ( $job ) {
|
|
|
|
|
$jobs[] = $job;
|
|
|
|
|
$queue->ack( $job );
|
|
|
|
|
}
|
2013-02-15 10:17:52 +00:00
|
|
|
if ( $job instanceof DuplicateJob ) {
|
|
|
|
|
++$dupcount;
|
|
|
|
|
}
|
2013-02-06 22:45:33 +00:00
|
|
|
} while ( $job );
|
|
|
|
|
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertCount( 10, $jobs, "Correct number of jobs popped ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
$this->assertEquals( 5, $dupcount, "Correct number of duplicate jobs popped ($desc)" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provider_fifoQueueLists
|
|
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testJobOrder( $queue, $recycles, $desc ) {
|
2013-02-06 22:45:33 +00:00
|
|
|
$queue = $this->$queue;
|
2013-03-13 21:26:28 +00:00
|
|
|
if ( !$queue ) {
|
|
|
|
|
$this->markTestSkipped( $desc );
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-06 22:45:33 +00:00
|
|
|
$this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
|
|
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $queue->getSize(), "Queue is empty ($desc)" );
|
|
|
|
|
$this->assertSame( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
|
2013-02-15 10:17:52 +00:00
|
|
|
for ( $i = 0; $i < 10; ++$i ) {
|
2014-04-16 17:51:11 +00:00
|
|
|
$this->assertNull( $queue->push( $this->newJob( $i ) ), "Push worked ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
2013-02-15 10:17:52 +00:00
|
|
|
for ( $i = 0; $i < 10; ++$i ) {
|
2013-02-06 22:45:33 +00:00
|
|
|
$job = $queue->pop();
|
|
|
|
|
$this->assertTrue( $job instanceof Job, "Jobs popped from queue ($desc)" );
|
|
|
|
|
$params = $job->getParams();
|
|
|
|
|
$this->assertEquals( $i, $params['i'], "Job popped from queue is FIFO ($desc)" );
|
|
|
|
|
$queue->ack( $job );
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-08 18:52:54 +00:00
|
|
|
$this->assertFalse( $queue->pop(), "Queue is not empty ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
|
|
|
|
|
$queue->flushCaches();
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $queue->getSize(), "Queue is empty ($desc)" );
|
|
|
|
|
$this->assertSame( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
2015-11-12 00:10:29 +00:00
|
|
|
public function testQueueAggregateTable() {
|
2021-04-01 20:55:37 +00:00
|
|
|
$this->hideDeprecated( 'JobQueue::getWiki' );
|
|
|
|
|
|
2015-11-12 00:10:29 +00:00
|
|
|
$queue = $this->queueFifo;
|
|
|
|
|
if ( !$queue || !method_exists( $queue, 'getServerQueuesWithJobs' ) ) {
|
|
|
|
|
$this->markTestSkipped();
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-11 01:13:48 +00:00
|
|
|
$this->assertNotContains(
|
2016-02-17 09:09:32 +00:00
|
|
|
[ $queue->getType(), $queue->getWiki() ],
|
2015-12-11 01:13:48 +00:00
|
|
|
$queue->getServerQueuesWithJobs(),
|
|
|
|
|
"Null queue not in listing"
|
|
|
|
|
);
|
2015-11-12 00:10:29 +00:00
|
|
|
|
|
|
|
|
$queue->push( $this->newJob( 0 ) );
|
|
|
|
|
|
2015-12-11 01:13:48 +00:00
|
|
|
$this->assertContains(
|
2016-02-17 09:09:32 +00:00
|
|
|
[ $queue->getType(), $queue->getWiki() ],
|
2015-12-11 01:13:48 +00:00
|
|
|
$queue->getServerQueuesWithJobs(),
|
|
|
|
|
"Null queue in listing"
|
2015-11-12 00:10:29 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-22 02:12:37 +00:00
|
|
|
public static function provider_queueLists() {
|
2016-02-17 09:09:32 +00:00
|
|
|
return [
|
|
|
|
|
[ 'queueRand', false, 'Random queue without ack()' ],
|
|
|
|
|
[ 'queueRandTTL', true, 'Random queue with ack()' ],
|
|
|
|
|
[ 'queueTimestamp', false, 'Time ordered queue without ack()' ],
|
|
|
|
|
[ 'queueTimestampTTL', true, 'Time ordered queue with ack()' ],
|
|
|
|
|
[ 'queueFifo', false, 'FIFO ordered queue without ack()' ],
|
|
|
|
|
[ 'queueFifoTTL', true, 'FIFO ordered queue with ack()' ]
|
|
|
|
|
];
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
2013-03-22 02:12:37 +00:00
|
|
|
public static function provider_fifoQueueLists() {
|
2016-02-17 09:09:32 +00:00
|
|
|
return [
|
|
|
|
|
[ 'queueFifo', false, 'Ordered queue without ack()' ],
|
|
|
|
|
[ 'queueFifoTTL', true, 'Ordered queue with ack()' ]
|
|
|
|
|
];
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
2019-10-09 18:24:07 +00:00
|
|
|
protected function newJob( $i = 0, $rootJob = [] ) {
|
2023-01-20 09:51:14 +00:00
|
|
|
$params = [
|
|
|
|
|
'namespace' => NS_MAIN,
|
|
|
|
|
'title' => 'Main_Page',
|
|
|
|
|
'lives' => 0,
|
|
|
|
|
'usleep' => 0,
|
|
|
|
|
'removeDuplicates' => 0,
|
|
|
|
|
'i' => $i
|
|
|
|
|
] + $rootJob;
|
|
|
|
|
|
|
|
|
|
return $this->getServiceContainer()->getJobFactory()->newJob( 'null', $params );
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
2019-10-09 18:24:07 +00:00
|
|
|
protected function newDedupedJob( $i = 0, $rootJob = [] ) {
|
2023-01-20 09:51:14 +00:00
|
|
|
$params = [
|
|
|
|
|
'namespace' => NS_MAIN,
|
|
|
|
|
'title' => 'Main_Page',
|
|
|
|
|
'lives' => 0,
|
|
|
|
|
'usleep' => 0,
|
|
|
|
|
'removeDuplicates' => 1,
|
|
|
|
|
'i' => $i
|
|
|
|
|
] + $rootJob;
|
|
|
|
|
|
|
|
|
|
return $this->getServiceContainer()->getJobFactory()->newJob( 'null', $params );
|
2013-02-06 22:45:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
2018-01-12 21:44:12 +00:00
|
|
|
|
|
|
|
|
class JobQueueDBSingle extends JobQueueDB {
|
|
|
|
|
protected function getDB( $index ) {
|
|
|
|
|
$lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
|
2018-04-04 23:29:18 +00:00
|
|
|
// Override to not use CONN_TRX_AUTOCOMMIT so that we see the same temporary `job` table
|
2018-10-13 07:29:23 +00:00
|
|
|
return $lb->getConnection( $index, [], $this->domain );
|
2018-01-12 21:44:12 +00:00
|
|
|
}
|
|
|
|
|
}
|