Merge "RCFilters: add namespace filters for all contents and all discussions"
[lhc/web/wiklou.git] / includes / objectcache / SqlBagOStuff.php
index a8c23d6..e97dc41 100644 (file)
@@ -27,6 +27,7 @@ use Wikimedia\Rdbms\IDatabase;
 use Wikimedia\Rdbms\DBError;
 use Wikimedia\Rdbms\DBQueryError;
 use Wikimedia\Rdbms\DBConnectionError;
+use Wikimedia\Rdbms\IMaintainableDatabase;
 use Wikimedia\Rdbms\LoadBalancer;
 use Wikimedia\ScopedCallback;
 use Wikimedia\WaitConditionLoop;
@@ -36,7 +37,7 @@ use Wikimedia\WaitConditionLoop;
  *
  * @ingroup Cache
  */
-class SqlBagOStuff extends BagOStuff {
+class SqlBagOStuff extends MediumSpecificBagOStuff {
        /** @var array[] (server index => server config) */
        protected $serverInfos;
        /** @var string[] (server index => tag/host name) */
@@ -55,8 +56,6 @@ class SqlBagOStuff extends BagOStuff {
        protected $tableName = 'objectcache';
        /** @var bool */
        protected $replicaOnly = false;
-       /** @var int */
-       protected $syncTimeout = 3;
 
        /** @var LoadBalancer|null */
        protected $separateMainLB;
@@ -68,7 +67,7 @@ class SqlBagOStuff extends BagOStuff {
        protected $connFailureErrors = [];
 
        /** @var int */
-       private static $GARBAGE_COLLECT_DELAY_SEC = 1;
+       private static $GC_DELAY_SEC = 1;
 
        /** @var string */
        private static $OP_SET = 'set';
@@ -159,9 +158,6 @@ class SqlBagOStuff extends BagOStuff {
                if ( isset( $params['shards'] ) ) {
                        $this->shards = intval( $params['shards'] );
                }
-               if ( isset( $params['syncTimeout'] ) ) {
-                       $this->syncTimeout = $params['syncTimeout'];
-               }
                // Backwards-compatibility for < 1.34
                $this->replicaOnly = $params['replicaOnly'] ?? ( $params['slaveOnly'] ?? false );
        }
@@ -170,23 +166,24 @@ class SqlBagOStuff extends BagOStuff {
         * Get a connection to the specified database
         *
         * @param int $serverIndex
-        * @return Database
+        * @return IMaintainableDatabase
         * @throws MWException
         */
        protected function getDB( $serverIndex ) {
-               if ( !isset( $this->conns[$serverIndex] ) ) {
-                       if ( $serverIndex >= $this->numServers ) {
-                               throw new MWException( __METHOD__ . ": Invalid server index \"$serverIndex\"" );
-                       }
+               if ( $serverIndex >= $this->numServers ) {
+                       throw new MWException( __METHOD__ . ": Invalid server index \"$serverIndex\"" );
+               }
 
-                       # Don't keep timing out trying to connect for each call if the DB is down
-                       if ( isset( $this->connFailureErrors[$serverIndex] )
-                               && ( time() - $this->connFailureTimes[$serverIndex] ) < 60
-                       ) {
-                               throw $this->connFailureErrors[$serverIndex];
-                       }
+               # Don't keep timing out trying to connect for each call if the DB is down
+               if (
+                       isset( $this->connFailureErrors[$serverIndex] ) &&
+                       ( $this->getCurrentTime() - $this->connFailureTimes[$serverIndex] ) < 60
+               ) {
+                       throw $this->connFailureErrors[$serverIndex];
+               }
 
-                       if ( $this->serverInfos ) {
+               if ( $this->serverInfos ) {
+                       if ( !isset( $this->conns[$serverIndex] ) ) {
                                // Use custom database defined by server connection info
                                $info = $this->serverInfos[$serverIndex];
                                $type = $info['type'] ?? 'mysql';
@@ -194,25 +191,26 @@ class SqlBagOStuff extends BagOStuff {
                                $this->logger->debug( __CLASS__ . ": connecting to $host" );
                                $db = Database::factory( $type, $info );
                                $db->clearFlag( DBO_TRX ); // auto-commit mode
+                               $this->conns[$serverIndex] = $db;
+                       }
+                       $db = $this->conns[$serverIndex];
+               } else {
+                       // Use the main LB database
+                       $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+                       $index = $this->replicaOnly ? DB_REPLICA : DB_MASTER;
+                       if ( $lb->getServerType( $lb->getWriterIndex() ) !== 'sqlite' ) {
+                               // Keep a separate connection to avoid contention and deadlocks
+                               $db = $lb->getConnectionRef( $index, [], false, $lb::CONN_TRX_AUTOCOMMIT );
                        } else {
-                               // Use the main LB database
-                               $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
-                               $index = $this->replicaOnly ? DB_REPLICA : DB_MASTER;
-                               if ( $lb->getServerType( $lb->getWriterIndex() ) !== 'sqlite' ) {
-                                       // Keep a separate connection to avoid contention and deadlocks
-                                       $db = $lb->getConnection( $index, [], false, $lb::CONN_TRX_AUTOCOMMIT );
-                               } else {
-                                       // However, SQLite has the opposite behavior due to DB-level locking.
-                                       // Stock sqlite MediaWiki installs use a separate sqlite cache DB instead.
-                                       $db = $lb->getConnection( $index );
-                               }
+                               // However, SQLite has the opposite behavior due to DB-level locking.
+                               // Stock sqlite MediaWiki installs use a separate sqlite cache DB instead.
+                               $db = $lb->getConnectionRef( $index );
                        }
-
-                       $this->logger->debug( sprintf( "Connection %s will be used for SqlBagOStuff", $db ) );
-                       $this->conns[$serverIndex] = $db;
                }
 
-               return $this->conns[$serverIndex];
+               $this->logger->debug( sprintf( "Connection %s will be used for SqlBagOStuff", $db ) );
+
+               return $db;
        }
 
        /**
@@ -358,7 +356,7 @@ class SqlBagOStuff extends BagOStuff {
                        $keysByTable[$serverIndex][$tableName][] = $key;
                }
 
-               $exptime = $this->convertToExpiry( $exptime );
+               $exptime = $this->getExpirationAsTimestamp( $exptime );
 
                $result = true;
                /** @noinspection PhpUnusedLocalVariableInspection */
@@ -475,7 +473,7 @@ class SqlBagOStuff extends BagOStuff {
 
        protected function cas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
                list( $serverIndex, $tableName ) = $this->getTableByKey( $key );
-               $exptime = $this->convertToExpiry( $exptime );
+               $exptime = $this->getExpirationAsTimestamp( $exptime );
 
                /** @noinspection PhpUnusedLocalVariableInspection */
                $silenceScope = $this->silenceTransactionProfiler();
@@ -565,7 +563,7 @@ class SqlBagOStuff extends BagOStuff {
                return $ok;
        }
 
-       protected function doChangeTTLMulti( array $keys, $exptime, $flags = 0 ) {
+       public function changeTTLMulti( array $keys, $exptime, $flags = 0 ) {
                return $this->modifyMulti(
                        array_fill_keys( $keys, null ),
                        $exptime,
@@ -586,7 +584,7 @@ class SqlBagOStuff extends BagOStuff {
        protected function isExpired( $db, $exptime ) {
                return (
                        $exptime != $this->getMaxDateTime( $db ) &&
-                       wfTimestamp( TS_UNIX, $exptime ) < time()
+                       wfTimestamp( TS_UNIX, $exptime ) < $this->getCurrentTime()
                );
        }
 
@@ -595,7 +593,7 @@ class SqlBagOStuff extends BagOStuff {
         * @return string
         */
        protected function getMaxDateTime( $db ) {
-               if ( time() > 0x7fffffff ) {
+               if ( (int)$this->getCurrentTime() > 0x7fffffff ) {
                        return $db->timestamp( 1 << 62 );
                } else {
                        return $db->timestamp( 0x7fffffff );
@@ -613,14 +611,18 @@ class SqlBagOStuff extends BagOStuff {
                        // Only purge on one in every $this->purgePeriod writes
                        mt_rand( 0, $this->purgePeriod - 1 ) == 0 &&
                        // Avoid repeating the delete within a few seconds
-                       ( time() - $this->lastGarbageCollect ) > self::$GARBAGE_COLLECT_DELAY_SEC
+                       ( $this->getCurrentTime() - $this->lastGarbageCollect ) > self::$GC_DELAY_SEC
                ) {
                        $garbageCollector = function () use ( $db ) {
-                               $this->deleteServerObjectsExpiringBefore( $db, time(), null, $this->purgeLimit );
+                               $this->deleteServerObjectsExpiringBefore(
+                                       $db, $this->getCurrentTime(),
+                                       null,
+                                       $this->purgeLimit
+                               );
                                $this->lastGarbageCollect = time();
                        };
                        if ( $this->asyncHandler ) {
-                               $this->lastGarbageCollect = time(); // avoid duplicate enqueues
+                               $this->lastGarbageCollect = $this->getCurrentTime(); // avoid duplicate enqueues
                                ( $this->asyncHandler )( $garbageCollector );
                        } else {
                                $garbageCollector();
@@ -629,7 +631,7 @@ class SqlBagOStuff extends BagOStuff {
        }
 
        public function expireAll() {
-               $this->deleteObjectsExpiringBefore( time() );
+               $this->deleteObjectsExpiringBefore( $this->getCurrentTime() );
        }
 
        public function deleteObjectsExpiringBefore(
@@ -929,8 +931,9 @@ class SqlBagOStuff extends BagOStuff {
        protected function markServerDown( DBError $exception, $serverIndex ) {
                unset( $this->conns[$serverIndex] ); // bug T103435
 
+               $now = $this->getCurrentTime();
                if ( isset( $this->connFailureTimes[$serverIndex] ) ) {
-                       if ( time() - $this->connFailureTimes[$serverIndex] >= 60 ) {
+                       if ( $now - $this->connFailureTimes[$serverIndex] >= 60 ) {
                                unset( $this->connFailureTimes[$serverIndex] );
                                unset( $this->connFailureErrors[$serverIndex] );
                        } else {
@@ -938,7 +941,6 @@ class SqlBagOStuff extends BagOStuff {
                                return;
                        }
                }
-               $now = time();
                $this->logger->info( __METHOD__ . ": Server #$serverIndex down until " . ( $now + 60 ) );
                $this->connFailureTimes[$serverIndex] = $now;
                $this->connFailureErrors[$serverIndex] = $exception;