From 1081356412d48b69648eb1a2312639e221268028 Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Thu, 5 Jul 2018 12:58:48 +0100 Subject: [PATCH] Move l10n_cache table to a separate DB for sqlite via the installer This does not set 'db' as the cache type so that admins can still easily set the cache directory to use the file-based cdb system. If they do not, then at least the second DB file will be used to avoid heavy contention. Bug: T93097 Change-Id: Ib3912f00cf12de99801ebda4f06135b2987ce71a --- includes/cache/localisation/LCStoreDB.php | 57 +++++++++++++------ .../cache/localisation/LocalisationCache.php | 2 + includes/installer/SqliteInstaller.php | 32 ++++++++++- 3 files changed, 74 insertions(+), 17 deletions(-) diff --git a/includes/cache/localisation/LCStoreDB.php b/includes/cache/localisation/LCStoreDB.php index c57145c04d..2d8e4d21a9 100644 --- a/includes/cache/localisation/LCStoreDB.php +++ b/includes/cache/localisation/LCStoreDB.php @@ -18,6 +18,7 @@ * @file */ +use Wikimedia\Rdbms\Database; use Wikimedia\Rdbms\IDatabase; use Wikimedia\Rdbms\DBQueryError; @@ -26,21 +27,29 @@ use Wikimedia\Rdbms\DBQueryError; * This will work on any MediaWiki installation. */ class LCStoreDB implements LCStore { - /** @var string */ private $currentLang; /** @var bool */ private $writesDone = false; - /** @var IDatabase */ + /** @var IDatabase|null */ private $dbw; /** @var array */ private $batch = []; /** @var bool */ private $readOnly = false; + /** @var array Server configuration map */ + private $server; + + public function __construct( $params ) { + $this->server = $params['server'] ?? []; + } public function get( $code, $key ) { - if ( $this->writesDone && $this->dbw ) { - $db = $this->dbw; // see the changes in finishWrite() + if ( $this->server || $this->writesDone ) { + // If a server configuration map is specified, always used that connection + // for reads and writes. Otherwise, if writes occurred in finishWrite(), make + // sure those changes are always visible. + $db = $this->getWriteConnection(); } else { $db = wfGetDB( DB_REPLICA ); } @@ -62,8 +71,8 @@ class LCStoreDB implements LCStore { throw new MWException( __METHOD__ . ": Invalid language \"$code\"" ); } - $this->dbw = wfGetDB( DB_MASTER ); - $this->readOnly = $this->dbw->isReadOnly(); + $dbw = $this->getWriteConnection(); + $this->readOnly = $dbw->isReadOnly(); $this->currentLang = $code; $this->batch = []; @@ -76,25 +85,22 @@ class LCStoreDB implements LCStore { throw new MWException( __CLASS__ . ': must call startWrite() before finishWrite()' ); } - $this->dbw->startAtomic( __METHOD__ ); + $dbw = $this->getWriteConnection(); + $dbw->startAtomic( __METHOD__ ); try { - $this->dbw->delete( - 'l10n_cache', - [ 'lc_lang' => $this->currentLang ], - __METHOD__ - ); + $dbw->delete( 'l10n_cache', [ 'lc_lang' => $this->currentLang ], __METHOD__ ); foreach ( array_chunk( $this->batch, 500 ) as $rows ) { - $this->dbw->insert( 'l10n_cache', $rows, __METHOD__ ); + $dbw->insert( 'l10n_cache', $rows, __METHOD__ ); } $this->writesDone = true; } catch ( DBQueryError $e ) { - if ( $this->dbw->wasReadOnlyError() ) { + if ( $dbw->wasReadOnlyError() ) { $this->readOnly = true; // just avoid site down time } else { throw $e; } } - $this->dbw->endAtomic( __METHOD__ ); + $dbw->endAtomic( __METHOD__ ); $this->currentLang = null; $this->batch = []; @@ -107,11 +113,30 @@ class LCStoreDB implements LCStore { throw new MWException( __CLASS__ . ': must call startWrite() before set()' ); } + $dbw = $this->getWriteConnection(); + $this->batch[] = [ 'lc_lang' => $this->currentLang, 'lc_key' => $key, - 'lc_value' => $this->dbw->encodeBlob( serialize( $value ) ) + 'lc_value' => $dbw->encodeBlob( serialize( $value ) ) ]; } + /** + * @return IDatabase + */ + private function getWriteConnection() { + if ( !$this->dbw ) { + if ( $this->server ) { + $this->dbw = Database::factory( $this->server['type'], $this->server ); + if ( !$this->dbw ) { + throw new MWException( __CLASS__ . ': failed to obtain a DB connection' ); + } + } else { + $this->dbw = wfGetDB( DB_MASTER ); + } + } + + return $this->dbw; + } } diff --git a/includes/cache/localisation/LocalisationCache.php b/includes/cache/localisation/LocalisationCache.php index 9e2a2fd2ca..75e5e19bce 100644 --- a/includes/cache/localisation/LocalisationCache.php +++ b/includes/cache/localisation/LocalisationCache.php @@ -203,6 +203,7 @@ class LocalisationCache { break; case 'db': $storeClass = LCStoreDB::class; + $storeConf['server'] = $conf['storeServer'] ?? []; break; case 'array': $storeClass = LCStoreStaticArray::class; @@ -215,6 +216,7 @@ class LocalisationCache { $storeClass = LCStoreCDB::class; } else { $storeClass = LCStoreDB::class; + $storeConf['server'] = $conf['storeServer'] ?? []; } break; default: diff --git a/includes/installer/SqliteInstaller.php b/includes/installer/SqliteInstaller.php index 6f168720c7..aa954389b1 100644 --- a/includes/installer/SqliteInstaller.php +++ b/includes/installer/SqliteInstaller.php @@ -230,6 +230,7 @@ class SqliteInstaller extends DatabaseInstaller { $status = Status::newGood(); $status->merge( $this->makeStubDBFile( $dir, $db ) ); $status->merge( $this->makeStubDBFile( $dir, "wikicache" ) ); + $status->merge( $this->makeStubDBFile( $dir, "{$db}_l10n_cache" ) ); if ( !$status->isOK() ) { return $status; } @@ -242,7 +243,8 @@ class SqliteInstaller extends DatabaseInstaller { # Create the global cache DB try { - $conn = Database::factory( 'sqlite', [ 'dbname' => 'wikicache', 'dbDirectory' => $dir ] ); + $conn = Database::factory( + 'sqlite', [ 'dbname' => 'wikicache', 'dbDirectory' => $dir ] ); # @todo: don't duplicate objectcache definition, though it's very simple $sql = <<getMessage() ); } + # Create the l10n cache DB + try { + $conn = Database::factory( + 'sqlite', [ 'dbname' => "{$db}_l10n_cache", 'dbDirectory' => $dir ] ); + # @todo: don't duplicate l10n_cache definition, though it's very simple + $sql = +<<query( $sql ); + $conn->query( "PRAGMA journal_mode=WAL" ); // this is permanent + $conn->close(); + } catch ( DBConnectionError $e ) { + return Status::newFatal( 'config-sqlite-connection-error', $e->getMessage() ); + } + # Open the main DB return $this->getConnection(); } @@ -330,6 +353,13 @@ EOT; 'dbDirectory' => \$wgSQLiteDataDir, 'flags' => 0 ] +]; +\$wgLocalisationCacheConf['storeServer'] = [ + 'type' => 'sqlite', + 'dbname' => \"{\$wgDBname}_l10n_cache\", + 'tablePrefix' => '', + 'dbDirectory' => \$wgSQLiteDataDir, + 'flags' => 0 ];"; } } -- 2.20.1