MediaWiki master
LCStoreDB.php
Go to the documentation of this file.
1<?php
24use Wikimedia\ScopedCallback;
25
31class LCStoreDB implements LCStore {
33 private $code;
35 private $server;
36
38 private $batch = [];
39
41 private $dbw;
43 private $writesDone = false;
45 private $readOnly = false;
46
47 public function __construct( $params ) {
48 $this->server = $params['server'] ?? [];
49 }
50
51 public function get( $code, $key ) {
52 if ( $this->server || $this->writesDone ) {
53 // If a server configuration map is specified, always used that connection
54 // for reads and writes. Otherwise, if writes occurred in finishWrite(), make
55 // sure those changes are always visible.
56 $db = $this->getWriteConnection();
57 } else {
58 $db = MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase();
59 }
60
61 $value = $db->newSelectQueryBuilder()
62 ->select( 'lc_value' )
63 ->from( 'l10n_cache' )
64 ->where( [ 'lc_lang' => $code, 'lc_key' => $key ] )
65 ->caller( __METHOD__ )->fetchField();
66
67 return ( $value !== false ) ? unserialize( $db->decodeBlob( $value ) ) : null;
68 }
69
70 public function startWrite( $code ) {
71 if ( $this->readOnly ) {
72 return;
73 } elseif ( !$code ) {
74 throw new InvalidArgumentException( __METHOD__ . ": Invalid language \"$code\"" );
75 }
76
77 $dbw = $this->getWriteConnection();
78 $this->readOnly = $dbw->isReadOnly();
79
80 $this->code = $code;
81 $this->batch = [];
82 }
83
84 public function finishWrite() {
85 if ( $this->readOnly ) {
86 return;
87 } elseif ( $this->code === null ) {
88 throw new LogicException( __CLASS__ . ': must call startWrite() before finishWrite()' );
89 }
90
91 $scope = Profiler::instance()->getTransactionProfiler()->silenceForScope();
92 $dbw = $this->getWriteConnection();
93 $dbw->startAtomic( __METHOD__ );
94 try {
96 ->deleteFrom( 'l10n_cache' )
97 ->where( [ 'lc_lang' => $this->code ] )
98 ->caller( __METHOD__ )->execute();
99 foreach ( array_chunk( $this->batch, 500 ) as $rows ) {
101 ->insertInto( 'l10n_cache' )
102 ->rows( $rows )
103 ->caller( __METHOD__ )->execute();
104 }
105 $this->writesDone = true;
106 } catch ( DBQueryError $e ) {
107 if ( $dbw->wasReadOnlyError() ) {
108 $this->readOnly = true; // just avoid site downtime
109 } else {
110 throw $e;
111 }
112 }
113 $dbw->endAtomic( __METHOD__ );
114 ScopedCallback::consume( $scope );
115
116 $this->code = null;
117 $this->batch = [];
118 }
119
120 public function set( $key, $value ) {
121 if ( $this->readOnly ) {
122 return;
123 } elseif ( $this->code === null ) {
124 throw new LogicException( __CLASS__ . ': must call startWrite() before set()' );
125 }
126
127 $dbw = $this->getWriteConnection();
128
129 $this->batch[] = [
130 'lc_lang' => $this->code,
131 'lc_key' => $key,
132 'lc_value' => $dbw->encodeBlob( serialize( $value ) )
133 ];
134 }
135
139 private function getWriteConnection() {
140 if ( !$this->dbw ) {
141 if ( $this->server ) {
142 $dbFactory = MediaWikiServices::getInstance()->getDatabaseFactory();
143 $this->dbw = $dbFactory->create( $this->server['type'], $this->server );
144 if ( !$this->dbw ) {
145 throw new RuntimeException( __CLASS__ . ': failed to obtain a DB connection' );
146 }
147 } else {
148 $this->dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase();
149 }
150 }
151
152 return $this->dbw;
153 }
154}
LCStore implementation which uses the standard DB functions to store data.
Definition LCStoreDB.php:31
finishWrite()
Finish a cache write transaction.
Definition LCStoreDB.php:84
startWrite( $code)
Start a cache write transaction.
Definition LCStoreDB.php:70
__construct( $params)
Definition LCStoreDB.php:47
Service locator for MediaWiki core services.
static instance()
Definition Profiler.php:105
Interface for the persistence layer of LocalisationCache.
Definition LCStore.php:40
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:36
endAtomic( $fname=__METHOD__)
Ends an atomic section of SQL statements.
newDeleteQueryBuilder()
Get an DeleteQueryBuilder bound to this connection.
startAtomic( $fname=__METHOD__, $cancelable=self::ATOMIC_NOT_CANCELABLE)
Begin an atomic section of SQL statements.
newInsertQueryBuilder()
Get an InsertQueryBuilder bound to this connection.
isReadOnly()
Check if this DB server is marked as read-only according to load balancer info.
encodeBlob( $b)
Some DBMSs have a special format for inserting into blob fields, they don't allow simple quoted strin...
wasReadOnlyError()
Determines if the last failure was due to the database being read-only.