Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
37 / 37 |
|
100.00% |
2 / 2 |
CRAP | |
100.00% |
1 / 1 |
GlobalBlockingBlockPurger | |
100.00% |
37 / 37 |
|
100.00% |
2 / 2 |
6 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
purgeExpiredBlocks | |
100.00% |
32 / 32 |
|
100.00% |
1 / 1 |
5 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\GlobalBlocking\Services; |
4 | |
5 | use MediaWiki\Config\ServiceOptions; |
6 | use MediaWiki\MainConfigNames; |
7 | use Wikimedia\Rdbms\IConnectionProvider; |
8 | use Wikimedia\Rdbms\ReadOnlyMode; |
9 | |
10 | /** |
11 | * Purges expired block rows from the globalblocks and global_block_whitelist tables. |
12 | * |
13 | * @since 1.42 |
14 | */ |
15 | class GlobalBlockingBlockPurger { |
16 | |
17 | public const CONSTRUCTOR_OPTIONS = [ |
18 | MainConfigNames::UpdateRowsPerQuery, |
19 | ]; |
20 | |
21 | private ServiceOptions $options; |
22 | private GlobalBlockingConnectionProvider $globalBlockingConnectionProvider; |
23 | private IConnectionProvider $connectionProvider; |
24 | private ReadOnlyMode $readOnlyMode; |
25 | |
26 | public function __construct( |
27 | ServiceOptions $options, |
28 | GlobalBlockingConnectionProvider $globalBlockingConnectionProvider, |
29 | IConnectionProvider $connectionProvider, |
30 | ReadOnlyMode $readOnlyMode |
31 | ) { |
32 | $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS ); |
33 | $this->options = $options; |
34 | $this->globalBlockingConnectionProvider = $globalBlockingConnectionProvider; |
35 | $this->connectionProvider = $connectionProvider; |
36 | $this->readOnlyMode = $readOnlyMode; |
37 | } |
38 | |
39 | /** |
40 | * Purge stale block rows. |
41 | * |
42 | * This should only be called on a request that performs writes to the database, |
43 | * such as creating a block, as this is an expensive operation. |
44 | * |
45 | * This acts similarly to DatabaseBlockStore::purgeExpiredBlocks, but the purge is not performed |
46 | * on POSTSEND. |
47 | */ |
48 | public function purgeExpiredBlocks() { |
49 | $globaldbw = $this->globalBlockingConnectionProvider->getPrimaryGlobalBlockingDatabase(); |
50 | if ( !$this->readOnlyMode->isReadOnly( $globaldbw->getDomainID() ) ) { |
51 | $deleteIds = $globaldbw->newSelectQueryBuilder() |
52 | ->select( 'gb_id' ) |
53 | ->from( 'globalblocks' ) |
54 | ->where( $globaldbw->expr( 'gb_expiry', '<=', $globaldbw->timestamp() ) ) |
55 | ->limit( $this->options->get( MainConfigNames::UpdateRowsPerQuery ) ) |
56 | ->caller( __METHOD__ ) |
57 | ->fetchFieldValues(); |
58 | if ( $deleteIds !== [] ) { |
59 | $deleteIds = array_map( 'intval', $deleteIds ); |
60 | $globaldbw->newDeleteQueryBuilder() |
61 | ->deleteFrom( 'globalblocks' ) |
62 | ->where( [ 'gb_id' => $deleteIds ] ) |
63 | ->caller( __METHOD__ ) |
64 | ->execute(); |
65 | } |
66 | } |
67 | |
68 | $dbw = $this->connectionProvider->getPrimaryDatabase(); |
69 | if ( !$this->readOnlyMode->isReadOnly() ) { |
70 | // Purge the global_block_whitelist table. |
71 | // We can't be perfect about this without an expensive check on the primary database |
72 | // for every single global block. However, we can be clever about it and store |
73 | // the expiry of global blocks in the global_block_whitelist table. |
74 | // That way, most blocks will fall out of the table naturally when they expire. |
75 | $deleteWhitelistIds = $dbw->newSelectQueryBuilder() |
76 | ->select( 'gbw_id' ) |
77 | ->from( 'global_block_whitelist' ) |
78 | ->where( $dbw->expr( 'gbw_expiry', '<=', $dbw->timestamp() ) ) |
79 | ->limit( $this->options->get( MainConfigNames::UpdateRowsPerQuery ) ) |
80 | ->caller( __METHOD__ ) |
81 | ->fetchFieldValues(); |
82 | if ( $deleteWhitelistIds !== [] ) { |
83 | $deleteWhitelistIds = array_map( 'intval', $deleteWhitelistIds ); |
84 | $dbw->newDeleteQueryBuilder() |
85 | ->deleteFrom( 'global_block_whitelist' ) |
86 | ->where( [ 'gbw_id' => $deleteWhitelistIds ] ) |
87 | ->caller( __METHOD__ ) |
88 | ->execute(); |
89 | } |
90 | } |
91 | } |
92 | } |