Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 53 |
|
0.00% |
0 / 6 |
CRAP | |
0.00% |
0 / 1 |
DatabaseMessageIndex | |
0.00% |
0 / 53 |
|
0.00% |
0 / 6 |
306 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
lock | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
unlock | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
retrieve | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
30 | |||
get | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
store | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
30 |
1 | <?php |
2 | declare ( strict_types = 1 ); |
3 | |
4 | namespace MediaWiki\Extension\Translate\MessageLoading; |
5 | |
6 | use MediaWiki\MediaWikiServices; |
7 | use Wikimedia\Rdbms\ILoadBalancer; |
8 | |
9 | /** |
10 | * Storage on the database itself. |
11 | * |
12 | * This is likely to be the slowest backend. However, it scales okay |
13 | * and provides random access. It also doesn't need any special setup, |
14 | * the database table is added with update.php together with other tables, |
15 | * which is the reason this is the default backend. It also works well |
16 | * on multi-server setup without needing for shared file storage. |
17 | */ |
18 | class DatabaseMessageIndex extends MessageIndex { |
19 | private ?array $index = null; |
20 | private ILoadBalancer $loadBalancer; |
21 | |
22 | public function __construct() { |
23 | parent::__construct(); |
24 | $this->loadBalancer = MediaWikiServices::getInstance()->getDBLoadBalancer(); |
25 | } |
26 | |
27 | protected function lock(): bool { |
28 | $dbw = $this->loadBalancer->getConnection( DB_PRIMARY ); |
29 | |
30 | // Any transaction should be flushed after getting the lock to avoid |
31 | // stale pre-lock REPEATABLE-READ snapshot data. |
32 | $ok = $dbw->lock( 'translate-messageindex', __METHOD__, 5 ); |
33 | if ( $ok ) { |
34 | $dbw->commit( __METHOD__, 'flush' ); |
35 | } |
36 | |
37 | return $ok; |
38 | } |
39 | |
40 | protected function unlock(): bool { |
41 | $fname = __METHOD__; |
42 | $dbw = $this->loadBalancer->getConnection( DB_PRIMARY ); |
43 | // Unlock once the rows are actually unlocked to avoid deadlocks |
44 | if ( !$dbw->trxLevel() ) { |
45 | $dbw->unlock( 'translate-messageindex', $fname ); |
46 | } else { |
47 | $dbw->onTransactionResolution( static function () use ( $dbw, $fname ) { |
48 | $dbw->unlock( 'translate-messageindex', $fname ); |
49 | }, $fname ); |
50 | } |
51 | |
52 | return true; |
53 | } |
54 | |
55 | public function retrieve( bool $readLatest = false ): array { |
56 | if ( $this->index !== null && !$readLatest ) { |
57 | return $this->index; |
58 | } |
59 | |
60 | $dbr = $this->loadBalancer->getConnection( $readLatest ? DB_PRIMARY : DB_REPLICA ); |
61 | $res = $dbr->newSelectQueryBuilder() |
62 | ->select( '*' ) |
63 | ->from( 'translate_messageindex' ) |
64 | ->caller( __METHOD__ ) |
65 | ->fetchResultSet(); |
66 | $this->index = []; |
67 | foreach ( $res as $row ) { |
68 | $this->index[$row->tmi_key] = $this->unserialize( $row->tmi_value ); |
69 | } |
70 | |
71 | return $this->index; |
72 | } |
73 | |
74 | /** @inheritDoc */ |
75 | protected function get( $key ) { |
76 | $dbr = $this->loadBalancer->getConnection( DB_REPLICA ); |
77 | $value = $dbr->newSelectQueryBuilder() |
78 | ->select( 'tmi_value' ) |
79 | ->from( 'translate_messageindex' ) |
80 | ->where( [ 'tmi_key' => $key ] ) |
81 | ->caller( __METHOD__ ) |
82 | ->fetchField(); |
83 | |
84 | return is_string( $value ) ? $this->unserialize( $value ) : null; |
85 | } |
86 | |
87 | protected function store( array $array, array $diff ): void { |
88 | $updates = []; |
89 | |
90 | foreach ( [ $diff['add'], $diff['mod'] ] as $changes ) { |
91 | foreach ( $changes as $key => $data ) { |
92 | [ , $new ] = $data; |
93 | $updates[] = [ |
94 | 'tmi_key' => $key, |
95 | 'tmi_value' => $this->serialize( $new ), |
96 | ]; |
97 | } |
98 | } |
99 | |
100 | $index = [ 'tmi_key' ]; |
101 | $deletions = array_keys( $diff['del'] ); |
102 | |
103 | $dbw = $this->loadBalancer->getConnection( DB_PRIMARY ); |
104 | $dbw->startAtomic( __METHOD__ ); |
105 | |
106 | if ( $updates !== [] ) { |
107 | $dbw->replace( 'translate_messageindex', [ $index ], $updates, __METHOD__ ); |
108 | } |
109 | |
110 | if ( $deletions !== [] ) { |
111 | $dbw->delete( 'translate_messageindex', [ 'tmi_key' => $deletions ], __METHOD__ ); |
112 | } |
113 | |
114 | $dbw->endAtomic( __METHOD__ ); |
115 | |
116 | $this->index = $array; |
117 | } |
118 | } |